やったもん勝ち

主にプログラミングのこと。生産性向上の某とかも。

AWS EMRを使ったhiveのチュートリアル

やりたいこと

以下のような3つの項目を持ったjsonファイルがあります。

id, cookie, date

基本的にはidはcookieと一対一の関係にありますが、たまにあるcookieに対して同じidが振られていることがあります。 つまり、cookieは完全にユニークですが、idには重複があります。 困りました。

この重複をなくして、idとcookieともに完全にユニークなデータセットにしたいと思います。 idが同じオブジェクトあったときは、dateを参照して新しい方のcookieを残して古い方のcookieは無視します。 同じ日に一つのidに対して異なるcookieが生成されることはないものとします。

データはS3から読み込んでS3に保存します。 入力 s3://hive-00001111-ap-northeast-1/Cookie_origin/の下に、複数ファイルに別れたtsvファイルがあります。 出力 s3://hive-00001111-ap-northeast-1/Cookie_unique/以下に保存したいです。

データセット生成

https://www.json-generator.com/ このサイトで以下のような条件で100レコード分のjsonファイルを生成して、indentをcompactでダウンロードします。

[
  '{{repeat(100, 100)}}',
  {
    _id: '{{objectId()}}',
    cookie: '{{guid()}}',
    date: '{{date(new Date(1000, 0, 1), new Date(), "YYYYMMdd")}}'
  }
]

generated.jsonという名前でダウンロードされました。

