text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<title>LPCUSB: Member List</title>
<link href="doxygen.css" rel="stylesheet" type="text/css">
<link href="tabs.css" rel="stylesheet" type="text/css">

Main Page

Classes

Files

Related Pages

Class List

Class Members

TUSBDescHeader Member List
This is the complete list of members for TUSBDescHeader, including all inherited members.

bDescriptorType	TUSBDescHeader

bLength	TUSBDescHeader

<address style="align: right;">Generated on Fri Jul 27 12:07:03 2007 for LPCUSB by

 1.5.1 </address>

redpajama_set_name: RedPajamaGithub


text:
class Module
  #
  # Removes all constants in the module's namespace -- this is useful when
  # writing specs for metaprogramming methods
  #
  def nuke_constants
    constants.each{|const| remove_const(const) }
  end
end


text:
/** @constructor */
ScalaJS.c.scala_Predef$Pair$ = (function() {
  ScalaJS.c.java_lang_Object.call(this)
});
ScalaJS.c.scala_Predef$Pair$.prototype = new ScalaJS.inheritable.java_lang_Object();
ScalaJS.c.scala_Predef$Pair$.prototype.constructor = ScalaJS.c.scala_Predef$Pair$;
ScalaJS.c.scala_Predef$Pair$.prototype.apply__O__O__Lscala_Tuple2 = (function(x, y) {
  return new ScalaJS.c.scala_Tuple2().init___O__O(x, y)
});
ScalaJS.c.scala_Predef$Pair$.prototype.unapply__Lscala_Tuple2__Lscala_Option = (function(x) {
  return new ScalaJS.c.scala_Some().init___O(x)
});
ScalaJS.c.scala_Predef$Pair$.prototype.unapply__Lscala_Tuple2__ = (function(x) {
  return this.unapply__Lscala_Tuple2__Lscala_Option(x)
});
ScalaJS.c.scala_Predef$Pair$.prototype.apply__O__O__ = (function(x$2, y) {
  return this.apply__O__O__Lscala_Tuple2(x$2, y)
});
/** @constructor */
ScalaJS.inheritable.scala_Predef$Pair$ = (function() {
  /*<skip>*/
});
ScalaJS.inheritable.scala_Predef$Pair$.prototype = ScalaJS.c.scala_Predef$Pair$.prototype;
ScalaJS.is.scala_Predef$Pair$ = (function(obj) {
  return (!(!((obj && obj.$classData) && obj.$classData.ancestors.scala_Predef$Pair$)))
});
ScalaJS.as.scala_Predef$Pair$ = (function(obj) {
  if ((ScalaJS.is.scala_Predef$Pair$(obj) || (obj === null))) {
    return obj
  } else {
    ScalaJS.throwClassCastException(obj, "scala.Predef$Pair")
  }
});
ScalaJS.isArrayOf.scala_Predef$Pair$ = (function(obj, depth) {
  return (!(!(((obj && obj.$classData) && (obj.$classData.arrayDepth === depth)) && obj.$classData.arrayBase.ancestors.scala_Predef$Pair$)))
});
ScalaJS.asArrayOf.scala_Predef$Pair$ = (function(obj, depth) {
  if ((ScalaJS.isArrayOf.scala_Predef$Pair$(obj, depth) || (obj === null))) {
    return obj
  } else {
    ScalaJS.throwArrayCastException(obj, "Lscala.Predef$Pair;", depth)
  }
});
ScalaJS.data.scala_Predef$Pair$ = new ScalaJS.ClassTypeData({
  scala_Predef$Pair$: 0
}, false, "scala.Predef$Pair$", ScalaJS.data.java_lang_Object, {
  scala_Predef$Pair$: 1,
  java_lang_Object: 1
});
ScalaJS.c.scala_Predef$Pair$.prototype.$classData = ScalaJS.data.scala_Predef$Pair$;
ScalaJS.moduleInstances.scala_Predef$Pair = undefined;
ScalaJS.modules.scala_Predef$Pair = (function() {
  if ((!ScalaJS.moduleInstances.scala_Predef$Pair)) {
    ScalaJS.moduleInstances.scala_Predef$Pair = new ScalaJS.c.scala_Predef$Pair$().init___()
  };
  return ScalaJS.moduleInstances.scala_Predef$Pair
});
//@ sourceMappingURL=Predef$Pair$.js.map


text:
@interface JXSwipeAbleTestViewController ()<JXSwipeAbleViewDataSource, JXSwipeAbleViewDelegate, UIActionSheetDelegate>

@property (nonatomic, strong) JXSwipeAbleView *swipeableView;

- (UIView *)nextViewForSwipeableView:(JXSwipeAbleView *)swipeableView;

@end

@implementation JXSwipeAbleTestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];

    JXSwipeAbleView *swipeableView = [[JXSwipeAbleView alloc] initWithFrame:CGRectZero];
    self.swipeableView = swipeableView;
    [self.view addSubview:self.swipeableView];

    // Required Data Source
    self.swipeableView.dataSource = self;

    // Optional Delegate
    self.swipeableView.delegate = self;

    self.swipeableView.translatesAutoresizingMaskIntoConstraints = NO;
    self.swipeableView.numberOfActiveViews = 4;

    NSDictionary *metrics = @{};

    [self.view addConstraints:[NSLayoutConstraint
                               constraintsWithVisualFormat:@"|-50-[swipeableView]-50-|"
                               options:0
                               metrics:metrics
                               views:NSDictionaryOfVariableBindings(
                                                                    swipeableView)]];

    [self.view addConstraints:[NSLayoutConstraint
                               constraintsWithVisualFormat:@"V:|-120-[swipeableView]-100-|"
                               options:0
                               metrics:metrics
                               views:NSDictionaryOfVariableBindings(
                                                                    swipeableView)]];

}

- (void)viewDidLayoutSubviews {
    [self.swipeableView loadViewsIfNeeded];
}

- (void)handleReload:(UIBarButtonItem *)sender {
    UIActionSheet *actionSheet =
    [[UIActionSheet alloc] initWithTitle:@"Load Cards"
                                delegate:self
                       cancelButtonTitle:@"Cancel"
                  destructiveButtonTitle:nil
                       otherButtonTitles:@"Programmatically", @"From Xib", nil];
    [actionSheet showInView:self.view];
}

#pragma mark - JXSwipeAbleViewDelegate

- (void)swipeableView:(JXSwipeAbleView *)swipeableView
         didSwipeView:(UIView *)view
          inDirection:(JXSwipeAbleViewDirection)direction {
    NSLog(@"did swipe in direction: %zd", direction);
}

- (void)swipeableView:(JXSwipeAbleView *)swipeableView didCancelSwipe:(UIView *)view {
    NSLog(@"did cancel swipe");
}

- (void)swipeableView:(JXSwipeAbleView *)swipeableView
  didStartSwipingView:(UIView *)view
           atLocation:(CGPoint)location {
    NSLog(@"did start swiping at location: x %f, y %f", location.x, location.y);
}

- (void)swipeableView:(JXSwipeAbleView *)swipeableView
          swipingView:(UIView *)view
           atLocation:(CGPoint)location
          translation:(CGPoint)translation {
    NSLog(@"swiping at location: x %f, y %f, translation: x %f, y %f", location.x, location.y,
          translation.x, translation.y);
}

- (void)swipeableView:(JXSwipeAbleView *)swipeableView
    didEndSwipingView:(UIView *)view
           atLocation:(CGPoint)location {
    NSLog(@"did end swiping at location: x %f, y %f", location.x, location.y);

}

#pragma mark - JXSwipeAbleViewDataSource

- (UIView *)nextViewForSwipeableView:(JXSwipeAbleView *)swipeableView {

    return nil;
    UIView *view = [[UIView alloc] initWithFrame:swipeableView.bounds];

    view.backgroundColor = [UIColor blueColor];

    return view;
}

#pragma mark - ()

