text:
<?xml version="1.0" encoding="utf-8"?><!--
  ~ Created by ttdevs at 16-8-19 上午11:16.
  ~ E-mail:ttdevs@gmail.com
  ~ https://github.com/ttdevs
  ~ Copyright (c) 2016 ttdevs
  -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:background="#FFF8F8F8"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="125dp"
        android:background="@drawable/bg_coupon_header"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginTop="18dp"
            android:orientation="horizontal">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:orientation="horizontal"
                android:paddingBottom="4dp"
                android:paddingLeft="16dp"
                android:paddingRight="16dp"
                android:paddingTop="4dp">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="¥"
                    android:textColor="#FFFF8800"
                    android:textSize="12sp" />

                <TextView
                    android:id="@+id/tv_money"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:textColor="#FFFF8800"
                    android:textSize="40sp"
                    tools:text="100" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="16dp"
                android:layout_weight="2"
                android:gravity="center_vertical"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tv_limit_production"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#FFFF8800"
                    android:textSize="16sp"
                    tools:text="超模25营养饮专用" />

                <TextView
                    android:id="@+id/tv_valid"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:textColor="#FF999999"
                    android:textSize="12sp"
                    tools:text="有效期2016.8.1~2016.8.31" />
            </LinearLayout>
        </LinearLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginTop="8dp">

            <TextView
                android:id="@+id/tv_limit_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:textSize="12sp"
                tools:text="满299使用" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_toLeftOf="@+id/view_status"
                android:text="使用规则"
                android:textSize="12sp" />

            <CheckBox
                android:id="@+id/view_status"
                android:layout_width="8dp"
                android:layout_height="8dp"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginLeft="4dp"
                android:background="@drawable/selector_coupon_check"
                android:button="@null"
                android:checked="false" />
        </RelativeLayout>
    </RelativeLayout>

    <TextView
        android:id="@+id/tv_description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_coupon_body"
        android:padding="16dp"
        android:text="1、你好 \n2、我是你爹 \n3、爱用不用 \n4、滚"
        android:textColor="#FF999999"
        android:textSize="12sp"
        android:visibility="gone" />

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#FFFF8800" />
</LinearLayout>


text:
<?php

$tree = new Ui\Tree();
$tree->itemBodyFn = function($category) {
    return Html::el('a', ['href' => $category->getHref()], $category->name());
};
$tree->itemActiveCheck = function($curCategory) use($category) {
    return $curCategory->id == $category->id || strpos($curCategory->tree_path, "/{$category->id}/") !== false;
};
$tree->draw($category->getRoot());

redpajama_set_name: RedPajamaGithub


text:
@interface MLRegisteredController ()<UITextFieldDelegate>
@property (nonatomic ,weak)UITextField *textField;
@property (nonatomic ,weak)UIButton *btn;
@end

@implementation MLRegisteredController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    //初始化
    [self chushihua];
}
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    //显示uinavigationcontroller
    [self.navigationController setNavigationBarHidden:NO animated:NO];
    self.navigationItem.title = @"注册";
    //设置右上角的下一步按钮
    UIBarButtonItem *youBtnBarItem = [[UIBarButtonItem alloc] initWithTitle:@"下一步" style:UIBarButtonItemStyleDone target:self action:@selector(dianjixiayibu)];
    self.navigationItem.rightBarButtonItem = youBtnBarItem;
}
#pragma mark - 初始化
-(void)chushihua{
    self.view.userInteractionEnabled = YES;
    //创建背景图片
//    UIImageView *bg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//    bg.userInteractionEnabled = YES;
//    bg.image = [UIImage imageNamed:@"3注册(2)"];
    //添加单击退出键盘
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
    tap.numberOfTapsRequired = 1;
    [tap addTarget:self action:@selector(danji)];
    [self.view addGestureRecognizer:tap];
//    [self.view addSubview:bg];
    //创建医生和营养师选择对话框
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20 , 20 + 64, self.view.frame.size.width - 40, 40)];
    imageView.userInteractionEnabled = YES;
    imageView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
    //圆角
    imageView.layer.cornerRadius = 3;
    imageView.layer.masksToBounds = YES;
    [self.view addSubview:imageView];
//    //医生和营养师选择
//    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(10, 0, imageView.frame.size.width - 20, 40)];
//    [btn setTitle:@"医生" forState:UIControlStateNormal];
//    [btn addTarget:self action:@selector(dianjixuanzhe) forControlEvents:UIControlEventTouchUpInside];
//    btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//    [imageView addSubview:btn];
//    self.btn = btn;
//    //创建箭头
//    UIImageView *jiantouImage = [[UIImageView alloc] initWithFrame:CGRectMake(45, 16, 10, 7)];
//    jiantouImage.userInteractionEnabled = YES;
//    jiantouImage.image = [UIImage imageNamed:@"btn-登录下拉"];
//    [btn addSubview:jiantouImage];
//    //线
//    UIView *xian = [[UIView alloc] initWithFrame:CGRectMake(0, 41, imageView.frame.size.width, 1)];
//    xian.backgroundColor = [UIColor whiteColor];
//    [imageView addSubview:xian];
    //创建+86
    UILabel * label86 = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 40, 40)];
    label86.textColor = [UIColor colorWithRed:3/255.0 green:166/255.0 blue:116/255.0 alpha:1];
    label86.text = @"+86";
    label86.font = [UIFont systemFontOfSize:19];
    [imageView addSubview:label86];
    //竖线
    UIView *xian1 = [[UIView alloc] initWithFrame:CGRectMake(label86.frame.origin.x + label86.frame.size.width + 5, label86.frame.origin.y, 1, label86.frame.size.width)];
    xian1.backgroundColor = [UIColor whiteColor];
    [imageView addSubview:xian1];
    //创建输入手机号码
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(xian1.frame.origin.x + 11, xian1.frame.origin.y, imageView.frame.size.width - xian1.frame.origin.x - 20, 40)];
    textField.textColor = [UIColor colorWithRed:3/255.0 green:166/255.0 blue:116/255.0 alpha:1];
    //设置提示文字
    NSMutableDictionary *attrs1 = [NSMutableDictionary dictionary];
//    attrs1[NSForegroundColorAttributeName] = [UIColor colorWithRed:100/255.0 green:100/255.0 blue:100/255.0 alpha:1];
    textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"输入您的手机号码" attributes:attrs1];
    textField.font = [UIFont systemFontOfSize:19];
    textField.tintColor = [UIColor colorWithRed:3/255.0 green:166/255.0 blue:116/255.0 alpha:1];
    //设置清除按钮
    textField.clearButtonMode = UITextFieldViewModeAlways;
    //弹出数字键盘
    textField.keyboardType = UIKeyboardTypeNumberPad;
    [imageView addSubview:textField];
    textField.delegate = self;
    self.textField = textField;
}
#pragma mark - 点击下一步
-(void)dianjixiayibu{
    //跳到验证码界面
    if (self.textField.text.length != 11) {
        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:@"请输入正确的手机号码" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
        [alert show];
        return ;
    }
    [MBProgressHUD showMessage:@"正在发送" toView:self.view];
    //发送网络请求
    //发送验证码
    [SMSSDK getVerificationCodeByMethod:SMSGetCodeMethodSMS phoneNumber:self.textField.text zone:@"86" customIdentifier:nil result:^(NSError *error) {
        if (error == nil) {
            //发送成功,做跳转
            [MBProgressHUD hideHUDForView:self.view];
            MLVerificationCodeController *vc = [[MLVerificationCodeController alloc] init];
            vc.shouji = self.textField.text;
            [self.navigationController pushViewController:vc animated:YES];
            self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleDone target:nil action:nil];
        }else{
            NSLog(@"%@",error);
            //发送失败,给提示
            [MBProgressHUD hideHUDForView:self.view];
            [MBProgressHUD showError:error.userInfo[@"getVerificationCode"] toView:self.view];

#warning 需要删除的代码
            MLVerificationCodeController *vc = [[MLVerificationCodeController alloc] init];
            vc.shouji = self.textField.text;
            [self.navigationController pushViewController:vc animated:YES];
            self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleDone target:nil action:nil];
        }
    }];
}
#pragma mark - 限制手机号码只有11位
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    if ([string isEqualToString:@"\n"]) {
        return YES;
    }
    NSString *toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    if (self.textField == textField) {
        if ([toBeString length] > 11) {
            textField.text = [toBeString substringToIndex:11];
            UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:@"请输入正确的手机号码" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
            [alert show];
            return NO;
        }
    }
    return YES;
}
#pragma mark - 点击医生营养师选择
-(void)dianjixuanzhe{
    NSLog(@"点击选择");
}

#pragma mark - 单击退出键盘
-(void)danji{
    [self.view endEditing:YES];
}
@end


text:
using System;
using System.Collections.Generic;
using System.Linq;
using Google;
using Google.Apis.Admin.Directory.directory_v1.Data;
using Lithnet.GoogleApps.ManagedObjects;
using Lithnet.Logging;
using Lithnet.MetadirectoryServices;
using Microsoft.MetadirectoryServices;

namespace Lithnet.GoogleApps.MA
{
    internal class ApiInterfaceGroupMembership : IApiInterface
    {
        private IManagementAgentParameters config;

        public string Api => "groupmembership";

        public ApiInterfaceGroupMembership(IManagementAgentParameters config)
        {
            this.config = config;
        }