若干テキストエディタで整形します。 まずは、先頭と最後の[]を削除します。 次に },{

}
{

で置換します。これで、いい感じの元データができました。 試しに先頭の5行だけ抜き出してみます。

{"_id":"5a02dcbb94b8be7f1a0fe41e","cookie":"c8be7285-608c-4158-86c6-0c8e03a5a8d3","date":20160720}
{"_id":"5a02dcbbec03abdc3585ba76","cookie":"b744804e-0423-4283-995a-843ce861f305","date":20130423}
{"_id":"5a02dcbb47fe699b5d4f1b4f","cookie":"1fbec6e8-b6a7-41f7-99a2-708e04f86ab8","date":20170912}
{"_id":"5a02dcbb5b3d47ca9c229706","cookie":"31216342-1d61-4f2b-8315-755b163ee225","date":20120325}
{"_id":"5a02dcbbfeb00508901f7261","cookie":"0415a1e9-19c8-4a77-a4c6-7aa85fff733a","date":20140928}

こんな感じです。 このファイルから5件だけidのvalueをコピーして他の行のidのvalueにしておきます。

s3の構成

s3のファイル構成は以下のようにします。

S3
└── hive-00001111-ap-northeast-1
    ├── Cookie_origin
    │   └── generated.json
    ├── Cookie_unique
    └── src
        └── uniquify_id.q

generated.jsonはダウンロードしたファイルです。 uniquify_id.qは後ほど説明します。

HiveQL

HiveはHiveQLというSQLによく似たクエリを使っています。 そんなにSQLに詳しくないですが、SQLでできる基本的なことはほとんどできるっぽいです。

uniquify_id.q

-- --------------------------------------------------
-- 使用するテーブルを作成
-- --------------------------------------------------

CREATE EXTERNAL TABLE IF NOT EXISTS id_origin(line STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' LOCATION 's3://hive-00001111-ap-northeast-1/Cookie_origin';

CREATE EXTERNAL TABLE IF NOT EXISTS id_unique ( id STRING, cookie STRING, get_date STRING)ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n' LOCATION 's3://hive-00001111-ap-northeast-1/Cookie_unique';


-- --------------------------------------------------
-- Jsonをパースする
-- --------------------------------------------------

INSERT INTO TABLE id_unique SELECT json_data.* FROM id_origin LATERAL VIEW json_tuple(id_origin.line, '_id','cookie', 'date') json_data AS id, cookie, get_date;


-- --------------------------------------------------
-- idとcookieを一対一にする。
-- --------------------------------------------------

INSERT OVERWRITE TABLE id_unique SELECT A.id, A.cookie, A.get_date FROM id_unique AS A INNER JOIN  (SELECT id,MAX(get_date) AS NEWEST FROM id_unique GROUP BY id) AS B ON A.id = B.id AND A.get_date = B.NEWEST;

細かいHiveQLの書き方は以下のサイトをとても参考にさせていただきました。 http://www.ne.jp/asahi/hishidama/home/tech/apache/hive/ql.html

AWS EMR

クラスター作成

ここからEMRに操作です。

まずはコンソールでEMRを選択します。

クラスタータブから、「クラスターを作成」

以下のように設定します。 f:id:benzenetarou:20171119182329p:plain 基本はデフォルトのままで。 クラスター名は適当。 EC2キーペアはいつもの奴を選択。 クラスターを作成。

ステップ追加

続いてステップを追加していきます。 ステップタブから、「ステップの追加」 f:id:benzenetarou:20171119182355p:plain

ステップタイプはHiveプログラム スクリプト場所は、s3://hive-00001111-ap-northeast-1/src/uniquify_id.q を指定して、作成。

しばらく待ちます。

今回のデータ量だと1,2分待てば良さそうです。 しばらく待ってから更新をかけます。 ステータスが完了になっていたら成功です。

確認

S3の保存先を確認してみます。 Cookie_uniqueフォルダと同じ階層にCookie_unique_$folder$というファイルができていますが、これはhiveのテーブルとCookie_unique/のデータを結びつけているものです。特に理由がなければそのままにしておくのが吉な気がします。

Cookie_uniqueの中に000000_0というファイルが生成されています。これが欲しかったデータです。 ダウンロードして確認してみます。 idが同一のデータを5ペア作ったので、95行になっているか確認します。

$ wc 000000_0  
95 285 6745 000000_0

うまくいってそうです。

これにてユニーク化終了です。お疲れ様でした。

なんかうまくいかないってときは、ターミナル等から直接操作もできます。

クラスター > (クラスターを選択する) > ハードウェア > ノードタイプがMASTERのやつを選択

パブリックIPアドレスから普通にsshでログインできます。 ユーザー名はhadoopでログインします。(ec2-userにしちゃうとhiveが使えない)

こんなのが表示されればOKです。

`$ hive' でhiveに入れます。 ここからはMySQLとかと同じ要領でできます。

というより最初からターミナルからSQL叩いて、うまく行ったのを一つづつスクリプトにしました。f:id:benzenetarou:20171119182802p:plain

benzenetarou.hatenablog.com

AWSのEMRを使ってHiveの基本的な使い方を確認しておく備忘録

Hiveとは?

Hiveとは?

Hive(ハイブ)とは、オープンソースの大規模分散計算フレームワークHadoop上で動作するデータウェアハウス(DWH)向けのプロダクトです。

Hiveとは | クラウド・データセンター用語集/IDCフロンティア

とあります。
大規模なデータ処理をするときに使うMySQLみたいなやつ。...だと勝手に思ってます。

実際HiveのためのHiveQLという言語は、SQLによく似ています。

Hiveを起動するまで

AWSネジメントコンソールからEMRを作成。 マスターノードのEC2インスタンスのipを調べて、sshでログイン。
ユーザー名はhadoopでログインします。
$ ssh -i path/to/your/ssh_key hadoop@[ip address]
ログインしてから$ hiveでHiveを使えるようになっています。

Hive

データベース一覧を表示

まずはデータベースを確認してみる。
SHOW DATABASES;

データベース作成

companyというデータベースを作成する。
CREATE DATABASE company;

作成されていることを確認します。
SHOW DATABASES;

今回、Hiveで扱いたいのは、EMPLOYEEという名前のファイルがjson形式でS3に保存してあるとしましょう。
保存場所はs3://mybucket/EMPLOYEE
中身は、以下です。

{"name":"tanaka","age":24,"section":"sales"}
{"name":"yamada","age":45,"section":"engineer"}
{"name":"yamamoto","age":34,"section":"engineer"}
{"name":"inoue","age":33,"section":"sales"}
{"name":"komori","age":22,"section":"engineer"}
{"name":"takahashi","age":40,"section":"admin"}

テーブルを作成

Hiveでデータの挿入は、1行ずつ入れるみたいなことができないらしい。
ファイルを全部読み込みバルクインサートっていう方法でしかインポートできないって書いてあった気がしました。
なので、予めファイルを作っておいて、それを読み込む形にしておきます。
と、データのインポートの前に、テーブルを定義しておきます。

データの流れとして、
1. カラムが一つのテーブルを作成し、1行1jsonとして格納します
2. jsonをパースして、3つのカラムに分けて新しい別のテーブルに格納します。

json格納用の一時テーブル

CREATE TABLE emp_line(
  line STRING
);

最終的に扱いたいテーブルをつくります

CREATE TABLE employee (
    name STRING,
    age INT,
    section STRING
);

テーブル一覧を表示

テーブルが作成されているか一覧で見てみましょう。
SHOW TABLES;

テーブルの構造を表示

カラム等の定義を確認することもできます。
DESC employee;

jsonファイルを事前に用意

s3://bucket-name/path/to/your/file/employee.json

{"name":"tanaka","age":24,"section":"sales"}
{"name":"yamada","age":45,"section":"engineer"}
{"name":"yamamoto","age":34,"section":"engineer"}
{"name":"inoue","age":33,"section":"sales"}
{"name":"komori","age"22:,"section":"engineer"}
{"name":"takahashi","age":40,"section":"admin"}

s3からデータをロード

データをロードして追加するなら以下
LOAD DATA INPATH 's3://mybucket/EMPLOYEE' INTO TABLE emp_line;

追加ではなく、上書きするなら、OVERWRITEをつけます。
LOAD DATA INPATH 's3://mybucket/EMPLOYEE' OVERWRITE INTO TABLE emp_line;

この状態だと、emp_lineテーブルに1カラム1jsonとして格納されている。

SELECT * FROM emp_line;をすると

OK
{"name":"tanaka","age":24,"section":"sales"}
{"name":"yamada","age":45,"section":"engineer"}
{"name":"yamamoto","age":34,"section":"engineer"}
{"name":"inoue","age":33,"section":"sales"}
{"name":"komori","age"22:,"section":"engineer"}
{"name":"takahashi","age":40,"section":"admin"}
Time taken: 1.4 seconds, Fetched: 6 row(s)

となる。

シンプルなjsonをパース

jsonをパースするには、次のようにします。

SELECT emp.* FROM emp_line 
    LATERAL VIEW json_tuple(
    emp_line.line,
    'name',
    'age',
    'section'
    ) emp AS name, age, section;

とすると、

OK
tanaka  24  sales
yamada  45  engineer
yamamoto    34  engineer
inoue   33  sales
komori  22  engineer
takahashi   40  admin
Time taken: 0.092 seconds, Fetched: 6 row(s)

となる。これを別のテーブルに格納すれば完成。
employeeテーブルに格納するには

INSERT INTO TABLE employee
SELECT emp.* FROM emp_line 
    LATERAL VIEW json_tuple(
    emp_line.line,
    'name',
    'age',
    'section'
    ) emp AS name, age, section;

と、SELECTの結果をINSERT INTO TABLE [table名]として新たに代入するだけです。

ネストしたjsonをパース

余談ですが、jsonがネストしているパターンを考えてみます。
ネストしているjsonを用意する

{"name":{"str":"tanaka"},"age":{"int":24},"section":{"str":"sales"}}
{"name":{"str":"yamada"},"age":{"int":30},"section":{"str":"engineer"}}
{"name":{"str":"katou"},"age":{"int":50},"section":{"str":"admin"}}
{"name":{"str":"yamamoto"},"age":{"int":44},"section":{"str":"sales"}}
{"name":{"str":"suzuki"},"age":{"int":29},"section":{"str":"engineer"}}
{"name":{"str":"hirose"},"age":{"int":48},"section":{"str":"sales"}}

シンプルなjsonと同じように記述する

SELECT data.* FROM emp_line
    LATERAL VIEW json_tuple(
        emp_line.line,
        'name',
        'age',
        'section',
    ) data AS name, age, section;

すると

OK
{"str":"tanaka"}  {"int":24}  {"str":"sales"}
.
.
.

となり、余計な文字が残ってしまう。

もう一つネストを外したい。

参考: https://qiita.com/unksato/items/42405305c28e5a788cd7

literal viewを連続で使う。

SELECT data.* FROM emp_line
    LATERAL VIEW json_tuple(
        emp_line.line,
        'name',
        'age',
        'section'
    ) json_data AS name, age, section
    lateral view json_tuple(
        json_data.name,
        'str'
    ) data as name
    lateral view json_tuple(
        json_data.age,
        'int'
    ) data as age
    lateral view json_tuple(
        json_data.section,
        'str'
    ) data as section;

これで正しく出力される。

勝手に意味を補ってみる。

SELECT data.* FROM emp_line[(後に作られる仮の)dataテーブルのすべてのカラムを選択][元になるのはemp_lineテーブル]
    LATERAL VIEW json_tuple(
        emp_line.line,[emp_lineテーブルのlineカラムをjsonパース]
        'name',[nameがキーのものを]
        'age',[ageがキーのものを]
        'section',[sectionがキーのものを]
    ) json_data AS name, age, section[仮のjson_dataテーブルにname,age,sectionというカラムで生成する。]
    lateral view json_tuple(
        json_data.name,[json_dateテーブルのnameカラムをjsonパース]
        'str'[sがキーのものを]
    ) data as name[仮のdataテーブルにnameというカラムで生成する。]
    [以下重複]
    lateral view json_tuple(
        json_data.age,
        'int'
    ) data as ttl
    lateral view json_tuple(
        json_data.section,
        'str'
    ) data as section;

ちなみにカラム名にdateを使うと、なんかだめっぽい。

整形先のテーブルを作成

create table emp_all(
    name string,
    age int,
    section string,
);

データのインサート

insert into table emp_all
SELECT data.* FROM emp_line
    LATERAL VIEW json_tuple(
        emp_line.line,
        'name',
        'age',
        'section',
    ) data AS name, age, section;

重複データを無視してSELECT

すべてのカラムが重複するデータを削除して表示する。

select distinct *  from emp_all;

これで、同一データはなくして軽くできる。

こんな感じで基本的な操作はMySQLで使えるものはほとんど使えるっぽい。

ビットコインの自動売買プログラムを作る①APIいじってみる〜

bitflyerのAPIを使ってビットコインの売買が結構かんたんにできちゃいます。

以下のページから使いたいAPIを探します。 全部日本語で書いてあるので、嬉しいですね。

ビットコイン取引所【bitFlyer Lightning】

こちらでサンプルコードが載っているので、ほぼまんまコピペで簡単にAPIを使えちゃえます。
これは至れり尽くせり。

ビットコイン取引所【bitFlyer Lightning】

sendchildorder.rb

require "net/http"
require "uri"
require "openssl"
require "./key"

key = API_KEY
secret = API_SECRET

timestamp = Time.now.to_i.to_s
method = "POST"
uri = URI.parse("https://api.bitflyer.jp")
uri.path = "/v1/me/sendchildorder"
body = '{
  "product_code": "BTC_JPY",
  "child_order_type": "LIMIT",
  "side": "BUY",
  "price": 740000,
  "size": 0.001,
  "minute_to_expire":1200 ,
  "time_in_force": "GTC"
}'

text = timestamp + method + uri.request_uri + body
sign = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, text)

options = Net::HTTP::Post.new(uri.request_uri, initheader = {
  "ACCESS-KEY" => key,
  "ACCESS-TIMESTAMP" => timestamp,
  "ACCESS-SIGN" => sign,
  "Content-Type" => "application/json"
});
options.body = body

https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
response = https.request(options)
puts response.body

key.rb

API_KEY = "XXXXXXXXXX"
API_SECRET = "XXXXXXXXXXXXXXXXXXXX"

これで740,000円で買い注文が出せました。

調整する部分は

 "product_code": "BTC_JPY",  # BTCとJPY
  "child_order_type": "LIMIT", # 指値注文
  "side": "BUY", # 買い注文
  "price": 740000, # 740,000円
  "size": 0.001, # 0.001BTC
  "minute_to_expire":1200 , # 有効期限 1200分
  "time_in_force": "GTC" 

の部分だけです。

これをうまいことチューニングするだけで果たして儲かるのでしょうか〜

次回は、価格がどんな風に動いているのか、できるだけ細かくログを取ってみます

rubyとseleniumでTwitterに自動ログインする。〜リベンジ編

SeleniumTwitterの自動ブラウザ操作を行っていきたいです。
以前設定していたのですが、しばらくしてみると、そのコードでは対応できなくなっていました。

require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "https://twitter.com/login"

ここまでで以下の画面になります。(firefoxのdriverなどの設定が別途必要だった気がしますが)

f:id:benzenetarou:20171003222021p:plain

このメールアドレスとパスワードに値を自動で挿入してログインボタンをクリックしてログインしたいと思います。
まずは、フォームから。
開発者ツールで、この要素を見てみると、次のようになっています。

f:id:benzenetarou:20171003222056p:plain
要素は

<input class="js-username-field email-input js-initial-focus" name="session[username_or_email]" autocomplete="on" value="" placeholder="電話番号/メールアドレス/ユーザー名" type="text">

です。
ここで、seleniumの書き方に従って、 (classを指定する方法は確か失敗した気がします。。。もしくは一意なclassがなかったか。)

driver.find_element(:name, 'session[username_or_email')..send_key "user_name"

としても、

Selenium::WebDriver::Error::ElementNotInteractableError: Element is not visible

となり、エラーになってしまいました。
f:id:benzenetarou:20171003222301p:plain
qiitaでやってみたらうまく行ったので、TwitterCSSの問題なのか、それとも単にハッシュみたいな書き方に対応していないのか。
と思ったら、以前書いてたコードでは普通に

driver.find_element(:name, "session[password]").send_key "password" 

のような形で書いていたので、おそらくCSS側の問題ですね。

解決策として、xpathを指定してあげたら、どうもうまくいきました。

xpathは開発者ツールで秒速で見つけられます。 f:id:benzenetarou:20171003222817p:plain
我らがfirefoxですね。
...おっと、firefoxではできませんでした。(探せば見つかるはずだけどめんどい)
ということで、我らがchrome
f:id:benzenetarou:20171003222940p:plain
与沢翼も裸足で逃げ出すくらい秒速でした。

    driver.find_element(:xpath, '//*[@id="page-container"]/div/div[1]/form/fieldset/div[1]/input').send_key "user_name"
    driver.find_element(:xpath, '//*[@id="page-container"]/div/div[1]/form/fieldset/div[2]/input').send_key "password"
    driver.find_element(:xpath, '//*[@id="page-container"]/div/div[1]/form/div[2]/button').click

こんな感じでログイン成功しました。

あとは煮るなり焼くなり好きにして状態ですね。

自動ファボ&RTのコード下の記事にあります。

benzenetarou.hatenablog.com

SeleniumとRubyでTwitterの複数アカウントを自動ファボ&RT

背景

自分の趣味というか、ちょっとしたプロジェクトでTwitterを運営していて、特定のワードで検索されたときに上位に表示させたいなーと思うことがありました。
一つの方法が、ツイートを頻繁にするということで、それはbotで普通に解決できました。これはノンプログラマーでもできますね

bot化したのはだいぶ前だったので、正確には覚えていませんが、確かココらへんを使ってた思い出があります。というか今も使ってるはずですが忘れて確認するのが面倒くさいだけです。

MAKEBOT - ツイッターボット作成サービス

twittbot - enjoy

楽ボッツ - 初心者向き twitter bot(ボット)サービス

ほんといいサービス達です。

で、Twitterで検索したときは、この画面がまず出るわけです。 f:id:benzenetarou:20170907231557p:plain

検索ワードによって少々構成が変わることもあったりはするみたいですが、基本こんな感じで、まず一番に表示されるのは話題のツイートです。
ここではツイートまたはユーザーが表示されます。
僕の独自の調査では、この表示されるのに有効な要素は、

  • フォロワー数
  • Tweetのファボ数
  • TweetのRT数

ですね。普通か。

ということで、ファボ&RT数を増やしたい。
それも自分の内々のアカウント群で。

どうやら調べてみると、なんとかマティックっていう怪しい有料のツールはあるみたいですが、それはなんか使いたくなかったので、自作してそれっぽいことをすることに。
いや〜成長しましたね〜 2年前の僕は必死にそういうサービスがないか検索しまくるのが関の山でした。
ということで、余談おしまいです。

APIは使えないのか?

TwitterにはもちろんAPIがありますが、ざっと調べた感じ、自動ファボはできなかったような気がします…
前はできたみたいなんですけどね
もしかしたら普通にできたかも。
Seleniumとかスクレイピングの練習ってことで!

コード

ちょっと前に実装したので、driverのinstallのあたりで若干苦戦した気もしますが、忘れてしまいました。

基本的には実際にTwitterのページを開いて開発者ツールでHTMLコード見て、ゴリゴリにチューニングしてどうにかこうにか動くって感じです。
結構Twitterは罠が多いです。ログインのところとか結構罠でした。
そういう理由から、チューニングしきれず、ちょいちょいエラーになりそうなのを強引にその後の処理を捨ててるので、精度はそこまで高くないです。
回り続けることを優先しています。

# execute `$ watch -n 1800 ruby path/to/this/project/auto_favo_RT_with_selenium.rb` to run every 30 minutes
require "selenium-webdriver"
user_names = [
  "account1",
  "account2",
  "account3"
]


for user_name in user_names do
  driver = Selenium::WebDriver.for :firefox

  driver.get "https://twitter.com"

  # ログインする
  sleep 1
  begin
    driver.find_element(:class, "js-login").click
    driver.find_element(:name, "session[username_or_email]").send_key "#{user_name}"
    driver.find_element(:name, "session[password]").send_key "password" #パスワードは全部同じとして
    driver.find_element(:class, "js-submit").click
  rescue
    driver.quit
    puts "login error"
    next
  end
  puts "login with #{user_name}"

  # 検索してファボ&RT
  driver.get "https://twitter.com/search?f=tweets&vertical=default&q=ANYWORD&src=typd"
  for i in 0..10 do
    begin
      driver.find_elements(:class, "js-actionFavorite")[i*2].click
      driver.find_elements(:class, "js-actionRetweet")[i*2].click
      driver.find_element(:class, "RetweetDialog-retweetActionLabel").click
    rescue
      puts "Error: in #{i}"
      break
    end
    sleep 2
  end
driver.quit
end

同じコードですがgithubに上げときます。 github.com

これをMacのターミナルから実行させときます。
$ watch -n 1800 ruby path/to/this/project/auto_favo_RT_with_selenium.rb
これだと1800秒ごと、30分ごとに実行されます。
これは自分のbotのツイート数と併せてこのくらいの感覚でいいなって思って設定しました。
30分ごとに10個ずつふぁぼっていくので、そのくらいのペースということです。
重複しそうになったら、そこでそのループは終了します。

では、悪用しないで楽しいTwitterライフを送りましょう!!!

課題

一つだけ問題点があって、普通にパソコンを使っていると、30分毎にお気にFirefoxが出現して、最前面でブラウザ操作を行っていくことです。
ちょっとうっとうしい。
次のステップとしては、フォローアンフォローも自動化していきたいです。
あとAWSとか使ってやりたい。
ブラウザ操作なんて不細工なことはしたくないな……

一つのPCから複数のgithubアカウントを使い分けてpushする

一つのPCから複数のgithubアカウントを使い分けてpushする ということをやってみたいと思いました。

背景

というのも、仕事とプライベートみたいな使い分け方じゃなく、開発用のアカウントと、英語学習用のアカウントを使い分けたいなと思ったからです。
日々の学んだことや、日記的なことを英語でアウトプットしていきたいなと思いまして、何かないかなーと。
メモやテキストに書いてるだけなのもなんかなーと思いまして。あと、オンラインで管理したい。
じゃあブログでいいじゃないかと思ったわけですが、ブログだと、検索ができない。(と思ったのですが、普通に検索できますね、ブログでも良かったかもしれない。しかしせっかくエンジニアになったので、エンジニアっぽいことをやってみようじゃないかと)
一つのディレクトリにまとめとけば、エディタ開いて検索とかできるなと。ブログより素早くアクセスできるなと。(しかしgithub嬢では検索できないかぁ〜) ということで、一つのリポジトリで管理しようと思いました。
これの何がいいって、githubで草を生やせること! これだけのためにgithubを使うと言っても過言でもない ちゃんと習慣としてできているかが一目瞭然になること!これがいい。
というわけで、githubを使っていく運びになりました。

やりたいこと

特定のリポジトリからpushするときだけは勝手に別のgithubアカウントのリポジトリにしてくれる。

やったこと

githubアカウントを作る

もう一つのgithubアカウントを作ります。普通に。

sshキーを登録する

新しく作ったアカウントのために新しくsshキーを作成します。

$ ls ~/.ssh
id_rsa  id_rsa.pub #普段使いのやつ

$ ssh-keygen -t rsa -C hoge@hoge.com -f fugafuga_rsa
#パスフレーズなどは適当に。ファイル名は既存の鍵と被って上書きしないように。

githubから鍵を登録します。 fugafuga_rsa.pubgithubに登録。

githubリポジトリを作成

作る。

configの設定

$ vi ~/.ssh/config

Host github.com-for-English
  User git
  Port 22
  HostName github.com
  IdentityFile ~/.ssh/fugafuga_rsa
  TCPKeepAlive yes
  IdentitiesOnly yes

を追加。(ここら編怪しいです。)

git clone

$ git clone git@github.com:English/practice/listening.gitとするところを $ git clone git@github.com-for-English:English/practice/listening.gitとする。

user情報を変更

このままだと、普段のアカウントのままなので、

$ git config --local user.name "アカウント名"
$ git config --local user.email "メールアドレス"

とする。

commit $ push

これで晴れてremoteにpushできました〜

苦労したぜ…

will_paginateの1ページあたりの表示数とラベルを変更する

ページネーションをうまいことやりたいとき、railsだとどうやらkaminariってのが有名らしい。

しかしrailsチュートリアルで、will_paginateを使っていたので、それを活用していきます。

参考までにkaminari github.com

そして今回のwill_paginate github.com

余談ですけど、kaminariとかnokogiriとか4文字の日本語をローマ字にしましたみたいなgemってなんか良いですよね。
僕も将来onigiriってgem作りたいなと構想しています。

1ページに表示するitemの数を指定する

Post.paginate(:page => params[:page], :per_page => 30)

と、:per_page => 30のようにオプションを付ければできます。

まとめてdefaultの設定もできるようですが、部分ごとに表示させたい数が違ったので、今回は使いませんでした。

# for the Post model
class Post
  self.per_page = 10
end

# set per_page globally
WillPaginate.per_page = 10

labelを変更する

個別にも設定できるみたいなんですが、1ページあたりの表示数と違って、これはサイト全体で統一するパターンが多いと思うので、一括で設定をしたいところです。

config/application.rbにて

config.i18n.default_locale = :ja

で、日本語に設定して、config/locals/ja.yml

ja:
  will_paginate:
    previous_label: "前"
    next_label: "次"
    page_gap: "…"

を設定。