- (UIColor *)colorForName:(NSString *)name {
    NSString *sanitizedName = [name stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSString *selectorString = [NSString stringWithFormat:@"flat%@Color", sanitizedName];
    Class colorClass = [UIColor class];
    return [colorClass performSelector:NSSelectorFromString(selectorString)];
}
@end


text:
Portia
======

Portia is a tool that allows you to visually scrape websites without any programming knowledge required. With Portia you can annotate a web page to identify the data you wish to extract, and Portia will understand based on these annotations how to scrape data from similar pages.

Anatomy of a Portia Project
---------------------------

A project in Portia generally consists of one or more spiders.

### Spider

A spider is a crawler for a particular website. The configuration of a spider is split into three sections:

* **Initialization**
* **Crawling**
* **Extraction**

The **Initialization** section is used to set up the spider when it's first launched. Here you can define the start URLs and login credentials.

The **Crawling** section is used to configure how the spider will behave when it encounters URLs. You can choose how links are followed and whether to respect [nofollow](http://en.wikipedia.org/wiki/nofollow). You can visualise the effects of the crawling rules using the **Overlay blocked links** option; this will highlight links that will be followed in green, links that will only be followed if JavaScript is enabled in dark green, and links that won't be followed in red.

You can enable JavaScript by checking the 'Enable JS' checkbox. Here you can set the patterns to match against for links that should have JavaScript enabled, as well as those that should have it disabled. Note that when deploying you will need to set the `SPLASH_URL` Scrapy setting to match your Splash endpoint URL in order for JavaScript to work during the crawl.

The **Extraction** section lists the templates for this spider.

### Templates

When the crawler visits a page, the page is matched against each template and templates with more annotations take precedence over those with less. If the page matches a template, data will be extracted using the template's annotations to yield an item (assuming all required fields are filled). Templates exist within the context of a spider and are made up of annotations which define the elements you wish to extract from a page. Within the template you define the item you want to extract as well as mark any fields that are required for that item.

![Field extractors](docs/_static/portia-extractors.png)

You can also add extractors to item fields. Extractors allow you to further refine information extracted from a page using either regexes or a predefined type. For example, let's say there's an element on the page that contains a phone number, but it also contains some other text that you don't need. You could use a regular expression that matches phone numbers and add it as an extractor to the relevant field; upon extracting that field only the phone number would be stored.

You may need to use more than one template even if you're only extracting a single item type. For example, an e-commerce site may have a different layout for books than it does for audio CDs, so you would need to create a template for each layout. See [Tips for Working with Multiple Templates](#tips-for-working-with-multiple-templates) for more info.

### Annotations

![Creating an annotation](docs/_static/portia-annotation-creation.png)

An annotation defines the location of a piece of data on the web page and how it should be used by the spider. Typically an annotation maps some element on the page to a particular field of an item, but there is also the option to mark the data as being required without storing the data in an item. It's possible to map attributes of a particular element instead of the content if this is required, for example you can map the ``href`` attribute of an anchor link rather than the text.

### Items

An item refers to a single item of data scraped from the target website. A common example of an item would be a product for sale on an e-commerce website. It's important to differentiate **item** and **item definition**; in Portia an item definition or item type refers to the schema of an item rather than the item itself. For example, ``book`` would be an item definition, and a specific book scraped from the website would be an item. An item definition consists of multiple fields, so using the example of a product you might have fields named ``name``, ``price``, ``manufacturer`` and so on. Annotations are used to extract data from the page into each of these fields.

If you want to ensure that certain fields are extracted for an item, you can set the **Required** flag on a field which will discard an item if the field is missing. Duplicate items are removed by default. In some cases you may have fields where the value can vary despite being the same item, in which case you can mark them as **Vary** which will ignore the field when checking for duplicates. It's important to only use **Vary** when necessary, as misuse could easily lead to duplicate items being stored. The ``url`` field is a good example of where **Vary** is useful, as the same item may have multiple URLs. If the ``url`` field wasn't marked as **Vary** each duplicate item would be seen as unique because its URL would be different.

### Field Types

Setting a field's type will ensure the field will only match certain types of data. The following field types are available:

            text
            Plain text. Any markup is stripped and text within nested elements is also extracted.

            number
            A numeric value e.g. 7, 9.59

            image
            An image URL. In most cases you will want to map an ``img`` element's ``src`` attribute.

            price
            The same as ``number``, a numeric value.

            raw html
            Non-sanitized HTML.

            safe html
            Sanitized HTML. See below for more details.

            geopoint
            The same as ``text``.

            url
            A URL.

            date
            A date value parsed by [dateparser](https://github.com/scrapinghub/dateparser). This won't work if the annotated element includes non-date text, in which case you should use partial annotations.

The ``safe html`` field type keeps the following elements: ``br``, ``p``, ``big``, ``em``, ``small``, ``strong``, ``sub``, ``sup``, ``ins``, ``del``, ``code``, ``kbd``, ``samp``, ``tt``, ``var``, ``pre``, ``listing``, ``plaintext``, ``abbr``, ``acronym``, ``address``, ``bdo``, ``blockquote``, ``q``, ``cite``, ``dfn``, ``table``, ``tr``, ``th``, ``td``, ``tbody``, ``ul``, ``ol``, ``li``, ``dl``, ``dd``, ``dt``.

All other elements are discarded, with the exception of header tags (`h1`, `h2` ... `h6`) and `b` which are replaced with `strong`, and `i` which is replaced with `em`. Whitelisted elements contained within non-whitelisted elements will still be retained, with the exception of elements contained within a `script`, `img` or `input` element. For example,``
example
`` would extract to ``example``, whereas ```` would be discarded completely.

Creating a Portia Project
-------------------------

To create a new project, begin by entering the site's URL in the navigation bar at the top of the page and clicking ``Start``. Portia can be used as a web browser, and you can navigate to pages you want to extract data from and create templates for them. Clicking ``Start`` should create a new project along with a spider for the website, and you should see the loaded web page:

![Newly created project](docs/_static/portia-new-project.png)

The spider can be configured on the right. The start pages are the URLs the spider will visit when beginning a new crawl. Portia can be used as a web browser, and you can navigate to the pages you want to extract data from and create new templates. To define the data you wish to extract from the page, click the ``Annotate this page`` button, which will create a new template and allow you to annotate the page.

![Annotating the page](docs/_static/portia-annotation.png)

You will now be able to define annotations by highlighting or clicking elements on the page. When annotating, a context menu will appear allowing you to map an element's attribute or content to a particular item field. Should you want to add a new item field without having to go into the item editor, you can use the ``-create new-`` option in the field drop down to create a new field. If you want to mark an element as having to exist on the page without storing its data, you can select ``-just required-`` instead of a field. It's important to note when using ``-just required-``, only the existence of the element will be checked rather than its content.

![Items editor](docs/_static/portia-item-editor.png)

Once you are finished annotating, you can then mark any fields that are required by going into the item editor under ``Extracted item``. As mentioned earlier, if the item appears in several locations and some fields differ despite being the same item, you can also tick ``Vary`` on any relevant fields to exclude them from being used to detect duplicate items.

![Extracted items will be shown on the page](docs/_static/portia-extracted-items.png)

You can now confirm that your template works by clicking ``Continue browsing``. The page should reload and a pop-up should appear showing you the items extracted from the page. When visiting a page in Portia, the whole extraction process is performed with the spider with the set of currently defined templates. This allows you to check that data will be extracted from the page before running the spider against the whole website.

If you have created a template around one page where the data extracts successfully, but you visit a similar page and no item is extracted, then it's likely that particular page has a different layout or some fields missing. In this case you would simply click ``Annotate this page`` again to create a new template for the page, and then annotate it the same way you had done with the other page. See [Tips for Working with Multiple Templates](#tips-for-working-with-multiple-templates) for more details on how multiple templates are used within a single spider.

Once you've confirmed that your spider works and extracts data properly, your project is now ready to [run](#running-portia) or [deploy](#deploying-a-project).

Advanced Use of Annotations
---------------------------

### Multiple Fields

It's possible to extract multiple fields using a single annotation if there are several properties you want to extract from an element. For example, if there was an anchor link on the page, you could map the ``href`` attribute containing the URL to one field, and you could map the text to another. You can view a particular annotation's settings by either clicking the cog in the annotation pop-up window or by clicking the cog beside the annotation in the ``Annotations`` section of the template configuration. Within this context there is an ``Attribute mappings`` section where you can define additional mappings for the selected annotation should you want to map other attributes.

### Variants

It's common for there to be a single item with a number of variations e.g. different sizes such as small, medium and large. It's likely that each variation will have its own annotation for one or more fields and you want to keep each variation's value. In situations like this you can use variants to make sure each value is stored. Each annotation you define has a variant selected, the default being ``Base`` referring to the base item. To assign an annotation to a variant, you simply select the variant you want the annotation to use in its options or under the ``Annotations`` section in the template settings.

Consider the following scenario where variants would be useful:

You are wanting to scrape an e-commerce website that sells beds, and some beds come in multiple sizes e.g. ``Single``, ``Double``, ``Queen``, ``King``. The product page for each bed has a table of prices for each size, like so:

            Single
            $300

            Double
            $500

            Queen
            $650

            King
            $800

The rest of the data you want to extract (product name, description etc.) is common across all sizes. In this case, you would annotate the common data to the base item and create the fields ``size`` and ``price``. You would then annotate the ``Single`` cell as variant 1 of ``size``, and the ``$300`` cell as variant 1 of ``price``, followed by annotating ``Double`` as variant 2 of ``size``, ``$500`` as variant 2 of ``price`` and so on. It's worth noting that in this case, it wouldn't be necessary to create a variant for each row; usually it is enough to annotate only the first and last row of the table as Portia will automatically create variants for rows in between.

### Partial Annotations

Partial annotations can be used to extract some part of text which exists as part of a common pattern. For example, if an element contained the text ``Price: $5.00``, you could highlight the ``5.00`` part and map it to a field. The ``Price: $`` part would be matched but removed before extracting the field. In order to create a partial annotation, all you need to do is highlight the text the way you would normally, by clicking and dragging the mouse. The annotation window will pop up and you will be able to map it to a field the same way you would with a normal annotation.

There are some limitations to partial annotations. As mentioned in the previous paragraph, the text must be part of a pattern. For example, let's say an element contains the following text:

    Veris in temporibus sub Aprilis idibus habuit concilium Romarici montium

One of the pages visited by the crawler contains the following text in the same element:

    Cui dono lepidum novum libellum arido modo pumice expolitum?

If you had annotated ``Aprilis`` in the template, nothing would have matched because the surrounding text differs from the content being matched against. However, if the following text had instead appeared in the same element:

    Veris in temporibus sub Januarii idibus habuit concilium Romarici montium

The word ``Januarii`` would have been extracted, because its surrounding text matches the text surrounding the text that was annotated in the template.

### Annotating Elements in \

To annotate elements that are stored in the **head** tag you need to click the `Toggle CSS` button. Once you do this, the elements in the **head** tag will become visible and you can annotate them just like any other element.

Tips for Working with Multiple Templates
----------------------------------------

It's often necessary to use multiple templates within one spider, even if you're only extracting one item type. Some pages containing the same item type may have a different layout or fields missing, and you will need to accommodate those pages by creating a template for each layout variation.

The more annotations a template has, the more specific the data being extracted and therefore less chance of a false positive. For this reason, templates with more annotations take precedence over those with less annotations. If a subset of templates contains equal number of annotations per template, then within that subset templates will be tried in the order they were created from first to last. In other words, templates are tried sequentially in order of number of annotations first, and age second.

If you are working with a large number of templates, it may be difficult to ensure the correct template is applied to the right page. It's best to keep templates as strict as possible to avoid any false matches. It's useful to take advantage of the ``-just required-`` option and annotate elements that will always appear on matching pages to reduce the number of false positives.

Consider the following example:

We have an item type with the fields ``name``, ``price``, ``description`` and ``manufacturer``, where ``name`` and ``price`` are required fields. We have create a template with annotations for each of those fields. Upon running the spider, many items are correctly scraped; however, there are a large number of items where the manufacturer field contains the description, and the description field is empty. This has been caused by some pages having a different layout:

Layout A:

            name
            price

            manufacturer

            description

Layout B:

            name
            price

            description

As you can see, the problem lies with the fact that in layout B the description is where manufacturer would be, and with ``description`` not being a required field it means that the template created for layout A will match layout B. Creating a new template for layout B won't be enough to fix the problem, as layout A's template would contain more annotation and be matched against first.

Instead we need to modify layout A's template, and mark the ``description`` annotation as **Required**. With this added constraint, items displayed with layout B will not be matched against with layout A's template due to the missing ``description`` field, so the spider will proceed onto layout B's template which will extract the data successfully.

Running Portia
--------------

### Running Portia with Vagrant (Recommended)

Checkout the repository:

    git clone https://github.com/scrapinghub/portia

You will need both [Vagrant](http://www.vagrantup.com/downloads.html) and [VirtualBox](https://www.virtualbox.org/wiki/Downloads) installed.

Run the following in Portia's directory:

    vagrant up

This will launch a Ubuntu virtual machine, build Portia and start the ``slyd`` server. You'll then be able to access Portia at ``http://localhost:8000``. You can stop the ``slyd`` server using ``vagrant suspend`` or ``vagrant halt``. To run ``portiacrawl`` you will need to SSH into the virtual machine by running ``vagrant ssh``.

### Running Portia Locally

**These instructions are only valid for a Debian based OS**

If you would like to run Portia locally you should create an environment with virtualenv:

    virtualenv YOUR_ENV_NAME --no-site-packages
    source YOUR_ENV_NAME/bin/activate
    cd ENV_NAME

Now clone this repository into that env:

    git clone https://github.com/scrapinghub/portia.git

install the requirements for building Portia:

    sudo docker/splash.sh

and inside the env install the required packages:

    cd portia
    pip install -r requirements.txt
    pip install -e ./slybot

To run Portia start slyd:

    cd slyd
    bin/slyd

Portia should now be running on port 9001 and you can access it at: ``http://localhost:9001``

###### Missing Dependencies on Linux

When running Portia on Ubuntu or Debian systems you may need to install the following dependencies:

    sudo apt-get install libxml2-dev libxslt-dev python-dev zlib1g-dev libffi-dev libssl-dev

### Running Portia with Docker

Checkout the repository:

    git clone https://github.com/scrapinghub/portia

If you are on a Linux machine you will need [Docker](https://docs.docker.com/installation/) installed or if you are using a [Windows](https://docs.docker.com/installation/windows/) or [Mac OS X](https://docs.docker.com/installation/mac/) machine you will need [boot2docker](http://boot2docker.io/).

After following the appropriate instructions above the Portia image can be built using the command below:

    docker build -t portia .

Portia can be run using the command below:

    docker run -i -t --rm \
    -v <PROJECT_FOLDER>/data:/app/slyd/data:rw \
    -p 9001:9001 \
    --name portia \
    portia

Portia will now be running on port 9001 and you can access it at: ``http://localhost:9001/static/index.html``
Projects will be stored in the project folder that you mount to docker.

To run `portiacrawl` add `/app/slybot/bin/portiacrawl <PROJECT_PATH> [SPIDER] [OPTIONS]` to the command above.

:warning: `For Windows the <PROJECT_FOLDER> path must be of the form /<DRIVE_LETTER/<PATH>`

### Running a Portia Spider

Projects you have created in Portia will reside in ``slyd/data/projects``. You can use ``portiacrawl`` to run a spider from one of your projects:

    portiacrawl PROJECT_PATH SPIDER_NAME

where ``PROJECT_PATH`` is the path of the project and ``SPIDER_NAME`` is a spider that exists within that project. You can list the spiders for a project with the following:

    portiacrawl PROJECT_PATH

Portia spiders are ultimately [Scrapy](http://scrapy.org/) spiders. You can pass Scrapy arguments when running with ``portiacrawl`` using the ``-a`` option. You can also specify a custom settings module using the ``--settings`` option. The [Scrapy documentation](http://doc.scrapy.org/en/latest) contains full details on available options and settings.

Deploying a Project
-------------------

Portia projects can be deployed using [Scrapyd](http://scrapyd.readthedocs.org/en/latest). You can deploy a Portia project by going into ``slyd/data/projects/PROJECT_NAME`` and adding your target to ``scrapy.cfg``. You can then run ``scrapyd-deploy`` to deploy your project using the default deploy target, or specify a target and project using the following:

    scrapyd-deploy your_scrapyd_target -p project_name

and then schedule your spider with:

    curl http://your_scrapyd_host:6800/schedule.json -d project=your_project_name -d spider=your_spider_name

:warning: `Running scrapyd from your project directory will cause deployment to fail`

Additional Features
-------------------

### Git backend for projects

Versioning for projects can be enabled by creating a `local_settings.py` file in `slyd/slyd` with the contents below:

    import os
    SPEC_FACTORY = {
        'PROJECT_SPEC': 'slyd.gitstorage.projectspec.ProjectSpec',
        'PROJECT_MANAGER': 'slyd.gitstorage.projects.ProjectsManager',
        'PARAMS': {
            'storage_backend': 'dulwich.fsrepo.FsRepo',
            'location': os.environ.get('PORTIA_DATA_DIR', SPEC_DATA_DIR)
        },
        'CAPABILITIES': {
            'version_control': True,
            'create_projects': True,
            'delete_projects': True,
            'rename_projects': True
        }
    }

### MySQL backend for projects

A MySQL backend can  be used by adding a `local_settings.py` file in `slyd/slyd` with the contents below:

    import os
    SPEC_FACTORY = {
        'PROJECT_SPEC': 'slyd.gitstorage.projectspec.ProjectSpec',
        'PROJECT_MANAGER': 'slyd.gitstorage.projects.ProjectsManager',
        'PARAMS': {
            'storage_backend': 'dulwich.mysqlrepo.MysqlRepo',
            'location': os.environ.get('DB_URL'),
        },
        'CAPABILITIES': {
            'version_control': True,
            'create_projects': True,
            'delete_projects': True,
            'rename_projects': True
        }
    }

This will store versioned projects as blobs within the MySQL database that you specify by setting the environment variable below:

    DB_URL = mysql://<USERNAME>:<PASSWORD>@<HOST>:<PORT>/<DB>

When this env variable is set the database can be initialized by running the `bin/init_mysqldb` script.


text:
static NSString * const kSchemaKey = @"schemaKey";

static TrainingColorSchema sSchema = TrainingColorSchemaBlue;

@implementation UIColor(TrainingTimer)

+ (void)loadSchema{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSNumber * object = (NSNumber *)[[TMDiskCache sharedCache] objectForKey:kSchemaKey];
        if (object) {
            sSchema = (TrainingColorSchema)[object integerValue];
        }

        // Make Pink the default color schema
        sSchema = TrainingColorSchemaPink;
    });
}

+ (void)setSchema:(TrainingColorSchema)schema{
    [[TMDiskCache sharedCache] setObject:@(schema) forKey:kSchemaKey];
}

+ (UIColor *)barBackgroundColor{
    return [[self class] mainColor];
//    return RGB(0xD4, 0xD5, 0xD5);
}

+ (UIColor *)mainColor{
    switch (sSchema) {
        case TrainingColorSchemaOrange:
            return RGB(0xEB, 0x6E, 0x12);
        case TrainingColorSchemaPink:
            return RGB(0xEA, 0x60, 0x5D);
        case TrainingColorSchemaBlue:
            // From Colours Pod: https://github.com/bennyguitar/Colours
            return [UIColor skyBlueColor];
    }
}

+ (UIColor *)lineFgColor{
    return RGB(0xD4, 0xD5, 0xD5);
//    return RGB(246, 246, 246);
}

+ (UIColor *)lineBgColor{
    return RGB(0xAF, 0xAD, 0xAA);
}

@end


text:
  <md-content>

Date range

      Starting

      <md-datepicker
        ng-model="startDate"
        md-min-date="minStartDate"
        md-max-date="maxStartDate"
        date-types="types"
        date-type-index="index"
        valid-date="isValidStartDate">
      </md-datepicker>

      for

      <md-select style="margin-top: 7px" ng-style="duration === 'Custom' && { 'display': 'none' }" ng-model="duration" placeholder="Duration">
        <md-option ng-repeat="duration in durations" value="{{duration.label}}">{{duration.label}}</md-option>
        <md-option value="Custom" ng-click="showEndCalendar()">Custom</md-option>
      </md-select>

Testing, testing, the above shouldn't overlap or move this at all

  </md-content>


text:
/**
 *  @file  testNonlinearFactor.cpp
 *  @brief Unit tests for Non-Linear Factor,
 *  create a non linear factor graph and a values structure for it and
 *  calculate the error for the factor.
 *  @author Christian Potthast
 **/

/*STL/C++*/
#include <iostream>

#include <CppUnitLite/TestHarness.h>

// TODO: DANGEROUS, create shared pointers
#define GTSAM_MAGIC_GAUSSIAN 2

#include <gtsam/base/Testable.h>
#include <gtsam/base/Matrix.h>
#include <gtsam/base/LieVector.h>
#include <tests/smallExample.h>
#include <tests/simulated2D.h>
#include <gtsam/linear/GaussianFactor.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/Symbol.h>

using namespace std;
using namespace gtsam;
using namespace example;

// Convenience for named keys
using symbol_shorthand::X;
using symbol_shorthand::L;

typedef boost::shared_ptr<NonlinearFactor > shared_nlf;

/* ************************************************************************* */
TEST( NonlinearFactor, equals )
{
	SharedNoiseModel sigma(noiseModel::Isotropic::Sigma(2,1.0));

	// create two nonlinear2 factors
	Point2 z3(0.,-1.);
	simulated2D::Measurement f0(z3, sigma, X(1),L(1));

	// measurement between x2 and l1
	Point2 z4(-1.5, -1.);
	simulated2D::Measurement f1(z4, sigma, X(2),L(1));

	CHECK(assert_equal(f0,f0));
	CHECK(f0.equals(f0));
	CHECK(!f0.equals(f1));
	CHECK(!f1.equals(f0));
}

/* ************************************************************************* */
TEST( NonlinearFactor, equals2 )
{
  // create a non linear factor graph
  Graph fg = createNonlinearFactorGraph();

  // get two factors
  Graph::sharedFactor f0 = fg[0], f1 = fg[1];

  CHECK(f0->equals(*f0));
	CHECK(!f0->equals(*f1));
	CHECK(!f1->equals(*f0));
}

/* ************************************************************************* */
TEST( NonlinearFactor, NonlinearFactor )
{
  // create a non linear factor graph
  Graph fg = createNonlinearFactorGraph();

  // create a values structure for the non linear factor graph
  Values cfg = createNoisyValues();

  // get the factor "f1" from the factor graph
  Graph::sharedFactor factor = fg[0];

  // calculate the error_vector from the factor "f1"
  // error_vector = [0.1 0.1]
  Vector actual_e = boost::dynamic_pointer_cast<NoiseModelFactor>(factor)->unwhitenedError(cfg);
  CHECK(assert_equal(0.1*ones(2),actual_e));

  // error = 0.5 * [1 1] * [1;1] = 1
  double expected = 1.0;

  // calculate the error from the factor "f1"
  double actual = factor->error(cfg);
  DOUBLES_EQUAL(expected,actual,0.00000001);
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_f1 )
{
	Values c = createNoisyValues();

  // Grab a non-linear factor
  Graph nfg = createNonlinearFactorGraph();
  Graph::sharedFactor nlf = nfg[0];

  // We linearize at noisy config from SmallExample
  GaussianFactor::shared_ptr actual = nlf->linearize(c, *c.orderingArbitrary());

  GaussianFactorGraph lfg = createGaussianFactorGraph(*c.orderingArbitrary());
  GaussianFactor::shared_ptr expected = lfg[0];

  CHECK(assert_equal(*expected,*actual));

  // The error |A*dx-b| approximates (h(x0+dx)-z) = -error_vector
  // Hence i.e., b = approximates z-h(x0) = error_vector(x0)
	//CHECK(assert_equal(nlf->error_vector(c),actual->get_b()));
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_f2 )
{
	Values c = createNoisyValues();

  // Grab a non-linear factor
  Graph nfg = createNonlinearFactorGraph();
  Graph::sharedFactor nlf = nfg[1];

  // We linearize at noisy config from SmallExample
  GaussianFactor::shared_ptr actual = nlf->linearize(c, *c.orderingArbitrary());

  GaussianFactorGraph lfg = createGaussianFactorGraph(*c.orderingArbitrary());
  GaussianFactor::shared_ptr expected = lfg[1];

  CHECK(assert_equal(*expected,*actual));
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_f3 )
{
  // Grab a non-linear factor
  Graph nfg = createNonlinearFactorGraph();
  Graph::sharedFactor nlf = nfg[2];

  // We linearize at noisy config from SmallExample
  Values c = createNoisyValues();
  GaussianFactor::shared_ptr actual = nlf->linearize(c, *c.orderingArbitrary());

  GaussianFactorGraph lfg = createGaussianFactorGraph(*c.orderingArbitrary());
  GaussianFactor::shared_ptr expected = lfg[2];

  CHECK(assert_equal(*expected,*actual));
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_f4 )
{
  // Grab a non-linear factor
  Graph nfg = createNonlinearFactorGraph();
  Graph::sharedFactor nlf = nfg[3];

  // We linearize at noisy config from SmallExample
  Values c = createNoisyValues();
  GaussianFactor::shared_ptr actual = nlf->linearize(c, *c.orderingArbitrary());

  GaussianFactorGraph lfg = createGaussianFactorGraph(*c.orderingArbitrary());
  GaussianFactor::shared_ptr expected = lfg[3];

  CHECK(assert_equal(*expected,*actual));
}

/* ************************************************************************* */
TEST( NonlinearFactor, size )
{
	// create a non linear factor graph
	Graph fg = createNonlinearFactorGraph();

	// create a values structure for the non linear factor graph
	Values cfg = createNoisyValues();

	// get some factors from the graph
	Graph::sharedFactor factor1 = fg[0], factor2 = fg[1],
			factor3 = fg[2];

	CHECK(factor1->size() == 1);
	CHECK(factor2->size() == 2);
	CHECK(factor3->size() == 2);
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_constraint1 )
{
	Vector sigmas = Vector_(2, 0.2, 0.0);
	SharedDiagonal constraint = noiseModel::Constrained::MixedSigmas(sigmas);

	Point2 mu(1., -1.);
	Graph::sharedFactor f0(new simulated2D::Prior(mu, constraint, X(1)));

	Values config;
	config.insert(X(1), Point2(1.0, 2.0));
	GaussianFactor::shared_ptr actual = f0->linearize(config, *config.orderingArbitrary());

	// create expected
	Ordering ord(*config.orderingArbitrary());
	Vector b = Vector_(2, 0., -3.);
	JacobianFactor expected(ord[X(1)], Matrix_(2,2, 5.0, 0.0, 0.0, 1.0), b, constraint);
	CHECK(assert_equal((const GaussianFactor&)expected, *actual));
}

/* ************************************************************************* */
TEST( NonlinearFactor, linearize_constraint2 )
{
	Vector sigmas = Vector_(2, 0.2, 0.0);
	SharedDiagonal constraint = noiseModel::Constrained::MixedSigmas(sigmas);

	Point2 z3(1.,-1.);
	simulated2D::Measurement f0(z3, constraint, X(1),L(1));

	Values config;
	config.insert(X(1), Point2(1.0, 2.0));
	config.insert(L(1), Point2(5.0, 4.0));
	GaussianFactor::shared_ptr actual = f0.linearize(config, *config.orderingArbitrary());

	// create expected
	Ordering ord(*config.orderingArbitrary());
	Matrix A = Matrix_(2,2, 5.0, 0.0, 0.0, 1.0);
	Vector b = Vector_(2, -15., -3.);
	JacobianFactor expected(ord[X(1)], -1*A, ord[L(1)], A, b, constraint);
	CHECK(assert_equal((const GaussianFactor&)expected, *actual));
}

/* ************************************************************************* */
class TestFactor4 : public NoiseModelFactor4<LieVector, LieVector, LieVector, LieVector> {
public:
  typedef NoiseModelFactor4<LieVector, LieVector, LieVector, LieVector> Base;
  TestFactor4() : Base(noiseModel::Diagonal::Sigmas(Vector_(1, 2.0)), X(1), X(2), X(3), X(4)) {}

  virtual Vector
    evaluateError(const LieVector& x1, const LieVector& x2, const LieVector& x3, const LieVector& x4,
        boost::optional<Matrix&> H1 = boost::none,
        boost::optional<Matrix&> H2 = boost::none,
        boost::optional<Matrix&> H3 = boost::none,
        boost::optional<Matrix&> H4 = boost::none) const {
    if(H1) {
      *H1 = Matrix_(1,1, 1.0);
      *H2 = Matrix_(1,1, 2.0);
      *H3 = Matrix_(1,1, 3.0);
      *H4 = Matrix_(1,1, 4.0);
    }
    return (Vector(1) << x1 + x2 + x3 + x4).finished();
  }

	virtual gtsam::NonlinearFactor::shared_ptr clone() const {
	  return boost::static_pointer_cast<gtsam::NonlinearFactor>(
	      gtsam::NonlinearFactor::shared_ptr(new TestFactor4(*this))); }
};

/* ************************************ */
TEST(NonlinearFactor, NoiseModelFactor4) {
  TestFactor4 tf;
  Values tv;
  tv.insert(X(1), LieVector(1, 1.0));
  tv.insert(X(2), LieVector(1, 2.0));
  tv.insert(X(3), LieVector(1, 3.0));
  tv.insert(X(4), LieVector(1, 4.0));
  EXPECT(assert_equal(Vector_(1, 10.0), tf.unwhitenedError(tv)));
  DOUBLES_EQUAL(25.0/2.0, tf.error(tv), 1e-9);
  Ordering ordering; ordering += X(1), X(2), X(3), X(4);
  JacobianFactor jf(*boost::dynamic_pointer_cast<JacobianFactor>(tf.linearize(tv, ordering)));
  LONGS_EQUAL(jf.keys()[0], 0);
  LONGS_EQUAL(jf.keys()[1], 1);
  LONGS_EQUAL(jf.keys()[2], 2);
  LONGS_EQUAL(jf.keys()[3], 3);
  EXPECT(assert_equal(Matrix_(1,1, 0.5), jf.getA(jf.begin())));
  EXPECT(assert_equal(Matrix_(1,1, 1.0), jf.getA(jf.begin()+1)));
  EXPECT(assert_equal(Matrix_(1,1, 1.5), jf.getA(jf.begin()+2)));
  EXPECT(assert_equal(Matrix_(1,1, 2.0), jf.getA(jf.begin()+3)));
  EXPECT(assert_equal(Vector_(1, -5.0), jf.getb()));
}

/* ************************************************************************* */
class TestFactor5 : public NoiseModelFactor5<LieVector, LieVector, LieVector, LieVector, LieVector> {
public:
  typedef NoiseModelFactor5<LieVector, LieVector, LieVector, LieVector, LieVector> Base;
  TestFactor5() : Base(noiseModel::Diagonal::Sigmas(Vector_(1, 2.0)), X(1), X(2), X(3), X(4), X(5)) {}

  virtual Vector
    evaluateError(const X1& x1, const X2& x2, const X3& x3, const X4& x4, const X5& x5,
        boost::optional<Matrix&> H1 = boost::none,
        boost::optional<Matrix&> H2 = boost::none,
        boost::optional<Matrix&> H3 = boost::none,
        boost::optional<Matrix&> H4 = boost::none,
        boost::optional<Matrix&> H5 = boost::none) const {
    if(H1) {
      *H1 = Matrix_(1,1, 1.0);
      *H2 = Matrix_(1,1, 2.0);
      *H3 = Matrix_(1,1, 3.0);
      *H4 = Matrix_(1,1, 4.0);
      *H5 = Matrix_(1,1, 5.0);
    }
    return (Vector(1) << x1 + x2 + x3 + x4 + x5).finished();
  }
};

/* ************************************ */
TEST(NonlinearFactor, NoiseModelFactor5) {
  TestFactor5 tf;
  Values tv;
  tv.insert(X(1), LieVector(1, 1.0));
  tv.insert(X(2), LieVector(1, 2.0));
  tv.insert(X(3), LieVector(1, 3.0));
  tv.insert(X(4), LieVector(1, 4.0));
  tv.insert(X(5), LieVector(1, 5.0));
  EXPECT(assert_equal(Vector_(1, 15.0), tf.unwhitenedError(tv)));
  DOUBLES_EQUAL(56.25/2.0, tf.error(tv), 1e-9);
  Ordering ordering; ordering += X(1), X(2), X(3), X(4), X(5);
  JacobianFactor jf(*boost::dynamic_pointer_cast<JacobianFactor>(tf.linearize(tv, ordering)));
  LONGS_EQUAL(jf.keys()[0], 0);
  LONGS_EQUAL(jf.keys()[1], 1);
  LONGS_EQUAL(jf.keys()[2], 2);
  LONGS_EQUAL(jf.keys()[3], 3);
  LONGS_EQUAL(jf.keys()[4], 4);
  EXPECT(assert_equal(Matrix_(1,1, 0.5), jf.getA(jf.begin())));
  EXPECT(assert_equal(Matrix_(1,1, 1.0), jf.getA(jf.begin()+1)));
  EXPECT(assert_equal(Matrix_(1,1, 1.5), jf.getA(jf.begin()+2)));
  EXPECT(assert_equal(Matrix_(1,1, 2.0), jf.getA(jf.begin()+3)));
  EXPECT(assert_equal(Matrix_(1,1, 2.5), jf.getA(jf.begin()+4)));
  EXPECT(assert_equal(Vector_(1, -7.5), jf.getb()));
}

/* ************************************************************************* */
class TestFactor6 : public NoiseModelFactor6<LieVector, LieVector, LieVector, LieVector, LieVector, LieVector> {
public:
  typedef NoiseModelFactor6<LieVector, LieVector, LieVector, LieVector, LieVector, LieVector> Base;
  TestFactor6() : Base(noiseModel::Diagonal::Sigmas(Vector_(1, 2.0)), X(1), X(2), X(3), X(4), X(5), X(6)) {}

  virtual Vector
    evaluateError(const X1& x1, const X2& x2, const X3& x3, const X4& x4, const X5& x5, const X6& x6,
        boost::optional<Matrix&> H1 = boost::none,
        boost::optional<Matrix&> H2 = boost::none,
        boost::optional<Matrix&> H3 = boost::none,
        boost::optional<Matrix&> H4 = boost::none,
        boost::optional<Matrix&> H5 = boost::none,
        boost::optional<Matrix&> H6 = boost::none) const {
    if(H1) {
      *H1 = Matrix_(1,1, 1.0);
      *H2 = Matrix_(1,1, 2.0);
      *H3 = Matrix_(1,1, 3.0);
      *H4 = Matrix_(1,1, 4.0);
      *H5 = Matrix_(1,1, 5.0);
      *H6 = Matrix_(1,1, 6.0);
    }
    return (Vector(1) << x1 + x2 + x3 + x4 + x5 + x6).finished();
  }

};

/* ************************************ */
TEST(NonlinearFactor, NoiseModelFactor6) {
  TestFactor6 tf;
  Values tv;
  tv.insert(X(1), LieVector(1, 1.0));
  tv.insert(X(2), LieVector(1, 2.0));
  tv.insert(X(3), LieVector(1, 3.0));
  tv.insert(X(4), LieVector(1, 4.0));
  tv.insert(X(5), LieVector(1, 5.0));
  tv.insert(X(6), LieVector(1, 6.0));
  EXPECT(assert_equal(Vector_(1, 21.0), tf.unwhitenedError(tv)));
  DOUBLES_EQUAL(110.25/2.0, tf.error(tv), 1e-9);
  Ordering ordering; ordering += X(1), X(2), X(3), X(4), X(5), X(6);
  JacobianFactor jf(*boost::dynamic_pointer_cast<JacobianFactor>(tf.linearize(tv, ordering)));
  LONGS_EQUAL(jf.keys()[0], 0);
  LONGS_EQUAL(jf.keys()[1], 1);
  LONGS_EQUAL(jf.keys()[2], 2);
  LONGS_EQUAL(jf.keys()[3], 3);
  LONGS_EQUAL(jf.keys()[4], 4);
  LONGS_EQUAL(jf.keys()[5], 5);
  EXPECT(assert_equal(Matrix_(1,1, 0.5), jf.getA(jf.begin())));
  EXPECT(assert_equal(Matrix_(1,1, 1.0), jf.getA(jf.begin()+1)));
  EXPECT(assert_equal(Matrix_(1,1, 1.5), jf.getA(jf.begin()+2)));
  EXPECT(assert_equal(Matrix_(1,1, 2.0), jf.getA(jf.begin()+3)));
  EXPECT(assert_equal(Matrix_(1,1, 2.5), jf.getA(jf.begin()+4)));
  EXPECT(assert_equal(Matrix_(1,1, 3.0), jf.getA(jf.begin()+5)));
  EXPECT(assert_equal(Vector_(1, -10.5), jf.getb()));

}

/* ************************************************************************* */
TEST( NonlinearFactor, clone_rekey )
{
	shared_nlf init(new TestFactor4());
  EXPECT_LONGS_EQUAL(X(1), init->keys()[0]);
  EXPECT_LONGS_EQUAL(X(2), init->keys()[1]);
  EXPECT_LONGS_EQUAL(X(3), init->keys()[2]);
  EXPECT_LONGS_EQUAL(X(4), init->keys()[3]);

  // Standard clone
  shared_nlf actClone = init->clone();
  EXPECT(actClone.get() != init.get()); // Ensure different pointers
  EXPECT(assert_equal(*init, *actClone));

  // Re-key factor - clones with different keys
  std::vector<Key> new_keys(4);
  new_keys[0] = X(5);
  new_keys[1] = X(6);
  new_keys[2] = X(7);
  new_keys[3] = X(8);
  shared_nlf actRekey = init->rekey(new_keys);
  EXPECT(actRekey.get() != init.get()); // Ensure different pointers

  // Ensure init is unchanged
  EXPECT_LONGS_EQUAL(X(1), init->keys()[0]);
  EXPECT_LONGS_EQUAL(X(2), init->keys()[1]);
  EXPECT_LONGS_EQUAL(X(3), init->keys()[2]);
  EXPECT_LONGS_EQUAL(X(4), init->keys()[3]);

  // Check new keys
  EXPECT_LONGS_EQUAL(X(5), actRekey->keys()[0]);
  EXPECT_LONGS_EQUAL(X(6), actRekey->keys()[1]);
  EXPECT_LONGS_EQUAL(X(7), actRekey->keys()[2]);
  EXPECT_LONGS_EQUAL(X(8), actRekey->keys()[3]);
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SLB Configuration And Versions</title>

    <link rel="stylesheet" href="/static/lib/bootstrap/css/bootstrap.css">
    <link rel="stylesheet" href="/static/lib/velonic-admin/css/bootstrap-reset.css">

    <link rel="stylesheet" href="/static/lib/bootstrap-table/bootstrap-table.css">
    <link rel="stylesheet" href="/static/lib/bootstrap-table/bootstrap-editable.css">

    <link rel="stylesheet" href="/static/lib/font-awesome-4.6.3/css/font-awesome.min.css">

    <link rel="stylesheet" href="/static/css/animate.css">
    <link href="/static/lib/velonic-admin/css/animate.css" rel="stylesheet">
    <link href="/static/lib/velonic-admin/assets/ionicon/css/ionicons.min.css" rel="stylesheet"/>

    <link href="/static/lib/velonic-admin/css/style.css" rel="stylesheet">
    <link href="/static/lib/velonic-admin/css/helper.css" rel="stylesheet">

    <link rel="stylesheet" href="/static/css/global-new.css">
    <link rel="stylesheet" href="/static/css/formatter.css">
    <link rel="stylesheet" href="/static/css/release.css">

            SLB Portal

        <button type="button" class="navbar-toggle pull-left">
            Toggle navigation

        </button>

                {{x.name}}

 <button class="btn btn-default btn-xs language-bt">语言</button> <select class="language-choose" ng-model="selected" ng-change="changeLanguage(selected)"><option style="color: white" data-ng-repeat="lan in data.languages">{{lan}}</option></select>

 Feedback

                    {{query.user}}

 {{resource['userinfo']['home']}}

 {{resource['userinfo']['auth']}}

 {{resource['userinfo']['logout']}}

                {{resource["navs"]["navs_info"]}}

 {{resource["navs"]["chart"]}}

 证书管理

 健康检测配置

 {{resource["navs"]["navs_net_java"]}}

 {{resource["navs"]["navs_release_version"]}}

 {{resource["navs"]["navs_health"]}}

 {{resource["navs"]["navs_log"]}}

 {{resource["navs"]["navs_feedback"]}}

 {{resource["navs"]["navs_rule"]}}

                    {{resource['bread']['home']}}

                    {{x}}

                         {{x}}

                        {{k}} : {{v}}

                        {{x.id}}/{{x.name}}

                             {{y.ip}}

                        {{x.id}}/{{x.name}}
                        ConfVersion:
                        {{x.version}}

                             {{y.ip}} / {{y.version}}

            ©2016 Framework, ctrip.com, all rights reserved; Support:框架研发部
            SLB Team

                <button type="button" class="close" data-dismiss="modal">×</button>

{{resource.languageApp.languageController.feedback}}

 {{resource.questionModal.token1}}

<textarea cols="2" rows="10" style="width: 100%"

                                                              id="comment-text"
                                                              placeholder="{{resource.questionModal.token2}}"></textarea>

                <button type="button" class="btn btn-info" id="submmit-question" data-dismiss="modal">
                    {{resource.userHome.html.modal.msg1}}
                </button>
                <button type="button" class="btn btn-warning deny-operator" data-dismiss="modal">
                    {{resource.userHome.html.modal.msg2}}
                </button>

        <input type="hidden" value="" class="status-hidden"/> <input type="hidden" value="" class="op-hidden"/> <input
                type="hidden" value="" class="ip-hidden"/>

<link href="/static/lib/velonic-admin/assets/sweet-alert/sweet-alert.min.css" rel="stylesheet">


text:
i18n.map('en', {
  global: {
    save: 'Guardar',
    create: 'Crear',
    logout: 'Salir',
    back: 'Atrás',
    cancel: 'Cancelar',
    delete: 'Borrar',
    confirm: 'Confirmar',
    choose: 'Elegir',
    noPermission: 'No tienes permisos',
    passwordNotMatch: 'Las contraseñas no son iguales',
    optional: 'Opcional'
  },
  accounts: {
    schema: {
      emails: {
        title: 'Emails',
        address: 'Dirección',
        verified: 'Verificado'
      },
      password: {
        title: 'Contraseña',
        new: 'Nueva Contraseña',
        confirm: 'Confirmar Contraseña'
      },
      profile: {
        name: 'Nombre'
      }
    },
    index: {
      title: 'Cuentas',
      actions: {
         edit: 'Editar',
      },
      tableTitles: {
        name: 'Nombre',
        email: 'Email',
        roles: 'Roles',
        actions: 'Acciones'
      }
    },
    update: {
      title: 'Editar Cuenta',
      messages: {
        noPermissions: 'No tienes permisos para editar este usuario',
      },
      sections: {
        profile: {
          title: 'Perfil'
        },
        roles: {
          title: 'Roles',
          selectRoles: 'Selecciona los roles'
        },
        changePassword: {
          title: 'Cambiar Contraseña'
        },
        deleteUser: {
          title: 'Borrar usuario',
          advice: 'Borrar usuarios puede causar problemas.',
          button: 'Borrar Usuario'
        }
      }
    },
    myAccount: {
      title: 'Mi Cuenta',
    },
    create: {
      title: 'Crear un usuario',
      createInvitation: 'Crear invitación',
      createUserNow: 'Crear usuario ahora',
      inviteOther: 'Invitar a otro',
      selectRoles: 'Selecciona los roles para el nuevo usuario',
      email: 'Email',
      inviteButton: 'Invitar',
      messages: {
        successfullyCreated: 'La invitación fue creada satisfactoriamente'
      }
    },
    changePassword: {
      title: 'Cambiar contraseña',
    },
    updateProfile: {
      title: 'Editar perfil',
    },
    register: {
      title: 'Registrarse',
      registerButton: 'Registrarse',
      fields: {
        email: 'Email',
        name: 'Nombre',
        password: 'Contraseña',
        confirmPassword: 'Confirmar contraseña'
      },
      messages: {
        invalidEmail: 'Email invalido',
        invalidInvitationCode: 'El código de invitacion es invalido',
      }
    }
  },
  collections: {
    create: {
      title: 'Crear un {$1}'
    },
    update: {
      title: 'Actualizar {$1}'
    },
    delete: {
      title: 'Borrar {$1}',
      confirmQuestion: '¿Estás seguro de que quieres borrar este {$1}?'
    },
    common: {
      defaultPluralName: 'items',
      defaultSingularName: 'item',
    }
  },
  config: {
    update: {
      title: 'Configuración',
    }
  },
  dictionary: {
    update: {
      title: 'Diccionario'
    }
  },
  filesystem: {
    messages: {
      notFound_id: 'Archivo no encontrado [{$i}]',
      errorUploading: 'Error subiendo archivo',
      errorRemoving: 'Error borrando archivo',
    }
  },
  pages: {
    schema: {
      title: 'Título',
      url: 'Url',
    },
    index: {
      title: 'Páginas',
    },
    create: {
      title: 'Crear página',
      chooseTemplate: 'Elegir plantilla'
    },
    update: {
      title: 'Editar página',
    },
    delete: {
      title: 'Borrar página',
      confirmQuestion: '¿Estás seguro de que quieres borrar esta página?'
    }
  },
  attributes: {
    users: {
      pluralName: 'usuarios',
      singularName: 'usuario',
    },
    file: {
      choose: 'Elegir archivo',
      noFile: 'Ningún archivo seleccionado',
    }
  },
  tabular: {
    search: 'Buscar:',
    info: 'Mostrando registros del _START_ al _END_ de un total de _TOTAL_ registros',
    infoEmpty: 'Mostrando registros del 0 al 0 de un total de 0 registros',
    lengthMenu: 'Mostrar _MENU_ registros',
    emptyTable: 'Ningún dato disponible en esta tabla',
    paginate: {
      first: 'Primero',
      previous: 'Anterior',
      next: 'Siguente',
      last: 'Último',
    }

  }
})


text:
import { Component, OnInit } from '@angular/core';
import {EngineService} from '../gameplay/engine.service';
import {Http} from '@angular/http';
import {Highscore} from './highscore';
import {Observable, Subject} from 'rxjs';
import {TranslateService} from '@ngx-translate/core';

@Component({
  selector: 'highscore',
  templateUrl: './highscore.component.html',
  styleUrls: ['./highscore.component.scss']
})
export class HighscoreComponent {

  public highscores$ = new Subject<Highscore[]>();

  constructor(public engineService: EngineService, http: Http, translate: TranslateService) {
    translate.use("es");
    http.get("https://example.com/rest/highscore").subscribe(response => {
      this.highscores$.next(response.json() as Highscore[]);
    });
  }
}


text:
use memory::{Address, MemoryArea, PageFlags, PhysicalAddress, VirtualAddress};

pub mod address_space_manager;
mod paging;

pub use self::paging::get_free_memory_size;

/// The maximum address of the lower part of the virtual address space.
const VIRTUAL_LOW_MAX_ADDRESS: VirtualAddress = VirtualAddress::from_const(0x00007fffffffffff);

/// The minimum address of the higher part of the virtual address space.
const VIRTUAL_HIGH_MIN_ADDRESS: VirtualAddress = VirtualAddress::from_const(0xffff800000000000);

/// The top of the stack after the kernel has been remapped.
pub const FINAL_STACK_TOP: VirtualAddress = VirtualAddress::from_const(0xfffffe8000000000);

/// The start address for the double fault stack area.
pub const DOUBLE_FAULT_STACK_AREA_BASE: VirtualAddress =
    VirtualAddress::from_const(0xfffffd0000000000);

/// The distance between two double fault stack tops.
pub const DOUBLE_FAULT_STACK_OFFSET: usize = 0x2000;

/// The maximum size of a double fault stack.
pub const DOUBLE_FAULT_STACK_MAX_SIZE: usize = 0x1000;

/// The base address of the kernel stack area.
pub const KERNEL_STACK_AREA_BASE: VirtualAddress = VirtualAddress::from_const(0xfffffe0000000000);

/// The offset of the start addresses of thread kernel stacks.
pub const KERNEL_STACK_OFFSET: usize = 0x400000;

/// The maximum size of a thread kernel stack.
pub const KERNEL_STACK_MAX_SIZE: usize = 0x200000;

/// The base address of the process stack area.
pub const USER_STACK_AREA_BASE: VirtualAddress = VirtualAddress::from_const(0x00007f8000000000);

/// The offset of the start addresses of thread stacks.
pub const USER_STACK_OFFSET: usize = 0x400000;

/// The maximum size of a thread stack.
pub const USER_STACK_MAX_SIZE: usize = 0x200000;

/// The start address of the heap.
pub const HEAP_START: VirtualAddress = VirtualAddress::from_const(0xfffffd8000000000);

/// The maximum size of the heap.
///
/// This is the amount of space a level 3 page table manages.
pub const HEAP_MAX_SIZE: usize = PAGE_SIZE * 512 * 512 * 512;

/// The size of a single page.
pub const PAGE_SIZE: usize = 0x1000;

/// The area where the initramfs will be mapped.
const INITRAMFS_MAP_AREA_START: VirtualAddress =
    VirtualAddress::from_const(0xffff800000000000 + 512 * 512 * 512);

/// The run-time memory area of the initramfs.
static mut INITRAMFS_AREA: MemoryArea<VirtualAddress> = MemoryArea::const_default();

extern "C" {
    /// The end of the kernel in its initial mapping.
    static KERNEL_END: PhysicalAddress;
    /// The start of the .text segment.
    static TEXT_START: PhysicalAddress;
    /// The start of the .rodata segment.
    static RODATA_START: PhysicalAddress;
    /// The start of the .data segment.
    static DATA_START: PhysicalAddress;
    /// The start of the .bss segment.
    static BSS_START: PhysicalAddress;
    /// The end of the .bss segment.
    static BSS_END: PhysicalAddress;
    /// The address of the temporary map table.
    static TEMPORARY_MAP_TABLE: PhysicalAddress;
    /// The address of the initial level 4 page table.
    static L4_TABLE: PhysicalAddress;
    /// The address of the initial level 3 page table.
    static L3_TABLE: PhysicalAddress;
    /// The address of the initial level 2 page table.
    static L2_TABLE: PhysicalAddress;
    /// The address of the initial stack level 2 page table.
    static STACK_L2_TABLE: PhysicalAddress;
    /// The address of the initial stack level 1 page table.
    static STACK_L1_TABLE: PhysicalAddress;
    /// The bottom of the initial kernel stack.
    static STACK_BOTTOM: PhysicalAddress;
    /// The top of the initial kernel stack.
    static STACK_TOP: PhysicalAddress;
}

/// The physical address at which the kernel starts.
pub fn get_kernel_area() -> MemoryArea<PhysicalAddress> {
    let start = unsafe { TEXT_START };
    let end = unsafe { KERNEL_END };
    MemoryArea::from_start_and_end(start, end)
}

/// Initializes the memory manager.
pub fn init() {
    assert_has_not_been_called!("The x86_64 memory initialization should only be called once.");

    let physical_initramfs_area = ::boot::get_initramfs_area();

    paging::init(physical_initramfs_area);

    let start = INITRAMFS_MAP_AREA_START + physical_initramfs_area.start_address().offset_in_page();
    unsafe {
        INITRAMFS_AREA = MemoryArea::new(start, physical_initramfs_area.length());
    }
}

/// Returns the start address of the initramfs.
pub fn get_initramfs_area() -> MemoryArea<VirtualAddress> {
    unsafe { INITRAMFS_AREA }
}

/// Maps the given page using the given flags.
pub fn map_page(page_address: VirtualAddress, flags: PageFlags) {
    paging::map_page(page_address, flags);
}

/// Maps the given page to the given frame using the given flags.
pub fn map_page_at(page_address: VirtualAddress, frame_address: PhysicalAddress, flags: PageFlags) {
    paging::map_page_at(page_address, frame_address, flags);
}

/// Returns the flags of the given page.
pub fn get_page_flags(page_address: VirtualAddress) -> PageFlags {
    paging::get_page_flags(page_address)
}

/// Unmaps the given page.
///
/// # Safety
/// - Make sure that nothing references that page anymore.
pub unsafe fn unmap_page(start_address: VirtualAddress) {
    paging::unmap_page(start_address);
}

/// Checks if the address is a kernel or a userspace address.
pub fn is_userspace_address(address: VirtualAddress) -> bool {
    address <= VIRTUAL_LOW_MAX_ADDRESS
}


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>Uses of Package org.apache.cassandra.service (apache-cassandra API)</title>
<meta name="date" content="2015-08-28">
<link rel="stylesheet" type="text/css" href="../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Uses of Package
org.apache.cassandra.service

Packages that use org.apache.cassandra.service

Package
Description

org.apache.cassandra.auth

org.apache.cassandra.cache

org.apache.cassandra.config

org.apache.cassandra.cql3

org.apache.cassandra.cql3.statements

org.apache.cassandra.db

org.apache.cassandra.locator

org.apache.cassandra.net

org.apache.cassandra.repair

org.apache.cassandra.service

org.apache.cassandra.service.pager

org.apache.cassandra.thrift

org.apache.cassandra.tools

org.apache.cassandra.transport

org.apache.cassandra.transport.messages

Classes in org.apache.cassandra.service used by org.apache.cassandra.auth

Class and Description

MigrationListener

Classes in org.apache.cassandra.service used by org.apache.cassandra.cache

Class and Description

CacheService.CacheType

Classes in org.apache.cassandra.service used by org.apache.cassandra.config

Class and Description

CacheService.CacheType

Classes in org.apache.cassandra.service used by org.apache.cassandra.cql3

Class and Description

ClientState

State related to a client connection.

QueryState

Represents the state related to a given query.

Classes in org.apache.cassandra.service used by org.apache.cassandra.cql3.statements

Class and Description

CASRequest

Abstract the conditions and updates for a CAS operation.

ClientState

State related to a client connection.

QueryState

Represents the state related to a given query.

Classes in org.apache.cassandra.service used by org.apache.cassandra.db

Class and Description

IReadCommand

MigrationListener

RowDataResolver

Classes in org.apache.cassandra.service used by org.apache.cassandra.locator

Class and Description

AbstractWriteResponseHandler

Classes in org.apache.cassandra.service used by org.apache.cassandra.net

Class and Description

AbstractWriteResponseHandler

Classes in org.apache.cassandra.service used by org.apache.cassandra.repair

Class and Description

StorageService

This abstraction contains the token/identifier of this node
 on the identifier space.

Classes in org.apache.cassandra.service used by org.apache.cassandra.service

Class and Description

AbstractReadExecutor

Sends a read request to the replicas needed to satisfy a given ConsistencyLevel.

AbstractRowResolver

AbstractWriteResponseHandler

ActiveRepairService

ActiveRepairService is the starting point for manual "active" repairs.

ActiveRepairService.ParentRepairSession

CacheService

CacheService.CacheType

CacheServiceMBean

CASRequest

Abstract the conditions and updates for a CAS operation.

CassandraDaemon

The CassandraDaemon is an abstraction for a Cassandra daemon
 service, which defines not only a way to activate and deactivate it, but also
 hooks into its lifecycle methods (see CassandraDaemon.setup(), CassandraDaemon.start(),
 CassandraDaemon.stop() and CassandraDaemon.setup()).

CassandraDaemon.Server

ClientState

State related to a client connection.

DigestMismatchException

FileCacheService

FileCacheService.CacheKey

GCInspectorMXBean

IEndpointLifecycleSubscriber

Interface on which interested parties can be notified of high level endpoint
 state changes.

IReadCommand

IResponseResolver

LoadBroadcaster

MigrationListener

MigrationManager

MigrationManager.MigrationsSerializer

PendingRangeCalculatorService

QueryState

Represents the state related to a given query.

ReadCallback

RowDataResolver

RowDigestResolver

StartupCheck

A test to determine if the system is in a valid state to start up.

StartupChecks

Verifies that the system and environment is in a fit state to be started.

StorageProxy

StorageProxy.WritePerformer

StorageProxyMBean

StorageService

This abstraction contains the token/identifier of this node
 on the identifier space.

StorageServiceMBean

WriteResponseHandler

Handles blocking writes for ONE, ANY, TWO, THREE, QUORUM, and ALL consistency levels.

Classes in org.apache.cassandra.service used by org.apache.cassandra.service.pager

Class and Description

ClientState

State related to a client connection.

Classes in org.apache.cassandra.service used by org.apache.cassandra.thrift

Class and Description

CassandraDaemon.Server

ClientState

State related to a client connection.

QueryState

Represents the state related to a given query.

Classes in org.apache.cassandra.service used by org.apache.cassandra.tools

Class and Description

CacheServiceMBean

StorageProxyMBean

StorageServiceMBean

Classes in org.apache.cassandra.service used by org.apache.cassandra.transport

Class and Description

CassandraDaemon.Server

QueryState

Represents the state related to a given query.

Classes in org.apache.cassandra.service used by org.apache.cassandra.transport.messages

Class and Description

QueryState

Represents the state related to a given query.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Copyright © 2015 The Apache Software Foundation


text:
var _ = require('lodash');
var Logger = require('../debug/logger.js');

var Cache = function(game) {
  this.game = game;
  this.cacheList = {};
};

Cache.prototype = {
  clear: function() {
    this.cacheList = {};
  },

  addObject: function(key, obj) {
    this.cacheList[key] = obj;
  },

  getObject: function(key) {
    var obj = this.cacheList[key];
    if(obj) {
      return obj;
    }
    else {
      Logger.warn("Object with key "+key+" not found.");
      return null;
    }
  }
};

module.exports = Cache;


text:
require 'spec_helper'

describe Sugifnoc do
  describe '#build' do
    before :each do
      @config = Sugifnoc.build :test do
        env :test do
          host 'www.example.com'
          port
          email 'test@example.com'
        end
      end
    end

    it 'assigns a value to a key' do
      @config.should be
    end

    it 'returns value if defined' do
      @config.host.should == 'www.example.com'
    end

    it 'returns nil if so defined' do
      @config.port.should be_nil
    end

    it 'raises error if value not exist' do
      expect { @config.foo }.to raise_error
    end
  end
end


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.networkservices.v1.model;

/**
 * Specification of how client requests are delayed as part of fault injection before being sent to
 * a destination.
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Network Services API. For a detailed explanation see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class GrpcRouteFaultInjectionPolicyDelay extends com.google.api.client.json.GenericJson {

  /**
   * Specify a fixed delay before forwarding the request.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private String fixedDelay;

  /**
   * The percentage of traffic on which delay will be injected. The value must be between [0, 100]
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.Integer percentage;

  /**
   * Specify a fixed delay before forwarding the request.
   * @return value or {@code null} for none
   */
  public String getFixedDelay() {
    return fixedDelay;
  }

  /**
   * Specify a fixed delay before forwarding the request.
   * @param fixedDelay fixedDelay or {@code null} for none
   */
  public GrpcRouteFaultInjectionPolicyDelay setFixedDelay(String fixedDelay) {
    this.fixedDelay = fixedDelay;
    return this;
  }

  /**
   * The percentage of traffic on which delay will be injected. The value must be between [0, 100]
   * @return value or {@code null} for none
   */
  public java.lang.Integer getPercentage() {
    return percentage;
  }

  /**
   * The percentage of traffic on which delay will be injected. The value must be between [0, 100]
   * @param percentage percentage or {@code null} for none
   */
  public GrpcRouteFaultInjectionPolicyDelay setPercentage(java.lang.Integer percentage) {
    this.percentage = percentage;
    return this;
  }

  @Override
  public GrpcRouteFaultInjectionPolicyDelay set(String fieldName, Object value) {
    return (GrpcRouteFaultInjectionPolicyDelay) super.set(fieldName, value);
  }

  @Override
  public GrpcRouteFaultInjectionPolicyDelay clone() {
    return (GrpcRouteFaultInjectionPolicyDelay) super.clone();
  }

}


text:
layout: post
title: Ceph集群部署笔记
categories: ops
---

准备用3台虚拟机搭建ceph集群,为了以后复习方便,这里做下笔记。

### 准备节点
3台主机,ceph1, ceph2, ceph3, 都为ubuntu系统,分别添加100G硬盘。

### 修改hosts文件
为每个节点,添加域名解析:
```
10.192.40.29 ceph1
10.192.40.30 ceph2
10.192.40.31 ceph3
```

### 创建ceph用户
```
useradd -m -d /home/ceph -k /etc/skel -s /bin/bash ceph
passwd ceph

echo "ceph ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ceph
chmod 0440 /etc/sudoers.d/ceph

```

### 添加ceph的apt源
```
wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add -

echo deb http://ceph.com/debian-firefly/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list # 注意$(lsb_release -sc)前面是有空格的哦

apt-get update
```

### ceph1节点安装ceph-deploy
```
apt-get install python-pip
pip install ceph-deploy
ceph-deploy --version
```

### ceph用户无密码ssh
```
ssh-keygen -q -t rsa

vim ~/.ssh/config

StrictHostKeyChecking no
Host ceph1
   Hostname ceph1
   User ceph
Host ceph2
   Hostname ceph2
   User ceph
Host ceph3
   Hostname ceph3
   User ceph

```
以上是在ceph1上配置公私钥。下面是使用ssh-copy-id将公私钥拷贝到其他节点。
```
ssh-copy-id ceph@ceph1
ssh-copy-id ceph@ceph2
ssh-copy-id ceph@ceph3
```

### 新建一个ceph cluster
```
ceph-deploy new ceph1 ceph2 ceph3
```

### 配置修改
ceph.conf:
```
[global]
fsid = a2345e92-706f-4b56-8f7a-3e3591b93d0c
mon_initial_members = ceph1, ceph2, ceph3
mon_host = 10.192.40.29,10.192.40.30,10.192.40.31
#auth_cluster_required = cephx
#auth_service_required = cephx
#auth_client_required = cephx
filestore_xattr_use_omap = true

# Add
auth_supported = none
# add public/cluster network
public_network = 10.192.0.0/16
cluster_network = 192.168.0.0/16
# special jornal size
osd_journal_size = 5120
# clock skew detected
mon_clock_drift_allowed = 1
# Set Default Replication
osd_pool_default_size = 2
```

### 安装3个节点
```
ceph-deploy install --release firefly ceph1
ceph-deploy install --release firefly ceph2
ceph-deploy install --release firefly ceph3
```

### 创建monitors
```
ceph-deploy mon create-initial
sudo ceph -s
```

### 添加osds
查看节点的磁盘情况:
```
ceph-deploy disk list ceph1
```
擦除磁盘:
```
ceph-deploy disk zap ceph1:vdb
```
创建osd
```
ceph-deploy osd create ceph1:vdb # 等价于先prepare再active
```

### 添加mds
```
ceph-deploy mds create ceph1
```

### 创建存储池
```
ceph osd pool create volumes 150
ceph osd pool create images 150
```

### 设置存储池副本数
```
ceph osd pool set volumes size 2
ceph osd pool set images size 2

# 查看存储池
rados lspools
```

### 安装rgw(hammer版)
如果安装的是hammer版的ceph, 那么在ceph-admin节点上就会有文件ceph.bootstrap-rgw.keyring,
也就可以使用如下的命令,快速的部署RGW实例。但需要注意的是,这里的实例使用的是内嵌civetweb的方式发布的。
```
ceph-deploy rgw create ceph-node1
```

### 安装rgw(firefly版)
```
sudo apt-get install apache2
sudo a2enmod proxy_fcgi
sudo service apache2 restart
sudo apt-get install openssl ssl-cert
sudo a2enmod ssl
sudo service apache2 restart
sudo mkdir /etc/apache2/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt
sudo service apache2 restart

sudo apt-get install radosgw
sudo apt-get install radosgw-agent

sudo ceph-authtool --create-keyring /etc/ceph/ceph.client.radosgw.keyring
sudo chmod +r /etc/ceph/ceph.client.radosgw.keyring
sudo ceph-authtool /etc/ceph/ceph.client.radosgw.keyring -n client.radosgw.gateway --gen-key
sudo ceph-authtool -n client.radosgw.gateway --cap osd 'allow rwx' --cap mon 'allow rwx' /etc/ceph/ceph.client.radosgw.keyring
sudo ceph -k /etc/ceph/ceph.client.admin.keyring auth add client.radosgw.gateway -i /etc/ceph/ceph.client.radosgw.keyring

vim /etc/ceph/ceph.conf
[client.radosgw.gateway]
host = ceph1
keyring = /etc/ceph/ceph.client.radosgw.keyring
rgw socket path = ""
log file = /var/log/radosgw/client.radosgw.gateway.log
rgw frontends = fastcgi socket_port=9000 socket_host=0.0.0.0
rgw print continue = false

ceph-deploy --overwrite-conf config pull ceph1
ceph-deploy --overwrite-conf config push ceph1 ceph2 ceph3

sudo mkdir -p /var/lib/ceph/radosgw/ceph-radosgw.gateway

sudo /etc/init.d/radosgw start

sudo vi /etc/apache2/conf-available/rgw.conf # ps: ln 到 conf-enabled
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html

ErrorLog /var/log/apache2/rgw_error.log
CustomLog /var/log/apache2/rgw_access.log combined

# LogLevel debug

RewriteEngine On

RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

SetEnv proxy-nokeepalive 1

ProxyPass / fcgi://localhost:9000/

</VirtualHost>

sudo service apache2 restart

```
以上已经安装好了rgw,  下面是创建用户:
```
man radosgw-admin
sudo radosgw-admin user create --uid="demouserid" --display-name="demo"
{ "user_id": "demouserid",
  "display_name": "demo",
  "email": "",
  "suspended": 0,
  "max_buckets": 1000,
  "auid": 0,
  "subusers": [
        { "id": "demouserid:swift",
          "permissions": "full-control"}],
  "keys": [
        { "user": "demouserid:swift",
          "access_key": "1ZGZ2FC4744YUP4DO15C",
          "secret_key": ""},
        { "user": "demouserid",
          "access_key": "Z2ETKC4RQFTR4XBQ1A72",
          "secret_key": "vqdQGtmruGW855mduffA8lsLx+ot9iXIb9QTtT2I"}],
  "swift_keys": [],
  "caps": [],
  "op_mask": "read, write, delete",
  "default_placement": "",
  "placement_tags": [],
  "bucket_quota": { "enabled": false,
      "max_size_kb": -1,
      "max_objects": -1},
  "user_quota": { "enabled": false,
      "max_size_kb": -1,
      "max_objects": -1},
  "temp_url_keys": []}

sudo radosgw-admin subuser create --uid=demouserid --subuser=demouserid:swift --access=full
{ "user_id": "demouserid",
  "display_name": "demo",
  "email": "",
  "suspended": 0,
  "max_buckets": 1000,
  "auid": 0,
  "subusers": [
        { "id": "demouserid:swift",
          "permissions": "full-control"}],
  "keys": [
        { "user": "demouserid:swift",
          "access_key": "1ZGZ2FC4744YUP4DO15C",
          "secret_key": ""},
        { "user": "demouserid",
          "access_key": "Z2ETKC4RQFTR4XBQ1A72",
          "secret_key": "vqdQGtmruGW855mduffA8lsLx+ot9iXIb9QTtT2I"}],
  "swift_keys": [
        { "user": "demouserid:swift",
          "secret_key": "etvOn7XyPlnYefo7dfC2LUIk+mgagdkHfhh\/cXzL"}],
  "caps": [],
  "op_mask": "read, write, delete",
  "default_placement": "",
  "placement_tags": [],
  "bucket_quota": { "enabled": false,
      "max_size_kb": -1,
      "max_objects": -1},
  "user_quota": { "enabled": false,
      "max_size_kb": -1,
      "max_objects": -1},
  "temp_url_keys": []}

```
下面是测试REST接口, 可以在任何一台可以方位rgw的节点上:
```
sudo apt-get install python-boto
import boto
import boto.s3.connection
access_key = 'I0PJDPCIYZ665MW88W9R'
secret_key = 'dxaXZ8U90SXydYzyS5ivamEP20hkLSUViiaR+ZDA'
conn = boto.connect_s3(
aws_access_key_id = access_key,
aws_secret_access_key = secret_key,
host = '{hostname}',
is_secure=False,
calling_format = boto.s3.connection.OrdinaryCallingFormat(),
)
bucket = conn.create_bucket('my-new-bucket')
for bucket in conn.get_all_buckets():
        print "{name}\t{created}".format(
                name = bucket.name,
                created = bucket.creation_date,
)
./s3test.py
my-new-bucket   2016-01-28T07:13:38.000Z
```
以上测试成功。

### 报错及解决
1. 问题1
```
[ceph3][DEBUG ] detect platform information from remote host
[ceph3][DEBUG ] detect machine type
[ceph3][DEBUG ] find the location of an executable
[ceph_deploy.install][ERROR ] Ceph is still installed on: ['ceph1']
[ceph_deploy][ERROR ] RuntimeError: refusing to purge data while Ceph is still installed
```
解决方案:
```
ceph-deploy uninstall ceph1 ceph2 ceph3

ceph-deploy purgedata ceph1 ceph2 ceph3
sudo rm -rf --one-file-system -- /var/lib/ceph
sudo rm -rf --one-file-system -- /etc/ceph/
```

2. 问题2
```
[ceph1][WARNIN] usermod: user ceph is currently used by process 6653

[ceph1][ERROR ] RuntimeError: command returned non-zero exit status: 100
[ceph_deploy][ERROR ] RuntimeError: Failed to execute command: env DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get --assume-yes -q --no-install-recommends install -o Dpkg::Options::=--force-confnew ceph ceph-mds radosgw
```
解决方案:
```
ceph-deploy install --release firelfy ceph1
重启系统?
```
虽然/etc/apt/source.list.d/ceph.list中的版本是firefly,但是运行ceph-deploy的时候会用最新版本的infernalis, 并且会更改/etc/apt/source.list.d/ceph.list的内容。。。

感谢IRC里Heebie的提示:
```
* leseb_ is now known as leseb_away

<Heebie> hr: Are you actually trying to install the "infernalis" version?  I'm guessing you're not..

it probably has a lot of dependencies that aren't met.  You probably want to add "--version hammer"  to your command line,

 but first you should do a "ceph-deploy purge ceph1" then logon to the node, do a "sudo dpkg --list | grep ceph" and it may list several packages such as ceph,

 ceph-common and some libraries and some ceph-related python thing... d
```

3. 问题3
```
 No RGW bootstrap key found. Will not be able to deploy RGW daemons
```
解决方案:
```
Note The bootstrap-rgw keyring is only created during installation of clusters running Hammer or newer
```

4. 问题4
```
    cluster 3230dd60-7be2-43e9-8ef7-45c9aee49eae
     health HEALTH_WARN 192 pgs stuck inactive; 192 pgs stuck unclean
```
解决方案:主要原因是新加的第二块网卡没有配置,配置完毕,在每个节点上执行以下命令:
```
sudo ifconfig eth1 192.168.6.5 netmask 255.255.255.0 broadcast 192.168.6.255 # 配置ip
sudo initctl list | grep ceph
sudo stop ceph-all
sudo start ceph-all
```

5. 问题5
```
boto.exception.S3ResponseError: S3ResponseError: 405 Method Not Allowed
```
解决方案:发现其实,apache还没有配置好。问题已在上面改正。

6. 问题6
```
AH00526: Syntax error on line 10 of /etc/apache2/conf-enabled/rgw.conf:
Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration
```
解决方案:在/etc/apache2/mods-available里找到rewrite.load, 然后在mods-enabled里做个符号链接。

7. 问题7
```
[ceph_deploy.mon][WARNIN] mon.ceph-node3 monitor is not yet in quorum, tries left: 5
[ceph_deploy.mon][WARNIN] waiting 5 seconds before retrying
[ceph-node3][INFO  ] Running command: sudo ceph --cluster=ceph --admin-daemon /var/run/ceph/ceph-mon.ceph-node3.asok mon_status
```
解决方案:私网网卡配置在重启虚拟机之后还原。
### 参考
[installation quick](http://docs.ceph.com/docs/master/start/)

[user is currently used by process](http://stackoverflow.com/questions/28972503/linux-usermod-user-is-currently-used-by-process)

[install ceph gateway](http://docs.ceph.com/docs/master/install/install-ceph-gateway/)

[simple config](http://docs.ceph.com/docs/master/radosgw/config/)


text:
Install npm (Node package manager) if you don't have it already by installing Nodejs here: https://nodejs.org/en/download/

Open up a terminal (search for it with Spotlight search on Mac). All of the following commands are run in the terminal.

Install [localturk](https://github.com/danvk/localturk) with: `npm install -g localturk`

Navigate to the directory at which you want to install my code. Then, `git clone https://github.com/aforsyth/regex_extraction.git` to clone my code into a directory called regex_extraction.

`cd` into regex_extraction, and then you can run my code as explained. below.

### Extraction Usage

Example:

`python extract_values.py "/path/to/input_file.txt" --extract_numerical_value True --phrases "EF"`

The example will extract the value 60 from notes including "EF is 60" or "EF: 60", for example. Note that phrase is case insensitive. When extracting numerical values following a phrase, anything like "[phrase] [num]", [phrase] is [num]", "[phrase] of [num]", "phrase: [num]" will be matched.

`input_filename`: a path to an RPDR-formatted EHR text file.

`output_filename`: path to the output CSV file, which has one row per record in the input file with the following columns: EMPI, MRN_Type, MRN, Report_Number, Report_Date_Time, Report_Description, Report_Type and regex result

`extract_numerical_value`: True if extracting a numerical value, False if only checking whether the notes contain a phrase (i.e. a boolean 0 or 1). Defaults to False.

`extract_date`: True if extracting a date. Note, both this and `extract_numerical_value` cannot be True at the same time. Matches dates of formats: "MM/DD/YY, MM/DD/YYYY, M/D/YY, MM-DD-YYYY, etc."

`phrases`: A list of comma separated phrases to check for either preceding the numerical value, or to check for their presence. The output will return numerical values following
any of the phrases in the supplied list. Example: "phrase1,phrase2,phrase3"

`report_description` (optional): If specified, only reports that exactly match the report_description value passed in will be examined.

`report_type` (optional): If specified, only reports that exactly match the report_type value passed in will be examined.

`turk_csv_filename` (optional): If specified, a CSV file will be written with
this filename that can be used to verify regex extraction with localturk.

`group_by_patient`: If specified, all patient notes for a single patient will be grouped together (i.e. there will be one output row per patient with a 1 if a phrase was present, else 0, or the first numerical value seen if extracting a numerical value). By default, this option enables the `context_size` option with a value of 10 because patient notes concatenated together would otherwise be too long to display in localturk.

`context_size`: Specified along with an integer, meaning that `context_size` number of words will be displayed before and after each regex match during localturk evaluation. This is useful 1) so that it's easier to identify matches, and 2) to reduce the total amount of text displayed, e.g. when a single note is too large to be loaded by localturk, such as when all notes for a single patient are concatenated when using the `group_by_patient` option. Around ~10 is a good starting value for this.

`num_negative_turk_matches_to_show`: By default, only positive matches are displayed in localturk for verification. Specify a number here to show up to that many negative matches, if needed, for example, to calculate false negatives in matching.

`ignore_punctuation`: If specified, punctuation characters will be ignored when finding a match. E.g. "full code confirmed" would also match "full code (confirmed)" and "full code -- confirmed". Note that this option will also ignore punctuation in the entered phrases themselves (e.g. "g-tube" will be considered the same as "gtube").

`show_n_words_context_before`: If this or `show_n_words_context_after` is specified and nonzero, the context in which the
desired phrase was found including up to n words before and n words after the actual phrase will be displayed in decreasing order of the frequency of that context string in the notes.

`show_n_words_context_after`: See above.

### Localturk usage

Install localturk from here: https://github.com/danvk/localturk

Run `localturk --static_dir . localturk/extract.html localturk/tasks.csv localturk/outputs.csv` where `extract.html` is the localturk html template included in this repository, and `turk_csv_filename.csv` is the csv output with turk `turk_csv_filename` option.

### Note Filtering Usage

`filter_notes.py` allows you to filter an RPDR note file to include only notes from patients of interest and only notes for those patients within a specified time range. This could be used, for example, to find notes for a patient that are within X days before and Y days after a certain procedure. Note that if you want notes within Z days after and Y days after the procedure, for example, between 30 days and 60 days after the procedure, days_before would be -Z or -30 days and days_after would be 60 days, meaning that all notes within -30 days before (i.e. 30 days after) and 60 days after the procedure would be included.

Running `python filter_notes.py rpdr_filename filter_csv_filename` will output a RPDR notes file of the same format as the origin, but filtered as described. It will write this new file to the same filename as the input file, but with "_filtered" added to the same before the file extension. Optionally, you can specify the output filename with `--output_filename`.

Note that filter_csv_filename should point to a file that looks like:

empi,procedure_date,days_before,days_after,include

1111,5/12/2016,30,0,1

1121,5/13/2016,10,10,0

Patients with a 0 for include will not be included in the output notes file.

### DFCI to RPDR Converter

`convert_dfci_to_rpdr.py` converts a DFCI epic clinical notes file to an lno RPDR formatted file, which can be used with the other regex_extraction tools.

Running `python convert_dfci_to_rpdr.py input_dfci_filename output_rpdr_filename` will convert the DFCI formatted file, `input_dfci_filename` to RPDR format and write the output file to `output_rpdr_filename`. Note that this converter will skip DFCI file entries with no date of service entered.


text:
//
//  CMAnimationTool.m
//  CMSingleSelectedView
//
//  Created by 陈华 on 16/11/6.
//  Copyright © 2016年 com.chmn123.singleSelectedView. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "CMAnimationTool.h"
#import "CMPresentationController.h"

@interface CMAnimationTool() <UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning>

/**是否正在显示*/
@property(nonatomic,assign,getter=isPresented) BOOL presented;

@end

@implementation CMAnimationTool

#pragma mark - UIViewControllerTransitioningDelegate

-(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
    CMPresentationController *vc = [[CMPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
    vc.presentedRect = self.presentedRect;
    return vc;
}

/**重写,用来设置显示动画*/
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    self.presented = YES;
    return self;
}

/**重写,用来设置消失动画*/
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    self.presented = NO;
    return self;
}

#pragma mark - UIViewControllerAnimatedTransitioning

/**设置动画事件*/
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return self.animationDuration;
}

/**设置动画*/
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    self.isPresented ? [self setupPresentedAnimation:transitionContext] : [self setupDismissedAnimation:transitionContext];
}

#pragma mark - 封装具体的动画
/**设置显示动画的具体实现过程*/
-(void)setupPresentedAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //1 获取当前正要显示的presentedView
    UIView *presentedView = [transitionContext viewForKey:UITransitionContextToViewKey];

    //2 将presentedView添加到containerView上
    [[transitionContext containerView] addSubview:presentedView];

    //3 设置动画
    presentedView.transform = CGAffineTransformMakeTranslation(0, [self.delegate heightForSingleSelectedView]);
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

        presentedView.transform = CGAffineTransformIdentity;

    } completion:^(BOOL finished) {
        //告诉上下文,动画结束
        [transitionContext completeTransition:YES];
    }];

}

/**设置消失动画的具体实现过程*/
-(void)setupDismissedAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //1 获取当前正要显示的presentedView
    UIView *dismissedView = [transitionContext viewForKey:UITransitionContextFromViewKey];

    //2 将dismissedView添加到containerView上
    [[transitionContext containerView] addSubview:dismissedView];

    //3 设置动画
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

        dismissedView.transform = CGAffineTransformMakeTranslation(0, [self.delegate heightForSingleSelectedView]);

    } completion:^(BOOL finished) {

        //移除dismissedView
        [dismissedView removeFromSuperview];
        //告诉上下文,动画结束
        [transitionContext completeTransition:YES];

    }];
}

@end


text:
#include "config.h"
#include "version.h"
/*
   The DOS Allegro display code.
 */

/* Standard Includes */
#include <stdio.h>
#include <stdlib.h>
#include <allegro.h>

/* My headers */
#include "types.h"
#include "vmachine.h"
#include "address.h"
#include "files.h"
#include "colours.h"
#include "keyboard.h"
#include "limiter.h"
#include "options.h"

struct AlColor
  {
    int red, green, blue;
  };

#define NUMCOLS 128
PALLETE colors;
PALLETE oldcol;

/* Various variables and short functions */

/* Start of image data */
BYTE *vscreen;
BITMAP *bmp;

/* The width and height of the image, including any magnification */
int vwidth, vheight, theight;

/* The frame rate counter. Used by the -rr switch */
int tv_counter = 0;

/* Optionally set by the X debugger. */
int redraw_flag = 1;

static int bytesize;

/* Inline helper to place the tv image */
static inline void
put_image (void)
{
  blit(bmp,screen,0,0,0,0,320,200);
}

/* Create the color map of Atari colors */
/* VGA colors are only 6 bits wide */
static void
create_cmap (void)
{
  int i;

  /* Initialise parts of the colors array */
  for (i = 0; i < NUMCOLS; i++)
    {
      /* Use the color values from the color table */
      colors[i].r = (colortable[i * 2] & 0xff0000) >> 18;
      colors[i].g = (colortable[i * 2] & 0x00ff00) >> 10;
      colors[i].b = (colortable[i * 2] & 0x0000ff) >> 2;
    }
}

/* Create the main window */
/* argc: argument count */
/* argv: argument text */
static void
create_window (int argc, char **argv)
{
  int i;

  /* Calculate the video width and height */
  vwidth = base_opts.magstep * tv_width * 2;
  theight = tv_height + 8;
  vheight = base_opts.magstep * theight;

  /* Turn on the keyboard. Must be done after toplevel is created */
  init_keyboard ();

  /* Save the current color map */
  get_palette_range(oldcol,0,NUMCOLS);

  /* Create the color map */
  create_cmap ();

  /* Use the color map */
  set_palette_range(colors,0,NUMCOLS,0);
}

/* Turns on the television. */
/* argc: argument count */
/* argv: argument text */
/* returns: 1 for success, 0 for failure */
int
tv_on (int argc, char **argv)
{

  /* Set up screen mode */
  allegro_init ();
  set_gfx_mode(GFX_VGA,320,200,0,0);

  bytesize = tv_height * 320;
  if (bytesize > 64000)
    bytesize = 64000;

  /* Create the windows */
  create_window (argc, argv);

  bmp=create_bitmap(vwidth,vheight);

  if (Verbose)
    printf ("OK\n  Allocating screen buffer...");

  vscreen = (BYTE *) (bmp->dat);

  if (!vscreen)
    {
      if (Verbose)
	printf ("Memory Allocation FAILED\n");
      return (0);
    }

  if (Verbose)
    printf ("OK\n");
  return (1);
}

/* Turn off the tv. Closes the X connection and frees the shared memory */
void
tv_off (void)
{
  int i;
  if (Verbose)
    printf ("Switching off...\n");

  /* Restore the color map */
  set_palette_range(oldcol,0,NUMCOLS,0);

  /* Go Text */
  set_gfx_mode(GFX_TEXT,80,25,0,0);
  allegro_exit();
}

/* Translates a 2600 color value into an X11 pixel value */
/* b: byte containing 2600 colour value */
/* returns: X11 pixel value */
BYTE
tv_color (BYTE b)
{
  return (b >> 1);
}

/* Displays the tv screen */
void
tv_display (void)
{
  /* Only display if the frame is a valid one. */
  if (tv_counter % base_opts.rr == 0)
    {
      put_image ();
    }
  tv_counter++;
}

/* The Event code. */
void
tv_event (void)
{
  read_keyboard ();
}

/* Single pixel plotting function. Used for debugging,  */
/* not in production code  */
/* x: horizontal position */
/* y: vertical position */
/* value: pixel value */
void
tv_putpixel (int x, int y, BYTE value)
{
  BYTE *p;

  switch (base_opts.magstep)
    {
    case 1:
      x = x << 1;
      p = vscreen + x + y * vwidth;
      *(p) = value;
      *(p + 1) = value;
    }
}


text:
{-# LANGUAGE PatternSynonyms #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  Graphics.GL.INGR.ColorClamp
-- Copyright   :  (c) Sven Panne 2019
-- License     :  BSD3
--
-- Maintainer  :  Sven Panne <svenpanne@gmail.com>
-- Stability   :  stable
-- Portability :  portable
--
--------------------------------------------------------------------------------

module Graphics.GL.INGR.ColorClamp (
  -- * Extension Support
  glGetINGRColorClamp,
  gl_INGR_color_clamp,
  -- * Enums
  pattern GL_ALPHA_MAX_CLAMP_INGR,
  pattern GL_ALPHA_MIN_CLAMP_INGR,
  pattern GL_BLUE_MAX_CLAMP_INGR,
  pattern GL_BLUE_MIN_CLAMP_INGR,
  pattern GL_GREEN_MAX_CLAMP_INGR,
  pattern GL_GREEN_MIN_CLAMP_INGR,
  pattern GL_RED_MAX_CLAMP_INGR,
  pattern GL_RED_MIN_CLAMP_INGR
) where

import Graphics.GL.ExtensionPredicates
import Graphics.GL.Tokens


text:
package test.integration.bind;

import org.junit.Test;
import se.jbee.inject.Injector;
import se.jbee.inject.Scope;
import se.jbee.inject.binder.BinderModule;
import se.jbee.inject.bootstrap.Bootstrap;

import static org.junit.Assert.assertEquals;

public class TestParentTargetBinds {

	private static class ParentTargetBindsModule extends BinderModule {

		@Override
		protected void declare() {
			bind(String.class).to("everywhere");
			per(Scope.targetInstance).construct(Child.class);
			injectingInto(Child.class).bind(String.class).to("child");
			per(Scope.targetInstance).construct(Parent.class);
			injectingInto(Child.class).within(Parent.class) //
					.bind(String.class).to("child-with-parent");
			construct(Grandparent.class);
			injectingInto(Child.class).within(Parent.class).within(
					Grandparent.class) //
					.bind(String.class).to("child-with-grandparent");
		}
	}

	public static class Grandparent {

		final Parent parent;

		public Grandparent(Parent parent) {
			this.parent = parent;
		}

	}

	public static class Parent {

		final Child child;

		public Parent(Child child) {
			this.child = child;
		}

	}

	public static class Child {

		final String value;

		public Child(String value) {
			this.value = value;
		}

	}

	private final Injector injector = Bootstrap.injector(
			ParentTargetBindsModule.class);

	@Test
	public void thatBindWithParentTargetIsUsedWhenInjectionHasParent() {
		assertEquals("child-with-grandparent",
				injector.resolve(Grandparent.class).parent.child.value);
		assertEquals("child-with-parent",
				injector.resolve(Parent.class).child.value);
		assertEquals("child", injector.resolve(Child.class).value);
	}

	/**
	 * counter-check to see that the more precise bind does not apply here
	 */
	@Test
	public void thatBindWithParentTargetIsNotUsedWhenInjectionHasNoParent() {
		assertEquals("child", injector.resolve(Child.class).value);
		assertEquals("everywhere", injector.resolve(String.class));
	}

}