        public void ApplyChanges(CSEntryChange csentry, CSEntryChange committedChanges, SchemaType type, ref object target, bool patch = false)
        {
            this.GetMemberChangesFromCSEntryChange(csentry, out GroupMembership membershipToAdd, out GroupMembership membershipToDelete, out GroupMembership reportedAdds, out GroupMembership reportedDeletes, out IList<Member> roleChanges, csentry.ObjectModificationType == ObjectModificationType.Replace);

            HashSet<string> allMembersToDelete = membershipToDelete.GetAllMembers();
            List<Member> allMembersToAdd = membershipToAdd.ToMemberList();

            AttributeModificationType modificationType = csentry.ObjectModificationType == ObjectModificationType.Update ? AttributeModificationType.Update : AttributeModificationType.Add;
            try
            {
                if (csentry.ObjectModificationType != ObjectModificationType.Add && allMembersToDelete.Count > 0)
                {
                    try
                    {
                        this.config.GroupsService.MemberFactory.RemoveMembers(csentry.DN, allMembersToDelete.ToList(), false);

                        foreach (string member in allMembersToDelete)
                        {
                            Logger.WriteLine($"Deleted member {member} from group {csentry.DN}", LogLevel.Debug);
                        }

                        if (allMembersToDelete.Count == 1)
                        {
                            Logger.WriteLine($"Deleted {allMembersToDelete.Count} member from group {csentry.DN}");
                        }
                        else
                        {
                            Logger.WriteLine($"Deleted {allMembersToDelete.Count} members from group {csentry.DN}");
                        }
                    }
                    catch (AggregateGroupUpdateException ex)
                    {
                        Logger.WriteLine("The following member removals failed");
                        foreach (Exception e in ex.Exceptions)
                        {
                            Logger.WriteException(e);
                        }

                        reportedDeletes.RemoveMembers(ex.FailedMembers);
                        throw;
                    }
                }

                if (csentry.ObjectModificationType != ObjectModificationType.Add && roleChanges?.Count > 0)
                {
                    try
                    {
                        foreach (Member change in roleChanges)
                        {
                            try
                            {
                                this.config.GroupsService.MemberFactory.ChangeMemberRole(csentry.DN, change);
                                Logger.WriteLine($"Changed member role {change.Email} to {change.Role}", LogLevel.Debug);
                            }
                            catch (GoogleApiException ex)
                            {
                                if (ex.HttpStatusCode == System.Net.HttpStatusCode.NotFound)
                                {
                                    throw new UnexpectedDataException($"Member {change.Email} cannot be assigned {change.Role} without being made a member", ex);
                                }

                                throw;
                            }
                        }
                    }
                    catch (AggregateGroupUpdateException ex)
                    {
                        Logger.WriteLine("The following member role changes failed");

                        foreach (Exception e in ex.Exceptions)
                        {
                            Logger.WriteException(e);
                        }

                        throw;
                    }
                }

                if (allMembersToAdd.Count > 0)
                {
                    try
                    {
                        this.config.GroupsService.MemberFactory.AddMembers(csentry.DN, this.NormalizeMembershipList(allMembersToAdd), false);

                        foreach (Member member in allMembersToAdd)
                        {
                            Logger.WriteLine($"Added {member.Role} {member.Email}", LogLevel.Debug);
                        }

                        if (allMembersToAdd.Count == 1)
                        {
                            Logger.WriteLine($"Added {allMembersToAdd.Count} member");
                        }
                        else
                        {
                            Logger.WriteLine($"Added {allMembersToAdd.Count} members");
                        }
                    }
                    catch (AggregateGroupUpdateException ex)
                    {
                        Logger.WriteLine("The following member additions failed");
                        foreach (Exception e in ex.Exceptions)
                        {
                            Logger.WriteException(e);
                        }

                        reportedAdds.RemoveMembers(ex.FailedMembers);
                        throw;
                    }
                }
            }
            finally
            {
                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupMemberAttributeName, modificationType, reportedDeletes.Members.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalMember", modificationType, reportedDeletes.ExternalMembers.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupManagerAttributeName, modificationType, reportedDeletes.Managers.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalManager", modificationType, reportedDeletes.ExternalManagers.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupOwnerAttributeName, modificationType, reportedDeletes.Owners.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalOwner", modificationType, reportedDeletes.ExternalOwners.ToValueChange(ValueModificationType.Delete), committedChanges.AttributeChanges);

                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupMemberAttributeName, modificationType, reportedAdds.Members.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalMember", modificationType, reportedAdds.ExternalMembers.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupManagerAttributeName, modificationType, reportedAdds.Managers.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalManager", modificationType, reportedAdds.ExternalManagers.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange(this.config.GroupOwnerAttributeName, modificationType, reportedAdds.Owners.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
                ApiInterfaceGroupMembership.AddAttributeChange("externalOwner", modificationType, reportedAdds.ExternalOwners.ToValueChange(ValueModificationType.Add), committedChanges.AttributeChanges);
            }
        }

        public IEnumerable<AttributeChange> GetChanges(string dn, ObjectModificationType modType, SchemaType type, object source)
        {
            if (!(source is GroupMembership membership))
            {
                if (!(source is GoogleGroup @group))
                {
                    throw new InvalidOperationException();
                }
                else
                {
                    membership = group.Membership;
                }
            }

            this.ApplyRoleInheritance(membership);

            foreach (IAttributeAdapter typeDef in ManagementAgent.Schema[SchemaConstants.Group].AttributeAdapters.Where(t => t.Api == this.Api))
            {
                foreach (string attributeName in typeDef.MmsAttributeNames)
                {
                    if (type.HasAttribute(attributeName))
                    {
                        foreach (AttributeChange change in typeDef.CreateAttributeChanges(dn, modType, membership))
                        {
                            yield return change;
                        }
                    }
                }
            }
        }

        private List<Member> NormalizeMembershipList(List<Member> members)
        {
            if (!this.config.InheritGroupRoles)
            {
                return members;
            }

            Dictionary<string, Member> normalizedList = new Dictionary<string, Member>(StringComparer.CurrentCultureIgnoreCase);

            foreach (Member member in members)
            {
                if (normalizedList.ContainsKey(member.Email))
                {
                    Member existingMember = normalizedList[member.Email];

                    if (existingMember.Role == "OWNER")
                    {
                        continue;
                    }

                    if (existingMember.Role == "MANAGER")
                    {
                        if (member.Role == "OWNER")
                        {
                            normalizedList[member.Email] = member;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        normalizedList[member.Email] = member;
                    }
                }
                else
                {
                    normalizedList.Add(member.Email, member);
                }
            }

            return normalizedList.Select(t => t.Value).ToList();
        }

        private static void AddAttributeChange(string attributeName, AttributeModificationType modificationType, IList<ValueChange> changes, IList<AttributeChange> attributeChanges)
        {
            AttributeChange existingChange = attributeChanges.FirstOrDefault(t => t.Name == attributeName);

            if (modificationType == AttributeModificationType.Delete)
            {
                if (existingChange != null)
                {
                    attributeChanges.Remove(existingChange);
                }

                attributeChanges.Add(AttributeChange.CreateAttributeDelete(attributeName));
                return;
            }

            if (changes == null || changes.Count == 0)
            {
                return;
            }

            IList<object> adds;
            switch (modificationType)
            {
                case AttributeModificationType.Add:
                    if (existingChange != null)
                    {
                        foreach (ValueChange valueChange in changes.Where(t => t.ModificationType == ValueModificationType.Add))
                        {
                            existingChange.ValueChanges.Add(valueChange);
                        }
                    }
                    else
                    {
                        adds = changes.Where(t => t.ModificationType == ValueModificationType.Add).Select(t => t.Value).ToList();

                        if (adds.Count > 0)
                        {
                            attributeChanges.Add(AttributeChange.CreateAttributeAdd(attributeName, adds));
                        }
                    }
                    break;

                case AttributeModificationType.Replace:
                    if (existingChange != null)
                    {
                        attributeChanges.Remove(existingChange);
                    }

                    adds = changes.Where(t => t.ModificationType == ValueModificationType.Add).Select(t => t.Value).ToList();
                    if (adds.Count > 0)
                    {
                        attributeChanges.Add(AttributeChange.CreateAttributeReplace(attributeName, adds));
                    }

                    break;

                case AttributeModificationType.Update:
                    if (existingChange != null)
                    {
                        if (existingChange.ModificationType != AttributeModificationType.Update)
                        {
                            throw new InvalidOperationException();
                        }

                        foreach (ValueChange valueChange in changes)
                        {
                            existingChange.ValueChanges.Add(valueChange);
                        }
                    }
                    else
                    {
                        if (changes.Count > 0)
                        {
                            attributeChanges.Add(AttributeChange.CreateAttributeUpdate(attributeName, changes));
                        }
                    }

                    break;

                case AttributeModificationType.Delete:
                case AttributeModificationType.Unconfigured:
                default:
                    throw new InvalidOperationException();
            }
        }

        private void ApplyRoleInheritance(GroupMembership membership)
        {
            if (this.config.InheritGroupRoles && membership != null)
            {
                foreach (string value in membership.Owners)
                {
                    membership.Managers.Add(value);
                }

                foreach (string value in membership.ExternalOwners)
                {
                    membership.ExternalManagers.Add(value);
                }

                foreach (string value in membership.Managers)
                {
                    membership.Members.Add(value);
                }

                foreach (string value in membership.ExternalManagers)
                {
                    membership.ExternalMembers.Add(value);
                }
            }
        }

        private void GetMemberChangesFromCSEntryChange(CSEntryChange csentry, out GroupMembership adds, out GroupMembership deletes, out GroupMembership reportedAdds, out GroupMembership reportedDeletes, out IList<Member> roleChanges, bool replacing)
        {
            adds = new GroupMembership();
            deletes = new GroupMembership();
            reportedAdds = new GroupMembership();
            reportedDeletes = new GroupMembership();
            roleChanges = new List<Member>();

            GroupMembership existingGroupMembership;
            bool hasExistingMembership = false;

            if (replacing | this.ExistingMembershipRequiredForUpdate(csentry))
            {
                existingGroupMembership = this.config.GroupsService.MemberFactory.GetMembership(csentry.DN);
                hasExistingMembership = true;
            }
            else
            {
                existingGroupMembership = new GroupMembership();
            }

            this.GetMemberChangesFromCSEntryChange(csentry, adds.Members, deletes.Members, existingGroupMembership.Members, this.config.GroupMemberAttributeName, replacing);
            this.GetMemberChangesFromCSEntryChange(csentry, adds.ExternalMembers, deletes.ExternalMembers, existingGroupMembership.ExternalMembers, "externalMember", replacing);
            this.GetMemberChangesFromCSEntryChange(csentry, adds.Managers, deletes.Managers, existingGroupMembership.Managers, this.config.GroupManagerAttributeName, replacing);
            this.GetMemberChangesFromCSEntryChange(csentry, adds.ExternalManagers, deletes.ExternalManagers, existingGroupMembership.ExternalManagers, "externalManager", replacing);
            this.GetMemberChangesFromCSEntryChange(csentry, adds.Owners, deletes.Owners, existingGroupMembership.Owners, this.config.GroupOwnerAttributeName, replacing);
            this.GetMemberChangesFromCSEntryChange(csentry, adds.ExternalOwners, deletes.ExternalOwners, existingGroupMembership.ExternalOwners, "externalOwner", replacing);

            if (hasExistingMembership && this.config.InheritGroupRoles)
            {
                this.ApplyRoleChanges(adds, deletes, out reportedAdds, out reportedDeletes, out roleChanges);
            }

            reportedAdds.MergeMembership(adds);
            reportedDeletes.MergeMembership(deletes);
        }

        private void ApplyRoleChanges(GroupMembership adds, GroupMembership deletes, out GroupMembership reportedAdds, out GroupMembership reportedDeletes, out IList<Member> roleChanges)
        {
            roleChanges = new List<Member>();
            reportedAdds = new GroupMembership();
            reportedDeletes = new GroupMembership();

            // Downgrades to member
            foreach (var member in deletes.Managers.Except(deletes.Members).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MEMBER" });
                deletes.Managers.Remove(member);
                reportedDeletes.Managers.Add(member);
            }

            foreach (var member in deletes.ExternalManagers.Except(deletes.ExternalMembers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MEMBER" });
                deletes.ExternalManagers.Remove(member);
                reportedDeletes.ExternalManagers.Add(member);
            }

            // Downgrades to managers
            foreach (var member in deletes.Owners.Except(deletes.Managers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MANAGER" });
                deletes.Owners.Remove(member);
                reportedDeletes.Owners.Add(member);
            }

            foreach (var member in deletes.ExternalOwners.Except(deletes.ExternalManagers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MANAGER" });
                deletes.ExternalOwners.Remove(member);
                reportedDeletes.ExternalOwners.Add(member);
            }

            // Upgrades to manager
            foreach (var member in adds.Managers.Except(adds.Members).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MANAGER" });
                adds.Managers.Remove(member);
                reportedAdds.Managers.Add(member);
            }

            foreach (var member in adds.ExternalManagers.Except(adds.ExternalMembers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "MANAGER" });
                adds.ExternalManagers.Remove(member);
                reportedAdds.ExternalManagers.Add(member);
            }

            // Upgrades to owner
            foreach (var member in adds.Owners.Except(adds.Managers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "OWNER" });
                adds.Owners.Remove(member);
                reportedAdds.Owners.Add(member);
            }

            foreach (var member in adds.ExternalOwners.Except(adds.ExternalManagers).ToList())
            {
                roleChanges.Add(new Member() { Email = member, Role = "OWNER" });
                adds.ExternalOwners.Remove(member);
                reportedAdds.ExternalOwners.Add(member);
            }
        }

        private void GetMemberChangesFromCSEntryChange(CSEntryChange csentry, HashSet<string> adds, HashSet<string> deletes, HashSet<string> existingMembers, string attributeName, bool replacing)
        {
            if (replacing)
            {
                foreach (string address in csentry.GetValueAdds<string>(attributeName))
                {
                    adds.Add(address);
                }

                foreach (string address in deletes.Except(adds))
                {
                    deletes.Add(address);
                }

                return;
            }
            else
            {
                AttributeChange change = csentry.AttributeChanges.FirstOrDefault(t => t.Name == attributeName);

                if (change == null)
                {
                    return;
                }

                switch (change.ModificationType)
                {
                    case AttributeModificationType.Add:
                        foreach (string address in csentry.GetValueAdds<string>(attributeName))
                        {
                            this.ValidateAddress(address, attributeName);
                            adds.Add(address);
                        }
                        break;

                    case AttributeModificationType.Delete:
                        foreach (string member in existingMembers)
                        {
                            this.ValidateAddress(member, attributeName);
                            deletes.Add(member);
                        }

                        break;

                    case AttributeModificationType.Replace:
                        IList<string> newMembers = csentry.GetValueAdds<string>(attributeName);
                        foreach (string address in newMembers)
                        {
                            this.ValidateAddress(address, attributeName);
                            adds.Add(address);
                        }

                        foreach (string member in existingMembers.Except(newMembers))
                        {
                            this.ValidateAddress(member, attributeName);
                            deletes.Add(member);
                        }

                        break;

                    case AttributeModificationType.Update:
                        foreach (string address in csentry.GetValueDeletes<string>(attributeName))
                        {
                            this.ValidateAddress( address, attributeName);
                            deletes.Add(address);
                        }

                        foreach (string address in csentry.GetValueAdds<string>(attributeName))
                        {
                            this.ValidateAddress(address, attributeName);
                            adds.Add(address);
                        }

                        break;

                    case AttributeModificationType.Unconfigured:
                    default:
                        throw new NotSupportedException("The modification type was unknown or unsupported");
                }
            }
        }

        private void ValidateAddress(string address, string attributeName)
        {
            if (this.config.MembersAsNonReference)
            {
                return;
            }

            if (attributeName.StartsWith("external"))
            {
                if (GroupMembership.IsAddressInternal(address))
                {
                    throw new UnexpectedDataException($"The value {address} cannot be exported as {attributeName} as it is a known internal domain. It should be exported as {attributeName.Replace("external", string.Empty)}");
                }
            }
            else
            {
                if (GroupMembership.IsAddressExternal(address))
                {
                    throw new UnexpectedDataException($"The value {address} cannot be exported as {attributeName} as it is not a known internal domain. It should be exported as external{attributeName}");
                }
            }
        }

        private bool ExistingMembershipRequiredForUpdate(CSEntryChange csentry)
        {
            if (csentry.ObjectModificationType == ObjectModificationType.Add)
            {
                return false;
            }

            if (this.config.InheritGroupRoles)
            {
                return (
               csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == this.config.GroupMemberAttributeName) ||
               csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == "externalMember") ||
               csentry.AttributeChanges.Any(t => t.Name == this.config.GroupManagerAttributeName) ||
               csentry.AttributeChanges.Any(t => t.Name == "externalManager") ||
               csentry.AttributeChanges.Any(t => t.Name == this.config.GroupOwnerAttributeName) ||
               csentry.AttributeChanges.Any(t => t.Name == "externalOwner"));
            }

            return (
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == this.config.GroupMemberAttributeName) ||
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == "externalMember") ||
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == this.config.GroupManagerAttributeName) ||
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == "externalManager") ||
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == this.config.GroupOwnerAttributeName) ||
                csentry.AttributeChanges.Any(t => (t.ModificationType == AttributeModificationType.Delete || t.ModificationType == AttributeModificationType.Replace) && t.Name == "externalOwner"));
        }
    }
}


text:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>

ERROR - 2015-06-04 08:51:18 --> 404 Page Not Found: M/inventory
ERROR - 2015-06-04 19:57:15 --> 404 Page Not Found: M/inventory


text:
Param (
    [Parameter(Mandatory=$True)] [String]$JenkinsJnlpUrl,
    [Parameter(Mandatory=$True)] [String]$JenkinsJnlpCredentials
)

Start-Process "C:\Program Files (x86)\Java\jre7\bin\java" -ArgumentList @("-jar", "slave.jar", "-jnlpUrl", $JenkinsJnlpUrl, "-jnlpCredentials", $JenkinsJnlpCredentials) -Verb RunAs


text:
'use strict';

function escapeTeamcityString(message) {
  if (!message) {
    return '';
  }

  return message.replace(/\|/g, '||')
    .replace(/\'/g, '|\'')
    .replace(/\n/g, '|n')
    .replace(/\r/g, '|r')
    .replace(/\u0085/g, '|x')
    .replace(/\u2028/g, '|l')
    .replace(/\u2029/g, '|p')
    .replace(/\[/g, '|[')
    .replace(/\]/g, '|]');
}

module.exports = {
  reporter: function(results) {
    var output = [];

    // Categorise each error by filename
    var errors = results.reduce(function(previous, current) {
      var error = current.error;

      if (!previous[current.file]) {
        previous[current.file] = [];
      }

      previous[current.file].push({
        name: escapeTeamcityString(current.file + ': line ' + error.line +
					', col ' + error.character + ', ' + error.reason),
        message: escapeTeamcityString(error.code + ': ' + error.reason),
        detailed: escapeTeamcityString(error.evidence)
      });

      return previous;
    }, {});

    output.push('##teamcity[compilationStarted compiler=\'jshint\']');
    Object.keys(errors).forEach(function(key) {
      errors[key].forEach(function(test) {
        var messageDetails = test.message + ': ' + test.detailed;
        output.push('##teamcity[message text=\'' + test.name +
					'\' errorDetails=\'' + messageDetails + '\' status=\'ERROR\']');
      });
    });
    output.push('##teamcity[compilationFinished compiler=\'jshint\']');

    // Print to process.stdout
    console.log(output.join('\n'));
  }
};


text:
<?php
/**
 * Created by PhpStorm.
 * User: MrSir
 * Date: 1/29/2017
 * Time: 7:33 PM
 */

namespace App\Http\Requests\Transaction\Frequency;

use Illuminate\Foundation\Http\FormRequest;

/**
 * Class Update
 * @package App\Http\Requests\Transaction\Frequency
 */
class Update extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'sometimes|required|string',
            'description' => 'sometimes|required|string'
        ];
    }
}


text:
package template

import (
	"fmt"
	"os"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/factory/base"
	"github.com/hyperhq/runv/factory/direct"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/lib/utils"
	"github.com/hyperhq/runv/template"
)

type templateFactory struct {
	s *template.TemplateVmConfig
}

func New(templateRoot string, cpu, mem int, kernel, initrd string, vsock bool) base.Factory {
	var vmName string

	for {
		vmName = fmt.Sprintf("template-vm-%s", utils.RandStr(10, "alpha"))
		if _, err := os.Stat(templateRoot + "/" + vmName); os.IsNotExist(err) {
			break
		}
	}
	s, err := template.CreateTemplateVM(templateRoot+"/"+vmName, vmName, cpu, mem, kernel, initrd, vsock)
	if err != nil {
		glog.Infof("failed to create template factory: %v", err)
		glog.Infof("use direct factory instead")
		return direct.New(cpu, mem, kernel, initrd, vsock)
	}
	return &templateFactory{s: s}
}

func NewFromExisted(s *template.TemplateVmConfig) base.Factory {
	return &templateFactory{s: s}
}

func (t *templateFactory) Config() *hypervisor.BootConfig {
	return t.s.BootConfigFromTemplate()
}

func (t *templateFactory) GetBaseVm() (*hypervisor.Vm, error) {
	return t.s.NewVmFromTemplate("")
}

func (t *templateFactory) CloseFactory() {
	t.s.Destroy()
}


text:
$(".order-item:not(.order-title)").click(function() {
  $(".active-list-item").removeClass("active-list-item");
  $(this).addClass("active-list-item");
  var orderId = $(this).attr("data-order-id");
  window.localStorage.orderId = orderId;
  $.ajax({
    url: '/orders/' + orderId,
    method: "GET",
    success: function(order) {
      var order = JSON.parse(order),
        dom = '
Информация за поръчка
' +
        '
' +
        '' +
        '
' +
        'Клиент: ' + order.name + '' +
        'Адрес: ' + order.deliveryAddress + '' +
        'Телефон: ' + order.phoneNumber + '' +
        '
' +
        '
' +
        '
' +
        '<div class="progress_container" data-order-id="<%= order.id %>">' +
          '
Приемане
' +
          '
Приготвяне
' +
          '
Проверка на качеството
' +
         '
На път за адрес
' +
          '
Доставена
' +
        '
'

      $(".details-view").html(dom);

      changeStatus(order.status);
      attachStatusActions();
    }
  });
});

function changeStatus(statusIndex) {
  $(".progress_container div").each(function (index) {
    if (index < statusIndex) {
      $(this).removeClass("active");
      $(this).addClass("done");
    } else if (index == statusIndex) {
      $(this).removeClass("active");
      $(this).addClass("active");
    } else {
      $(this).removeClass("active");
      $(this).removeClass("done");
    }
  });
}

function attachStatusActions() {
  $(".progress_container div").click(function() {
    var statusIndex = $(".progress_container div").index(this);
    $.ajax({
      url: '/orders/' + window.localStorage.orderId,
      method: "PUT",
      data: { statusIndex },
      dataType: "text",
      success: function(order) {
      	changeStatus(statusIndex);
      }
    });
  });
}


text:
title: ActiveRecord Sharding
categories:
  -
---

[前回](/2015/11/19/activerecord-connection/)の続き

ActiveRecordが実際にDBにクエリを発行する場合`ActiveRecord::Base.connection`を経由する。話を単純にする為に、クエリの実行だけに限定してShardingを実現しようと思った場合このメソッドがそれぞれのShardに対して確立されたコネクションを返せば良い。

一番多く採用されている実現方法はconnectionメソッドを上書きしてproxyを経由させるパターンで、多くのGemで採用されている。その他にもコネクションの確立方法・管理などで既存のGemがどうやって実現しているかをいくつか紹介する。

## Octopus

おそらく一番有名なのかもしれないGem。[kovyrin/db-charmer](https://github.com/kovyrin/db-charmer)の影響を少なからず受けている。

実際に置き換えているのは、ActiveRecord::BaseにOctopus::Modelをextendした際に実行されるhijack_methodsで行われている。

```ruby
# lib/octopus/model.rb

def hijack_methods
  # ...

  class << self
    # ...

    alias_method_chain :connection, :octopus
    alias_method_chain :connection_pool, :octopus
    alias_method_chain :clear_all_connections!, :octopus
    alias_method_chain :clear_active_connections!, :octopus
    alias_method_chain :connected?, :octopus

    # ...
  end
end

def connection_with_octopus
  if should_use_normal_connection?
    connection_without_octopus
  else
    connection_proxy.current_model = self
    connection_proxy
  end
end

def connection_proxy
  ActiveRecord::Base.class_variable_defined?(:@@connection_proxy) &&
    ActiveRecord::Base.class_variable_get(:@@connection_proxy) ||
    ActiveRecord::Base.class_variable_set(:@@connection_proxy, Octopus::Proxy.new)
end
```

Octopus::Proxyはそこそこにツラみを伴うアレなクラスで、Shard毎のコネクションの切り替え以外にもShardへのコネクション確立(プールの作成)と管理もProxyクラス自身で行っている。つまりActiveRecordのConnectionHandlerのような役割を自前で実装している。初めてconnectionが実行されたタイミングでインスタンスが生成され、initalizeで各Shardへのプールを作成する。

内部ではTLSを使ってShardを管理していて、method_missiongでその時に指定されているShardのコネクションを取得してProxyする。簡単なダミーコードで説明すると

```ruby
Thread.current[:octopus_shard]              # => nil
ActiveRecord::Base.connection.execute(...)  # => default connection.

Octopus.using(:shard1) do
  Thread.current[:octopus_shard]             # => :shard1
  ActiveRecord::Base.connection.execute(...) # => :shard1 connection.
end
```

のような感じ。 Shardへの振り分けはProxyのmethod_missingで

```ruby
def method_missing(method, *args, &block)
  if should_clean_connection_proxy?(method)
    conn = select_connection
    self.last_current_shard = current_shard
    clean_connection_proxy
    conn.send(method, *args, &block)
  elsif should_send_queries_to_shard_slave_group?(method)
    send_queries_to_shard_slave_group(method, *args, &block)
  elsif should_send_queries_to_slave_group?(method)
    send_queries_to_slave_group(method, *args, &block)
  elsif should_send_queries_to_replicated_databases?(method)
    send_queries_to_selected_slave(method, *args, &block)
  else
    select_connection.send(method, *args, &block)
  end
end
```

現在のShardに対するconnectionを取得してそのconnectionに対してsendでdelegateする実装になっている。その他にも実際にはRelation・Associationの対応でもう少し作りこみが必要だが基本的な考え方はこれで問題ない。

## SwitchPoint

ShardingではなくR/W splittingに使われているGem。コネクションを切り替えるという点ではだいたい似ているので紹介しておく。軽量なGemで見通しも良いので何か1つガッツリ読みこんで勉強したいのであればこのGemを選択するの良いかもしれない。

SwitchPoint::ModelをincludeしたModelに対してconnectionを差し替える。その後use_switch_pointを実行することでproxyを有効にする。

```ruby
# lib/switch_point/model.rb

def self.included(model)
  model.singleton_class.class_eval do
    include ClassMethods
    alias_method_chain :connection, :switch_point
    alias_method_chain :cache, :switch_point
    alias_method_chain :uncached, :switch_point
  end
end

module ClassMethods
  def connection_with_switch_point
    if switch_point_proxy
      switch_point_proxy.connection
    else
      connection_without_switch_point
    end
  end

  def switch_point_proxy
    if @switch_point_name
      ProxyRepository.checkout(@switch_point_name)
    elsif self == ActiveRecord::Base
      nil
    else
      superclass.switch_point_proxy
    end
  end
end
```

コネクションはProxyRepository.checkoutのタイミングで未初期化の@switch_point_nameであれば作成される。また、継承関係にも対応していて自身でuse_switch_pointを実行していないクラスは親クラスのproxyをたどることでActiveRecordの様にコネクションを使い回す様になっている。

コネクションの確立方法は、ActiveRecord::Baseを継承するダミークラスを作成してそのクラスを使ってestablish_connectionを実行している。

```ruby
# lib/switch_point/proxy.rb

def define_model(name, mode)
  model_name = SwitchPoint.config.model_name(name, mode)
  if model_name
    model = Class.new(ActiveRecord::Base)
    Proxy.const_set(model_name, model)
    model.establish_connection(SwitchPoint.config.database_name(name, mode))
    model
  elsif mode == :readonly
    # Re-use writable connection
    Proxy.const_get(SwitchPoint.config.model_name(name, :writable))
  else
    Class.new(ActiveRecord::Base)
  end
end
```

後は必要に応じてクラスを取り出してconnectionを取得する。この方式のいいところはOctopusと違ってestablish_connection経由でのコネクション確立なのでActiveRecordの変更に比較的強い所と、ActiveRecord::BaseのConnectionHandlerに管理を任せられる部分にある(clear_active_connections!等の確保済みコネクションに対する処理の辺り)。

SwitchPointもproxy内部でmodeがTLSで管理されていてreadとwriteのコネクションを切り替える仕組みになっている。

```ruby
Article.with_readonly { Article.first } # Read from db-blog-slave
Category.with_readonly { Category.first } # Also read from db-blog-slave
Comment.with_readonly { Comment.first } # Read from db-comment-slave

Article.with_readonly do
  article = Article.first  # Read from db-blog-slave
  article.title = 'new title'
  Article.with_writable do
    article.save!  # Write to db-blog-master
    article.reload  # Read from db-blog-master
    Category.first  # Read from db-blog-master
  end
end
```

## ActiveRecord::Turntable

ドリコム製のライブラリ。これまでにあったGemとはアプローチが大きく異なりconnectionを上書きしない。更に`Ocotpus.using`のようなShardの指定を明示的に行わずにShardingを実現している。また、クラスタでUniqなIDを生成するためのSequencerが付いてきたりする。

```ruby
# lib/active_record/turntable/base.rb

def turntable(cluster_name, shard_key_name, options = {})
  # ...

  self.turntable_cluster =
    self.turntable_clusters[cluster_name] ||= Cluster.new(
                                                turntable_config[:clusters][cluster_name],
                                                options
                                              )
  turntable_replace_connection_pool
end

def turntable_replace_connection_pool
  ch = connection_handler
  cp = ConnectionProxy.new(self, turntable_cluster)
  pp = PoolProxy.new(cp)
  ch.class_to_pool.clear if defined?(ch.class_to_pool)
  ch.send(:class_to_pool)[name] = ch.send(:owner_to_pool)[name] = pp
end
```

[前回](/2015/11/19/activerecord-connection/)見たConnectionHandlerのowner_to_poolをConnectionProxyで置き換えている。PoolProxyはプール全体のコネクションに実行する必要のあるメソッドをproxyしている。

コネクションの確立方法はSwitchPoint等と同様に、Shard毎にActiveRecord::Baseを継承するダミークラスを作成してestablish_connectionするパターン。

```ruby
def get_or_set_connection_class
  if Connections.const_defined?(name.classify)
    klass = Connections.const_get(name.classify)
  else
    klass = Class.new(ActiveRecord::Base)
    Connections.const_set(name.classify, klass)
    klass.abstract_class = true
  end
  klass
end

def create_connection_class
  klass = get_or_set_connection_class
  klass.remove_connection
  klass.establish_connection ActiveRecord::Base.connection_pool.spec.config[:shards][name].with_indifferent_access
  klass
end
```

後は必要に応じてクラスを取り出してconnectionとconnection_poolを取得する。ただ、Turntableの場合は変更が大きすぎて確保済みのコネクションに対する処理などは自前で行う必要があるっぽいけど。

実際にコネクションを切り替える部分は

```ruby
# lib/active_record/turntable/connection_proxy.rb

def method_missing(method, *args, &block)
  clear_query_cache_if_needed(method)
  if shard_fixed?
    connection.send(method, *args, &block)
  elsif mixable?(method, *args)
    fader = @mixer.build_fader(method, *args, &block)
    logger.debug { "[ActiveRecord::Turntable] Sending method: #{method}, " +
      "sql: #{args.first}, " +
      "shards: #{fader.shards_query_hash.keys.map(&:name)}" }
    fader.execute
  else
    connection.send(method, *args, &block)
  end
end
```

となっている。`shard_fixed?`はOctopus.usingでShardを指定した時の様に、Shardが指定されていて判定する必要がない場合。通常のクエリなどで判定の必要がある場合には`@mixer.build_fader`でクエリから対象のShardを判定している。この中で引数のSQLを[wvanbergen/sql_tree](https://github.com/wvanbergen/sql_tree)を使ってSQLをパースして対象Shardを絞り込んでいる。大分マッチョだ。

## Other

その他にも、開発が止まってるものやReplication系の物も含めるといくつかライブラリはある。

* [taiki45/mixed_gauge](https://github.com/taiki45/mixed_gauge)
* [zendesk/active_record_shards](https://github.com/zendesk/active_record_shards)
* [mperham/data_fabric](https://github.com/mperham/data_fabric)
* [technoweenie/masochism](https://github.com/technoweenie/masochism)
* [r7kamura/replicat](https://github.com/r7kamura/replicat)
* [schoefmax/multi_db](https://github.com/schoefmax/multi_db)
* [mixonic/ShardTheLove](https://github.com/mixonic/ShardTheLove)

## Yet Another

クエリの実行だけに絞って見てきたが、実際には

* Relation
* Association
* Migration
* QueryCache
* インスタンスの処理
  * saveなどで呼ばれるtransactionのコネクション指定
* その他connection操作
  * clear_all_connections!
  * clear_active_connections!
  * etc.

などコネクションの差替え以外にも作りこまなければいけない部分が多い。が、基本的には上で見てきた切替えがベースとなる。

今私が作っているソーシャルゲームはRailsで書いていてDB ShardingはOctopusを使用しているんだけど、なかなか使いづらい所や不満もそこそこにはあったりする。もう直ってるけどコネクションプールを独自で管理してるのでコネクションがリクエストごとにロストしたりしてたこともある。また、他のDB系のライブラリ(annotate_modelsやdatabase_rewinderとか)が手を入れないとうまく動かない。都度パッチを投げても良いんだけど別の実装アイディアもあったりするので、最近プロトタイプ的な感じで1から新しく設計し始めている。

Sharding用のライブラリはザックリと「コネクションの管理」と「シャードの特定」の2つの要素に分解してそれぞれをコンパクトに実装するのが大切だと思っている。コネクションの管理は各シャードへのコネクション確立とModelがクエリを実行するコネクションの切替部分、シャードの特定はその上で実装されるShardingやReplicationのロジックのことを指している。今作ろうとしているものはコネクションの管理をメインに行うつもりだ。

特にコネクション管理をコンパクトに保つのはとても大切で、ActiveRecordの変更に追従するのは非常に困難であることはよく知られている。(実際に[kovyrin/db-charmer](https://github.com/kovyrin/db-charmer)の作者は[ギブアップ](http://kovyrin.net/2014/11/14/dbcharmer-suspended/)してしまった)コネクションの管理をうまく一般化・抽象化することができればそのGemが土台となって、ActiveRecordとの互換性を気にせず本当に作りたかった機能に注力出来て皆ハッピーみたいな事が出来ればとか考えていたりする。

まあ実際にやってみるとActiveRecordの設計から大きく外れないように設計し、更には内部のAPIなどを極力呼ばない様に作っていくの抜け道を探しているような、パズルをしているような間隔でそれはそれで楽しいものだ。一種の縛りプレイをやっているような感覚に近いかもしれない。

もう少し設計が固まってきたら、また纏めたいと思う。


text:
package com.biglakesystems.scamp.impl.spring;

import com.biglakesystems.quality.Assert;
import com.biglakesystems.scamp.source.Source;
import com.biglakesystems.scamp.source.SourceLocator;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class SpringSourceLocator implements SourceLocator
{
    private static final Logger LOG = LoggerFactory.getLogger(SpringSourceLocator.class);

    private final List<String> m_prefixes;
    private final ResourcePatternResolver m_resolver;

    /**
     * Construct a {@link SpringSourceLocator} instance.
     *
     * @param resolver the resource resolver which will be used to resolve script resources.
     * @param prefixes the script prefix search path.
     */
    SpringSourceLocator(final ResourcePatternResolver resolver, List<String> prefixes)
    {
        super();
        Assert.argumentNotNull("resolver", m_resolver = resolver);
        Assert.argumentNotNull("prefixes", prefixes);
        m_prefixes = Collections.unmodifiableList(new ArrayList<String>(prefixes));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
        return new ToStringBuilder(this).append("resolver", m_resolver).append("prefixes", m_prefixes).toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Source locate(final String name)
    {
        Resource found = null;
        for (final String nextPrefix : m_prefixes)
        {
            final String expanded = nextPrefix + name;
            final Resource resource = m_resolver.getResource(expanded);
            if (null != resource && resource.exists() && resource.isReadable())
            {
                found = resource;
                LOG.debug("Found resource {} for name [{}] with resolver {} and prefix [{}].", found, expanded,
                        m_resolver, nextPrefix);
                break;
            }
        }

        /* Create and return the script source. */
        final Source result;
        if (null == found)
        {
            result = null;
            LOG.info("Found no source for script with name [{}].", name);
        }
        else
        {
            result = new ResourceSource(found);
            LOG.info("Returning source {} for script with name [{}].", result, name);
        }
        return result;
    }

    /**
     * For testing purposes only.
     *
     * @return {@link List} instance.
     */
    List<String> getPrefixes()
    {
        return m_prefixes;
    }

    /**
     * For testing purposes only.
     *
     * @return {@link ResourcePatternResolver} instance.
     */
    ResourcePatternResolver getResolver()
    {
        return m_resolver;
    }
}


text:
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#one-app-collapse-1">
                Toggle navigation

            </button>

                 Angular Point Demo

                     Demo


text:
/**
 * @fileoverview Low-level DOM traversal utility functions to find the
 *     next (or previous) character, word, sentence, line, or paragraph,
 *     in a completely stateless manner without actually manipulating the
 *     selection.
 */

/**
 * A class to represent a cursor location in the document,
 * like the start position or end position of a selection range.
 *
 * Later this may be extended to support "virtual text" for an object,
 * like the ALT text for an image.
 *
 * Note: we cache the text of a particular node at the time we
 * traverse into it. Later we should add support for dynamically
 * reloading it.
 * @param {Node} node The DOM node.
 * @param {number} index The index of the character within the node.
 * @param {string} text The cached text contents of the node.
 * @constructor
 */
Cursor = function(node, index, text) {
  this.node = node;
  this.index = index;
  this.text = text;
};

/**
 * @return {Cursor} A new cursor pointing to the same location.
 */
Cursor.prototype.clone = function() {
  return new Cursor(this.node, this.index, this.text);
};

/**
 * Modify this cursor to point to the location that another cursor points to.
 * @param {Cursor} otherCursor The cursor to copy from.
 */
Cursor.prototype.copyFrom = function(otherCursor) {
  this.node = otherCursor.node;
  this.index = otherCursor.index;
  this.text = otherCursor.text;
};

/**
 * Utility functions for stateless DOM traversal.
 * @constructor
 */
TraverseUtil = function() {};

/**
 * Gets the text representation of a node. This allows us to substitute
 * alt text, names, or titles for html elements that provide them.
 * @param {Node} node A DOM node.
 * @return {string} A text string representation of the node.
 */
TraverseUtil.getNodeText = function(node) {
  if (node.constructor == Text) {
    return node.data;
  } else {
    return '';
  }
};

/**
 * Return true if a node should be treated as a leaf node, because
 * its children are properties of the object that shouldn't be traversed.
 *
 * TODO(dmazzoni): replace this with a predicate that detects nodes with
 * ARIA roles and other objects that have their own description.
 * For now we just detect a couple of common cases.
 *
 * @param {Node} node A DOM node.
 * @return {boolean} True if the node should be treated as a leaf node.
 */
TraverseUtil.treatAsLeafNode = function(node) {
  return node.childNodes.length == 0 ||
         node.nodeName == 'SELECT' ||
         node.nodeName == 'OBJECT';
};

/**
 * Return true only if a single character is whitespace.
 * From https://developer.mozilla.org/en/Whitespace_in_the_DOM,
 * whitespace is defined as one of the characters
 *  "\t" TAB \u0009
 *  "\n" LF  \u000A
 *  "\r" CR  \u000D
 *  " "  SPC \u0020.
 *
 * @param {string} c A string containing a single character.
 * @return {boolean} True if the character is whitespace, otherwise false.
 */
TraverseUtil.isWhitespace = function(c) {
  return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
};

/**
 * Set the selection to the range between the given start and end cursors.
 * @param {Cursor} start The desired start of the selection.
 * @param {Cursor} end The desired end of the selection.
 * @return {Selection} the selection object.
 */
TraverseUtil.setSelection = function(start, end) {
  var sel = window.getSelection();
  sel.removeAllRanges();
  var range = document.createRange();
  range.setStart(start.node, start.index);
  range.setEnd(end.node, end.index);
  sel.addRange(range);

  return sel;
};

/**
 * Use the computed CSS style to figure out if this DOM node is currently
 * visible.
 * @param {Node} node A HTML DOM node.
 * @return {boolean} Whether or not the html node is visible.
 */
TraverseUtil.isVisible = function(node) {
  if (!node.style)
    return true;
  var style = window.getComputedStyle(/** @type {Element} */(node), null);
  return (!!style && style.display != 'none' && style.visibility != 'hidden');
};

/**
 * Use the class name to figure out if this DOM node should be traversed.
 * @param {Node} node A HTML DOM node.
 * @return {boolean} Whether or not the html node should be traversed.
 */
TraverseUtil.isSkipped = function(node) {
  if (node.constructor == Text)
    node = node.parentElement;
  if (node.className == 'CaretBrowsing_Caret' ||
      node.className == 'CaretBrowsing_AnimateCaret') {
    return true;
  }
  return false;
};

/**
 * Moves the cursor forwards until it has crossed exactly one character.
 * @param {Cursor} cursor The cursor location where the search should start.
 *     On exit, the cursor will be immediately to the right of the
 *     character returned.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The character found, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.forwardsChar = function(cursor, nodesCrossed) {
  while (true) {
    // Move down until we get to a leaf node.
    var childNode = null;
    if (!TraverseUtil.treatAsLeafNode(cursor.node)) {
      for (var i = cursor.index; i < cursor.node.childNodes.length; i++) {
        var node = cursor.node.childNodes[i];
        if (TraverseUtil.isSkipped(node)) {
          nodesCrossed.push(node);
          continue;
        }
        if (TraverseUtil.isVisible(node)) {
          childNode = node;
          break;
        }
      }
    }
    if (childNode) {
      cursor.node = childNode;
      cursor.index = 0;
      cursor.text = TraverseUtil.getNodeText(cursor.node);
      if (cursor.node.constructor != Text) {
        nodesCrossed.push(cursor.node);
      }
      continue;
    }

    // Return the next character from this leaf node.
    if (cursor.index < cursor.text.length)
      return cursor.text[cursor.index++];

    // Move to the next sibling, going up the tree as necessary.
    while (cursor.node != null) {
      // Try to move to the next sibling.
      var siblingNode = null;
      for (var node = cursor.node.nextSibling;
           node != null;
           node = node.nextSibling) {
        if (TraverseUtil.isSkipped(node)) {
          nodesCrossed.push(node);
          continue;
        }
        if (TraverseUtil.isVisible(node)) {
          siblingNode = node;
          break;
        }
      }
      if (siblingNode) {
        cursor.node = siblingNode;
        cursor.text = TraverseUtil.getNodeText(siblingNode);
        cursor.index = 0;

        if (cursor.node.constructor != Text) {
          nodesCrossed.push(cursor.node);
        }

        break;
      }

      // Otherwise, move to the parent.
      if (cursor.node.parentNode &&
          cursor.node.parentNode.constructor != HTMLBodyElement) {
        cursor.node = cursor.node.parentNode;
        cursor.text = null;
        cursor.index = 0;
      } else {
        return null;
      }
    }
  }
};

/**
 * Moves the cursor backwards until it has crossed exactly one character.
 * @param {Cursor} cursor The cursor location where the search should start.
 *     On exit, the cursor will be immediately to the left of the
 *     character returned.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The previous character, or null if the top of the
 *     document has been reached.
 */
TraverseUtil.backwardsChar = function(cursor, nodesCrossed) {
  while (true) {
    // Move down until we get to a leaf node.
    var childNode = null;
    if (!TraverseUtil.treatAsLeafNode(cursor.node)) {
      for (var i = cursor.index - 1; i >= 0; i--) {
        var node = cursor.node.childNodes[i];
        if (TraverseUtil.isSkipped(node)) {
          nodesCrossed.push(node);
          continue;
        }
        if (TraverseUtil.isVisible(node)) {
          childNode = node;
          break;
        }
      }
    }
    if (childNode) {
      cursor.node = childNode;
      cursor.text = TraverseUtil.getNodeText(cursor.node);
      if (cursor.text.length)
        cursor.index = cursor.text.length;
      else
        cursor.index = cursor.node.childNodes.length;
      if (cursor.node.constructor != Text)
        nodesCrossed.push(cursor.node);
      continue;
    }

    // Return the previous character from this leaf node.
    if (cursor.text.length > 0 && cursor.index > 0) {
      return cursor.text[--cursor.index];
    }

    // Move to the previous sibling, going up the tree as necessary.
    while (true) {
      // Try to move to the previous sibling.
      var siblingNode = null;
      for (var node = cursor.node.previousSibling;
           node != null;
           node = node.previousSibling) {
        if (TraverseUtil.isSkipped(node)) {
          nodesCrossed.push(node);
          continue;
        }
        if (TraverseUtil.isVisible(node)) {
          siblingNode = node;
          break;
        }
      }
      if (siblingNode) {
        cursor.node = siblingNode;
        cursor.text = TraverseUtil.getNodeText(siblingNode);
        if (cursor.text.length)
          cursor.index = cursor.text.length;
        else
          cursor.index = cursor.node.childNodes.length;
        if (cursor.node.constructor != Text)
          nodesCrossed.push(cursor.node);
        break;
      }

      // Otherwise, move to the parent.
      if (cursor.node.parentNode &&
          cursor.node.parentNode.constructor != HTMLBodyElement) {
        cursor.node = cursor.node.parentNode;
        cursor.text = null;
        cursor.index = 0;
      } else {
        return null;
      }
    }
  }
};

/**
 * Finds the next character, starting from endCursor.  Upon exit, startCursor
 * and endCursor will surround the next character. If skipWhitespace is
 * true, will skip until a real character is found. Otherwise, it will
 * attempt to select all of the whitespace between the initial position
 * of endCursor and the next non-whitespace character.
 * @param {Cursor} startCursor On exit, points to the position before
 *     the char.
 * @param {Cursor} endCursor The position to start searching for the next
 *     char.  On exit, will point to the position past the char.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {boolean} skipWhitespace If true, will keep scanning until a
 *     non-whitespace character is found.
 * @return {?string} The next char, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextChar = function(
    startCursor, endCursor, nodesCrossed, skipWhitespace) {

  // Save the starting position and get the first character.
  startCursor.copyFrom(endCursor);
  var c = TraverseUtil.forwardsChar(endCursor, nodesCrossed);
  if (c == null)
    return null;

  // Keep track of whether the first character was whitespace.
  var initialWhitespace = TraverseUtil.isWhitespace(c);

  // Keep scanning until we find a non-whitespace or non-skipped character.
  while ((TraverseUtil.isWhitespace(c)) ||
      (TraverseUtil.isSkipped(endCursor.node))) {
    c = TraverseUtil.forwardsChar(endCursor, nodesCrossed);
    if (c == null)
      return null;
  }
  if (skipWhitespace || !initialWhitespace) {
    // If skipWhitepace is true, or if the first character we encountered
    // was not whitespace, return that non-whitespace character.
    startCursor.copyFrom(endCursor);
    startCursor.index--;
    return c;
  }
  else {
    for (var i = 0; i < nodesCrossed.length; i++) {
      if (TraverseUtil.isSkipped(nodesCrossed[i])) {
        // We need to make sure that startCursor and endCursor aren't
        // surrounding a skippable node.
        endCursor.index--;
        startCursor.copyFrom(endCursor);
        startCursor.index--;
        return ' ';
      }
    }
    // Otherwise, return all of the whitespace before that last character.
    endCursor.index--;
    return ' ';
  }
};

/**
 * Finds the previous character, starting from startCursor.  Upon exit,
 * startCursor and endCursor will surround the previous character.
 * If skipWhitespace is true, will skip until a real character is found.
 * Otherwise, it will attempt to select all of the whitespace between
 * the initial position of endCursor and the next non-whitespace character.
 * @param {Cursor} startCursor The position to start searching for the
 *     char. On exit, will point to the position before the char.
 * @param {Cursor} endCursor The position to start searching for the next
 *     char. On exit, will point to the position past the char.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {boolean} skipWhitespace If true, will keep scanning until a
 *     non-whitespace character is found.
 * @return {?string} The previous char, or null if the top of the
 *     document has been reached.
 */
TraverseUtil.getPreviousChar = function(
    startCursor, endCursor, nodesCrossed, skipWhitespace) {

  // Save the starting position and get the first character.
  endCursor.copyFrom(startCursor);
  var c = TraverseUtil.backwardsChar(startCursor, nodesCrossed);
  if (c == null)
    return null;

  // Keep track of whether the first character was whitespace.
  var initialWhitespace = TraverseUtil.isWhitespace(c);

  // Keep scanning until we find a non-whitespace or non-skipped character.
  while ((TraverseUtil.isWhitespace(c)) ||
      (TraverseUtil.isSkipped(startCursor.node))) {
    c = TraverseUtil.backwardsChar(startCursor, nodesCrossed);
    if (c == null)
      return null;
  }
  if (skipWhitespace || !initialWhitespace) {
    // If skipWhitepace is true, or if the first character we encountered
    // was not whitespace, return that non-whitespace character.
    endCursor.copyFrom(startCursor);
    endCursor.index++;
    return c;
  } else {
    for (var i = 0; i < nodesCrossed.length; i++) {
      if (TraverseUtil.isSkipped(nodesCrossed[i])) {
        startCursor.index++;
        endCursor.copyFrom(startCursor);
        endCursor.index++;
        return ' ';
      }
    }
    // Otherwise, return all of the whitespace before that last character.
    startCursor.index++;
    return ' ';
  }
};

/**
 * Finds the next word, starting from endCursor.  Upon exit, startCursor
 * and endCursor will surround the next word.  A word is defined to be
 * a string of 1 or more non-whitespace characters in the same DOM node.
 * @param {Cursor} startCursor On exit, will point to the beginning of the
 *     word returned.
 * @param {Cursor} endCursor The position to start searching for the next
 *     word.  On exit, will point to the end of the word returned.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The next word, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextWord = function(startCursor, endCursor,
    nodesCrossed) {

  // Find the first non-whitespace or non-skipped character.
  var cursor = endCursor.clone();
  var c = TraverseUtil.forwardsChar(cursor, nodesCrossed);
  if (c == null)
    return null;
  while ((TraverseUtil.isWhitespace(c)) ||
      (TraverseUtil.isSkipped(cursor.node))) {
    c = TraverseUtil.forwardsChar(cursor, nodesCrossed);
    if (c == null)
      return null;
  }

  // Set startCursor to the position immediately before the first
  // character in our word. It's safe to decrement |index| because
  // forwardsChar guarantees that the cursor will be immediately to the
  // right of the returned character on exit.
  startCursor.copyFrom(cursor);
  startCursor.index--;

  // Keep building up our word until we reach a whitespace character or
  // would cross a tag.  Don't actually return any tags crossed, because this
  // word goes up until the tag boundary but not past it.
  endCursor.copyFrom(cursor);
  var word = c;
  var newNodesCrossed = [];
  c = TraverseUtil.forwardsChar(cursor, newNodesCrossed);
  if (c == null) {
    return word;
  }
  while (!TraverseUtil.isWhitespace(c) &&
     newNodesCrossed.length == 0) {
    word += c;
    endCursor.copyFrom(cursor);
    c = TraverseUtil.forwardsChar(cursor, newNodesCrossed);
    if (c == null) {
      return word;
    }
  }
  return word;
};

/**
 * Finds the previous word, starting from startCursor.  Upon exit, startCursor
 * and endCursor will surround the previous word.  A word is defined to be
 * a string of 1 or more non-whitespace characters in the same DOM node.
 * @param {Cursor} startCursor The position to start searching for the
 *     previous word.  On exit, will point to the beginning of the
 *     word returned.
 * @param {Cursor} endCursor On exit, will point to the end of the
 *     word returned.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The previous word, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getPreviousWord = function(startCursor, endCursor,
    nodesCrossed) {
  // Find the first non-whitespace or non-skipped character.
  var cursor = startCursor.clone();
  var c = TraverseUtil.backwardsChar(cursor, nodesCrossed);
  if (c == null)
    return null;
  while ((TraverseUtil.isWhitespace(c) ||
      (TraverseUtil.isSkipped(cursor.node)))) {
    c = TraverseUtil.backwardsChar(cursor, nodesCrossed);
    if (c == null)
      return null;
  }

  // Set endCursor to the position immediately after the first
  // character we've found (the last character of the word, since we're
  // searching backwards).
  endCursor.copyFrom(cursor);
  endCursor.index++;

  // Keep building up our word until we reach a whitespace character or
  // would cross a tag.  Don't actually return any tags crossed, because this
  // word goes up until the tag boundary but not past it.
  startCursor.copyFrom(cursor);
  var word = c;
  var newNodesCrossed = [];
  c = TraverseUtil.backwardsChar(cursor, newNodesCrossed);
  if (c == null)
    return word;
  while (!TraverseUtil.isWhitespace(c) &&
      newNodesCrossed.length == 0) {
    word = c + word;
    startCursor.copyFrom(cursor);
    c = TraverseUtil.backwardsChar(cursor, newNodesCrossed);
    if (c == null)
      return word;
  }

  return word;
};

/**
 * Finds the next sentence, starting from endCursor.  Upon exit,
 * startCursor and endCursor will surround the next sentence.
 *
 * @param {Cursor} startCursor On exit, marks the beginning of the sentence.
 * @param {Cursor} endCursor The position to start searching for the next
 *     sentence.  On exit, will point to the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {Object} breakTags Associative array of tags that should break
 *     the sentence.
 * @return {?string} The next sentence, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextSentence = function(
    startCursor, endCursor, nodesCrossed, breakTags) {
  return TraverseUtil.getNextString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        if (str.substr(-1) == '.')
          return true;
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && (style.display != 'inline' ||
                        breakTags[nodes[i].tagName])) {
            return true;
          }
        }
        return false;
      });
};

/**
 * Finds the previous sentence, starting from startCursor.  Upon exit,
 * startCursor and endCursor will surround the previous sentence.
 *
 * @param {Cursor} startCursor The position to start searching for the next
 *     sentence.  On exit, will point to the start of the returned string.
 * @param {Cursor} endCursor On exit, the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {Object} breakTags Associative array of tags that should break
 *     the sentence.
 * @return {?string} The previous sentence, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getPreviousSentence = function(
    startCursor, endCursor, nodesCrossed, breakTags) {
  return TraverseUtil.getPreviousString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        if (word.substr(-1) == '.')
          return true;
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && (style.display != 'inline' ||
                        breakTags[nodes[i].tagName])) {
            return true;
          }
        }
        return false;
      });
};

/**
 * Finds the next line, starting from endCursor.  Upon exit,
 * startCursor and endCursor will surround the next line.
 *
 * @param {Cursor} startCursor On exit, marks the beginning of the line.
 * @param {Cursor} endCursor The position to start searching for the next
 *     line.  On exit, will point to the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {number} lineLength The maximum number of characters in a line.
 * @param {Object} breakTags Associative array of tags that should break
 *     the line.
 * @return {?string} The next line, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextLine = function(
    startCursor, endCursor, nodesCrossed, lineLength, breakTags) {
  return TraverseUtil.getNextString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        if (str.length + word.length + 1 > lineLength)
          return true;
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && (style.display != 'inline' ||
                        breakTags[nodes[i].tagName])) {
            return true;
          }
        }
        return false;
      });
};

/**
 * Finds the previous line, starting from startCursor.  Upon exit,
 * startCursor and endCursor will surround the previous line.
 *
 * @param {Cursor} startCursor The position to start searching for the next
 *     line.  On exit, will point to the start of the returned string.
 * @param {Cursor} endCursor On exit, the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {number} lineLength The maximum number of characters in a line.
 * @param {Object} breakTags Associative array of tags that should break
 *     the sentence.
 *  @return {?string} The previous line, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getPreviousLine = function(
    startCursor, endCursor, nodesCrossed, lineLength, breakTags) {
  return TraverseUtil.getPreviousString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        if (str.length + word.length + 1 > lineLength)
          return true;
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && (style.display != 'inline' ||
                        breakTags[nodes[i].tagName])) {
            return true;
          }
        }
        return false;
      });
};

/**
 * Finds the next paragraph, starting from endCursor.  Upon exit,
 * startCursor and endCursor will surround the next paragraph.
 *
 * @param {Cursor} startCursor On exit, marks the beginning of the paragraph.
 * @param {Cursor} endCursor The position to start searching for the next
 *     paragraph.  On exit, will point to the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The next paragraph, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextParagraph = function(startCursor, endCursor,
    nodesCrossed) {
  return TraverseUtil.getNextString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && style.display != 'inline') {
            return true;
          }
        }
        return false;
      });
};

/**
 * Finds the previous paragraph, starting from startCursor.  Upon exit,
 * startCursor and endCursor will surround the previous paragraph.
 *
 * @param {Cursor} startCursor The position to start searching for the next
 *     paragraph.  On exit, will point to the start of the returned string.
 * @param {Cursor} endCursor On exit, the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @return {?string} The previous paragraph, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getPreviousParagraph = function(
    startCursor, endCursor, nodesCrossed) {
  return TraverseUtil.getPreviousString(
      startCursor, endCursor, nodesCrossed,
      function(str, word, nodes) {
        for (var i = 0; i < nodes.length; i++) {
          if (TraverseUtil.isSkipped(nodes[i])) {
            return true;
          }
          var style = window.getComputedStyle(nodes[i], null);
          if (style && style.display != 'inline') {
            return true;
          }
        }
        return false;
      });
};

/**
 * Customizable function to return the next string of words in the DOM, based
 * on provided functions to decide when to break one string and start
 * the next. This can be used to get the next sentence, line, paragraph,
 * or potentially other granularities.
 *
 * Finds the next contiguous string, starting from endCursor.  Upon exit,
 * startCursor and endCursor will surround the next string.
 *
 * The breakBefore function takes three parameters, and
 * should return true if the string should be broken before the proposed
 * next word:
 *   str The string so far.
 *   word The next word to be added.
 *   nodesCrossed The nodes crossed in reaching this next word.
 *
 * @param {Cursor} startCursor On exit, will point to the beginning of the
 *     next string.
 * @param {Cursor} endCursor The position to start searching for the next
 *     string.  On exit, will point to the end of the returned string.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {function(string, string, Array<string>)} breakBefore
 *     Function that takes the string so far, next word to be added, and
 *     nodes crossed, and returns true if the string should be ended before
 *     adding this word.
 * @return {?string} The next string, or null if the bottom of the
 *     document has been reached.
 */
TraverseUtil.getNextString = function(
    startCursor, endCursor, nodesCrossed, breakBefore) {
  // Get the first word and set the start cursor to the start of the
  // first word.
  var wordStartCursor = endCursor.clone();
  var wordEndCursor = endCursor.clone();
  var newNodesCrossed = [];
  var str = '';
  var word = TraverseUtil.getNextWord(
      wordStartCursor, wordEndCursor, newNodesCrossed);
  if (word == null)
    return null;
  startCursor.copyFrom(wordStartCursor);

  // Always add the first word when the string is empty, and then keep
  // adding more words as long as breakBefore returns false
  while (!str || !breakBefore(str, word, newNodesCrossed)) {
    // Append this word, set the end cursor to the end of this word, and
    // update the returned list of nodes crossed to include ones we crossed
    // in reaching this word.
    if (str)
      str += ' ';
    str += word;
    nodesCrossed = nodesCrossed.concat(newNodesCrossed);
    endCursor.copyFrom(wordEndCursor);

    // Get the next word and go back to the top of the loop.
    newNodesCrossed = [];
    word = TraverseUtil.getNextWord(
        wordStartCursor, wordEndCursor, newNodesCrossed);
    if (word == null)
      return str;
  }

  return str;
};

/**
 * Customizable function to return the previous string of words in the DOM,
 * based on provided functions to decide when to break one string and start
 * the next. See getNextString, above, for more details.
 *
 * Finds the previous contiguous string, starting from startCursor.  Upon exit,
 * startCursor and endCursor will surround the next string.
 *
 * @param {Cursor} startCursor The position to start searching for the
 *     previous string.  On exit, will point to the beginning of the
 *     string returned.
 * @param {Cursor} endCursor On exit, will point to the end of the
 *     string returned.
 * @param {Array<Node>} nodesCrossed Any HTML nodes crossed between the
 *     initial and final cursor position will be pushed onto this array.
 * @param {function(string, string, Array<string>)} breakBefore
 *     Function that takes the string so far, the word to be added, and
 *     nodes crossed, and returns true if the string should be ended before
 *     adding this word.
 * @return {?string} The next string, or null if the top of the
 *     document has been reached.
 */
TraverseUtil.getPreviousString = function(
    startCursor, endCursor, nodesCrossed, breakBefore) {
  // Get the first word and set the end cursor to the end of the
  // first word.
  var wordStartCursor = startCursor.clone();
  var wordEndCursor = startCursor.clone();
  var newNodesCrossed = [];
  var str = '';
  var word = TraverseUtil.getPreviousWord(
      wordStartCursor, wordEndCursor, newNodesCrossed);
  if (word == null)
    return null;
  endCursor.copyFrom(wordEndCursor);

  // Always add the first word when the string is empty, and then keep
  // adding more words as long as breakBefore returns false
  while (!str || !breakBefore(str, word, newNodesCrossed)) {
    // Prepend this word, set the start cursor to the start of this word, and
    // update the returned list of nodes crossed to include ones we crossed
    // in reaching this word.
    if (str)
      str = ' ' + str;
    str = word + str;
    nodesCrossed = nodesCrossed.concat(newNodesCrossed);
    startCursor.copyFrom(wordStartCursor);
v
    // Get the previous word and go back to the top of the loop.
    newNodesCrossed = [];
    word = TraverseUtil.getPreviousWord(
        wordStartCursor, wordEndCursor, newNodesCrossed);
    if (word == null)
      return str;
  }

  return str;
};


text:
import React from 'react';
import PropTypes from 'prop-types';
import Link from './../elements/Link';
import { newPostsPath, contactsPath } from './../../helpers/routes/paths.js';

const MainLayout = ({ children }) => (

    <Logo />
    {children}

);

MainLayout.propTypes = {
  children: PropTypes.node
};

const Logo = () => (

    <Link to='/'>
      Blog
    </Link>

    <Link to={contactsPath}>
      Contacts
    </Link>

    <Link to={newPostsPath}>
      Create post
    </Link>

);

const Footer = () => (

    Powered by React.

);

export default MainLayout;


text:
DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
                              port,
                              9661,
                              "The port the MASQUE server will listen on.");

DEFINE_QUIC_COMMAND_LINE_FLAG(
    std::string,
    cache_dir,
    "",
    "Specifies the directory used during QuicHttpResponseCache "
    "construction to seed the cache. Cache directory can be "
    "generated using `wget -p --save-headers <url>`");

DEFINE_QUIC_COMMAND_LINE_FLAG(
    std::string,
    server_authority,
    "",
    "Specifies the authority over which the server will accept MASQUE "
    "requests. Defaults to empty which allows all authorities.");

int main(int argc, char* argv[]) {
  const char* usage = "Usage: masque_server [options]";
  std::vector<std::string> non_option_args =
      quic::QuicParseCommandLineFlags(usage, argc, argv);
  if (!non_option_args.empty()) {
    quic::QuicPrintCommandLineFlagHelp(usage);
    return 0;
  }

  auto backend = std::make_unique<quic::MasqueServerBackend>(
      GetQuicFlag(FLAGS_server_authority), GetQuicFlag(FLAGS_cache_dir));

  auto server = std::make_unique<quic::MasqueEpollServer>(backend.get());

  if (!server->CreateUDPSocketAndListen(quic::QuicSocketAddress(
          quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) {
    return 1;
  }

  std::cerr << "Started MASQUE server" << std::endl;
  server->HandleEventsForever();
  return 0;
}


text:
/* ** GENEREATED FILE - DO NOT MODIFY ** */
package com.wilutions.mslib.msforms.impl;
import com.wilutions.com.*;

@SuppressWarnings("all")
@CoClass(guid="{BB9597AC-0B0F-CA8F-02E8-694DA5A0B6B5}")
public class TabStripEventsImpl extends Dispatch implements com.wilutions.mslib.msforms.TabStripEvents {
  @DeclDISPID(3)  public void onBeforeDragOver(final Integer Index, final com.wilutions.mslib.msforms.ReturnBoolean Cancel, final com.wilutions.mslib.msforms.DataObject Data, final Float X, final Float Y, final com.wilutions.mslib.msforms.fmDragState DragState, final com.wilutions.mslib.msforms.ReturnEffect Effect, final Short Shift) throws ComException {
    assert(Index != null);
    assert(X != null);
    assert(Y != null);
    assert(DragState != null);
    assert(Shift != null);
    this._dispatchCall(3,"BeforeDragOver", DISPATCH_METHOD,null,Index,Dispatch.param(Cancel),Dispatch.param(Data),X,Y,DragState.value,Dispatch.param(Effect),Shift);
  }
  @DeclDISPID(4)  public void onBeforeDropOrPaste(final Integer Index, final com.wilutions.mslib.msforms.ReturnBoolean Cancel, final com.wilutions.mslib.msforms.fmAction Action, final com.wilutions.mslib.msforms.DataObject Data, final Float X, final Float Y, final com.wilutions.mslib.msforms.ReturnEffect Effect, final Short Shift) throws ComException {
    assert(Index != null);
    assert(Action != null);
    assert(X != null);
    assert(Y != null);
    assert(Shift != null);
    this._dispatchCall(4,"BeforeDropOrPaste", DISPATCH_METHOD,null,Index,Dispatch.param(Cancel),Action.value,Dispatch.param(Data),X,Y,Dispatch.param(Effect),Shift);
  }
  @DeclDISPID(2)  public void onChange() throws ComException {
    this._dispatchCall(2,"Change", DISPATCH_METHOD,null);
  }
  @DeclDISPID(-600)  public void onClick(final Integer Index) throws ComException {
    assert(Index != null);
    this._dispatchCall(-600,"Click", DISPATCH_METHOD,null,Index);
  }
  @DeclDISPID(-601)  public void onDblClick(final Integer Index, final com.wilutions.mslib.msforms.ReturnBoolean Cancel) throws ComException {
    assert(Index != null);
    this._dispatchCall(-601,"DblClick", DISPATCH_METHOD,null,Index,Dispatch.param(Cancel));
  }
  @DeclDISPID(-608)  public void onError(final Short Number, final com.wilutions.mslib.msforms.ReturnString Description, final Integer SCode, final String Source, final String HelpFile, final Integer HelpContext, final com.wilutions.mslib.msforms.ReturnBoolean CancelDisplay) throws ComException {
    assert(Number != null);
    assert(SCode != null);
    assert(Source != null);
    assert(HelpFile != null);
    assert(HelpContext != null);
    this._dispatchCall(-608,"Error", DISPATCH_METHOD,null,Number,Dispatch.param(Description),SCode,Source,HelpFile,HelpContext,Dispatch.param(CancelDisplay));
  }
  @DeclDISPID(-602)  public void onKeyDown(final com.wilutions.mslib.msforms.ReturnInteger KeyCode, final Short Shift) throws ComException {
    assert(Shift != null);
    this._dispatchCall(-602,"KeyDown", DISPATCH_METHOD,null,Dispatch.param(KeyCode),Shift);
  }
  @DeclDISPID(-603)  public void onKeyPress(final com.wilutions.mslib.msforms.ReturnInteger KeyAscii) throws ComException {
    this._dispatchCall(-603,"KeyPress", DISPATCH_METHOD,null,Dispatch.param(KeyAscii));
  }
  @DeclDISPID(-604)  public void onKeyUp(final com.wilutions.mslib.msforms.ReturnInteger KeyCode, final Short Shift) throws ComException {
    assert(Shift != null);
    this._dispatchCall(-604,"KeyUp", DISPATCH_METHOD,null,Dispatch.param(KeyCode),Shift);
  }
  @DeclDISPID(-605)  public void onMouseDown(final Integer Index, final Short Button, final Short Shift, final Float X, final Float Y) throws ComException {
    assert(Index != null);
    assert(Button != null);
    assert(Shift != null);
    assert(X != null);
    assert(Y != null);
    this._dispatchCall(-605,"MouseDown", DISPATCH_METHOD,null,Index,Button,Shift,X,Y);
  }
  @DeclDISPID(-606)  public void onMouseMove(final Integer Index, final Short Button, final Short Shift, final Float X, final Float Y) throws ComException {
    assert(Index != null);
    assert(Button != null);
    assert(Shift != null);
    assert(X != null);
    assert(Y != null);
    this._dispatchCall(-606,"MouseMove", DISPATCH_METHOD,null,Index,Button,Shift,X,Y);
  }
  @DeclDISPID(-607)  public void onMouseUp(final Integer Index, final Short Button, final Short Shift, final Float X, final Float Y) throws ComException {
    assert(Index != null);
    assert(Button != null);
    assert(Shift != null);
    assert(X != null);
    assert(Y != null);
    this._dispatchCall(-607,"MouseUp", DISPATCH_METHOD,null,Index,Button,Shift,X,Y);
  }
  public TabStripEventsImpl(String progId) throws ComException {
    super(progId, "{7B020EC7-AF6C-11CE-9F46-00AA00574A4F}");
  }
  protected TabStripEventsImpl(long ndisp) {
    super(ndisp);
  }
  public String toString() {
    return "[TabStripEventsImpl" + super.toString() + "]";
  }
}


text:
class CreateTiposPregunta < ActiveRecord::Migration
  def change
    create_table :tipos_pregunta do |t|
      t.string :nombre, limit: 45, null: false
      t.string :valor, limit: 45, null: false
      t.string :valor_html, limit: 45, null: false
    end
  end
end


text:
class RemoveEmailIndexFromUser < ActiveRecord::Migration
  def self.up
    remove_index :users, 'email'
    add_index :users, 'uid', unique: true
  end

  def self.down
    remove_index :users, 'uid'
    add_index :users, 'email', unique: true
  end
end


text:
package org.neo4art.importer.wikipedia.parser;

import java.text.SimpleDateFormat;

import org.junit.Assert;
import org.junit.Test;
import org.neo4art.domain.Artist;

public class WikipediaArtistInfoboxTest {

	private static String INFOBOX =
		"{{Infobox artist\n" +
        "| bgcolour      = #FBEC5D\n" +
        "| name          = Vincent van Gogh\n" +
        "| image         = Vincent van Gogh - Self-Portrait - Google Art Project (454045).jpg|\n" +
        "| caption       = ''Self-Portrait'', Spring 1887, Oil on pasteboard, 42 x 33.7 cm., [[Art Institute of Chicago]] (F 345)\n" +
        "| alt           = An intense man with close cropped hair and red beard gazes to the left.\n" +
        "| birth_date    = 30 March 1853\n" +
        "| birth_place   = [[Zundert]], [[Netherlands]]\n" +
        "| death_date    = {{Death date and age|df=yes|1890|7|29|1853|3|30}}\n" +
        "| death_place   = [[Auvers-sur-Oise]], [[French Third Republic|France]]\n" +
        "| field         = Painting, drawing\n" +
        "| training      = [[Anton Mauve]]\n" +
        "| movement      = [[Post-Impressionism]]\n" +
        "| works         =  ''[[The Starry Night|Starry Night]]'', ''[[Sunflowers (Van Gogh series)|Sunflowers]]'', ''[[Bedroom in Arles]]'', ''[[Portrait of Dr. Gachet]]'', ''[[Sien (Van Gogh series)|Sorrow]]''\n" +
        "| patron        = [[Theo van Gogh (art dealer)|Theo van Gogh]]}}";

	@Test
	public void shouldParseArtistInfobox() {
	  SimpleDateFormat formatDate = new SimpleDateFormat("yyyy M dd");

	  Artist artist = WikipediaArtistInfoboxParser.parse(INFOBOX);

		Assert.assertEquals("Vincent van Gogh", artist.getName());
		Assert.assertEquals("1853 3 30", formatDate.format(artist.getBirthDate().getTime()));
		Assert.assertEquals("Zundert, Netherlands", artist.getBirthPlace());
		Assert.assertEquals("1890 7 29", formatDate.format(artist.getDeathDate().getTime()));
		Assert.assertEquals("Auvers-sur-Oise", artist.getDeathPlace());
	}
}