巨人の足元でたじlog

そうして言葉を軽んじるから――― 君は私の言葉を聞き逃す

railsのactiverecordでlastの一つ手前を取得したい。secondの逆をやりたい。

$ rails c
# ユーザー取得
u = User.find(1)

# ユーザーのtweetを取得したい
u.tweets.content

# ユーザーのtweetの最新を取得したい
u.tweets.last.content

# ユーザーのtweetの最初のを取得したい
u.tweets.first.content

# ユーザーのtweetの最初2個目を取得したい
u.tweets.second.content

# ユーザーのtweetの最新の一つ前を取得したい
u.tweets.last(2)[0].content

u.tweets.last(2).content だと構文エラーになる罠があるので注意。
u.tweets.last(2)で取得できるのは、tweetオブジェクトの配列なので、要素指定してあげないとだめ。

railsのbelongs_toとhas_manyとreferencesの使い方について整理する

ややこしいので、一気に整理する。

空のrailsプロジェクトを立ち上げて、

$ be rails g model user name:text

※ alias be='bundle exec'

userテーブルができました。

$ be rails g model tweet content:text references:users

tweetテーブルができました。

こいつらモデルのクラスを確認してみると、 app/models/user.rb

class User < ApplicationRecord
end

app/models/tweet.rb

class Tweet < ApplicationRecord
end

dbマイグレートする

$ be rake db:migrate
== 20180717181025 CreateTweets: migrating =====================================
-- create_table(:tweets)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

undefined method `user' for #<ActiveRecord::ConnectionAdapters::SQLite3::TableDefinition:0x00007ff17cfc1488>
.
,

と、エラーになる。

これは、もしかして順番がよろしくなかったか?

もう一度やり直す。

$ be rails g model user name:text
$ be rake db:migrate
$ be rails g model tweet content:text references:user
$ be rake db:migrate
== 20180717181557 CreateTweets: migrating =====================================
-- create_table(:tweets)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

undefined method `user' for #<ActiveRecord::ConnectionAdapters::SQLite3::TableDefinition:0x00007fc20b59a978>
.
.
.

順番なんとか関係なかったです、すみません。
たぶんuserを複数形にしたら行ける気がする。
そのために、一度2つ目のmigrationファイルはなかったことにする。

$ be rails destroy model tweet
$ be rails g model tweet content:text references:users
$ be rake db:migrate
.
.
undefined method `users' for #<ActiveRecord::ConnectionAdapters::SQLite3::TableDefinition:0x00007f8ab5104ea0>

うーむ。先にreferencesを使わない方法を実装して確認する。

もう一度もとに戻します。

$ be rails destroy model tweet

シンプルなやつ

$ be rails g model tweet content:text user_id:integer
$ be rake db:migrate

成功。
しかしこれでは、ただ単にuser_idというたまたまuserテーブルと関係ありそうな名前のカラムがあるだけで、実際には無関係です。
2つのファイルを次のように変更 app/models/user.rb

class User < ApplicationRecord
  has_many :tweets
end

app/models/tweet.rb

class Tweet < ApplicationRecord
  belongs_to :user
end

そして、rails consoleにて試してみる。
まずは一通り。

be rails c -s
Loading development environment in sandbox (Rails 5.2.0)
Any modifications you make will be rolled back on exit
irb(main):001:0> u = User.create(name:"hoge")
   (0.1ms)  SAVEPOINT active_record_1
  User Create (0.6ms)  INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "hoge"], ["created_at", "2018-07-17 18:34:37.448049"], ["updated_at", "2018-07-17 18:34:37.448049"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 1, name: "hoge", created_at: "2018-07-17 18:34:37", updated_at: "2018-07-17 18:34:37">
irb(main):002:0>
irb(main):003:0>
irb(main):004:0>
irb(main):005:0>
irb(main):006:0> t = Tweet.create(content:"aaaaaaaa", user_id:1)
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Tweet Create (0.2ms)  INSERT INTO "tweets" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "aaaaaaaa"], ["user_id", 1], ["created_at", "2018-07-17 18:35:22.904090"], ["updated_at", "2018-07-17 18:35:22.904090"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Tweet id: 1, content: "aaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:22", updated_at: "2018-07-17 18:35:22">
irb(main):007:0> t2 = Tweet.create(content:"bbbbbaaaaaaaa", user_id:1)
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Tweet Create (0.2ms)  INSERT INTO "tweets" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "bbbbbaaaaaaaa"], ["user_id", 1], ["created_at", "2018-07-17 18:35:29.800640"], ["updated_at", "2018-07-17 18:35:29.800640"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Tweet id: 2, content: "bbbbbaaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:29", updated_at: "2018-07-17 18:35:29">
irb(main):008:0>
irb(main):009:0>
irb(main):010:0>
irb(main):011:0>
irb(main):012:0>
irb(main):013:0>
irb(main):014:0> u.tweets
  Tweet Load (0.2ms)  SELECT  "tweets".* FROM "tweets" WHERE "tweets"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tweet id: 1, content: "aaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:22", updated_at: "2018-07-17 18:35:22">, #<Tweet id: 2, content: "bbbbbaaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:29", updated_at: "2018-07-17 18:35:29">]>
irb(main):015:0>
irb(main):016:0>
irb(main):017:0>
irb(main):018:0>
irb(main):019:0> t.user
=> #<User id: 1, name: "hoge", created_at: "2018-07-17 18:34:37", updated_at: "2018-07-17 18:34:37">

sqlをちょっと読んでみる。 まずは一つ目。Userの作成。

u = User.create(name:"hoge")
   (0.1ms)  SAVEPOINT active_record_1
  User Create (0.6ms)  INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "hoge"], ["created_at", "2018-07-17 18:34:37.448049"], ["updated_at", "2018-07-17 18:34:37.448049"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 1, name: "hoge", created_at: "2018-07-17 18:34:37", updated_at: "2018-07-17 18:34:37">

特に難しいことはしていない。ここは至ってシンプル。
ただ、(?, ?, ?)とかの部分はなんかエスケープしてるんだろうくらいの雰囲気で流している。

続いてTweetの作成。

t = Tweet.create(content:"aaaaaaaa", user_id:1)
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Tweet Create (0.2ms)  INSERT INTO "tweets" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "aaaaaaaa"], ["user_id", 1], ["created_at", "2018-07-17 18:35:22.904090"], ["updated_at", "2018-07-17 18:35:22.904090"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Tweet id: 1, content: "aaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:22", updated_at: "2018-07-17 18:35:22">

なるほど、一度Userの情報を探している。
ちなみに、createの際にuser_idを指定しなかったり、usersテーブルのidに存在しない値を指定すると、失敗し、rollbackする。

userに紐づくtweetを取得する

u.tweets
  Tweet Load (0.2ms)  SELECT  "tweets".* FROM "tweets" WHERE "tweets"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tweet id: 1, content: "aaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:22", updated_at: "2018-07-17 18:35:22">, #<Tweet id: 2, content: "bbbbbaaaaaaaa", user_id: 1, created_at: "2018-07-17 18:35:29", updated_at: "2018-07-17 18:35:29">]>

なるほど、かってにuserテーブルのidをtweetテーブルのuser_idに変換してwhereの条件をかけているわけか。

最後に、Tweetに紐付いたuserの情報

t.user
=> #<User id: 1, name: "hoge", created_at: "2018-07-17 18:34:37", updated_at: "2018-07-17 18:34:37">

はっ!? SQLは発行されないのか!! これはー多分最初にtを作成したときにuser_idを指定していて、そのときにuserの情報がloadされていたので、それを利用しているぽい。

なるほどね、SQLの中身がわかってちゃんと落ち着いて読めばそんなに対してことやってないってことがわかった。
ただし、userテーブルのidをtweetテーブルのuser_idに変換するとか、名前で制約をつけていることが多くて、そこ離れるまでは大変そうだと思ったよ。

referenceを理解する

これと同じことをreferencesを使ってやってみる。
ちょっと改めて調べたところ、さっき自分がやっていたことはとんでもないウンコマンなことだった。

改めて空のrailsプロジェクトから

$ be rails g model user name:text
$ be rails g model tweet content:text user:references
$ be rake db:migrate

userという名前の型がreferencesというわけだ。
先程までのエラーは、referencesというカラムをuserという型で定義しようとしていたために発生したエラーだったのだった。。。ゴミすぎる。笑
モデルのクラスのファイルを見てみる。

app/models/user.rb

class User < ApplicationRecord
end

app/models/tweet.rb

class Tweet < ApplicationRecord
  belongs_to :user
end

おっと、このままの状態だと、Userクラスに何も書いてないが、果たしてこれで動くのか?
rails consoleで確認。

be rails c -s
Loading development environment in sandbox (Rails 5.2.0)
Any modifications you make will be rolled back on exit
irb(main):001:0> u = User.create(name: "hoge")
   (0.1ms)  SAVEPOINT active_record_1
  User Create (0.6ms)  INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "hoge"], ["created_at", "2018-07-18 14:01:54.105305"], ["updated_at", "2018-07-18 14:01:54.105305"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 1, name: "hoge", created_at: "2018-07-18 14:01:54", updated_at: "2018-07-18 14:01:54">
irb(main):002:0>
irb(main):003:0>
irb(main):004:0>
irb(main):005:0>
irb(main):006:0> t = Tweet.create(content: "aaaaaaa", user_id:1)
   (0.1ms)  SAVEPOINT active_record_1
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Tweet Create (0.3ms)  INSERT INTO "tweets" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "aaaaaaa"], ["user_id", 1], ["created_at", "2018-07-18 14:02:19.665391"], ["updated_at", "2018-07-18 14:02:19.665391"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Tweet id: 1, content: "aaaaaaa", user_id: 1, created_at: "2018-07-18 14:02:19", updated_at: "2018-07-18 14:02:19">
irb(main):007:0>
irb(main):008:0>
irb(main):009:0>
irb(main):010:0>
irb(main):011:0> u
=> #<User id: 1, name: "hoge", created_at: "2018-07-18 14:01:54", updated_at: "2018-07-18 14:01:54">
irb(main):012:0> u.tweets
Traceback (most recent call last):
        1: from (irb):12
NoMethodError (undefined method `tweets' for #<User:0x00007f9dc8987f48>)
irb(main):013:0>
irb(main):014:0>
irb(main):015:0>
irb(main):016:0>
irb(main):017:0>
irb(main):018:0> t.user
=> #<User id: 1, name: "hoge", created_at: "2018-07-18 14:01:54", updated_at: "2018-07-18 14:01:54">
irb(main):019:0>

userからtweetを取得するのができない。
やはりuser has_many tweetと定義していないからだ。

しかし、tweet belongs_to userなので、tweetからuserを取得することはできる。

なので、相互からアクセスしたいときには、
app/models/user.rb

class User < ApplicationRecord
  has_many :tweets
end

と、has_manyを追加する必要がある。

ところで、型をreferencesとして定義するメリットは何なんだろう?
確かに、belongs_toは自動で書いてくれたけど、結局has_manyを自分で書かないといけないんだったら、そんなに旨味はないように感じるのだが。。。

外部キーをreferences型カラムで保存する
によると、references型はbelongs_toと同じ意味だと。
references型でテーブル定義をすると、hoge_idというように"_id"をつけなくてもいいらしい。
しかし、これがメリットなのか?

【Rails入門】has_many、belongs_toの使い方まとめ | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト

また、referencesを使った場合はapp/model/castle.rb にbelongs_to :ownerが自動で追加されます。 ですので、使えるなら、いちいち手動で設定するよりもこちらのコマンドを使うよにしてください。

この記事のニュアンスだと、別にreferences使わないとだめってことはないみたい。
そうか、別にreferencesじゃなくてもいいのか、ただreferences使ったほうがちょっと便利でラクできるよ!ってことらしい。

そういう理解にしておいて先に進もう。
と、最後にdb/schema.rbだけは確認しておきたい。 referencesを使ってmigrationファイルを作成した場合。

ActiveRecord::Schema.define(version: 2018_07_17_194545) do

  create_table "tweets", force: :cascade do |t|
    t.text "content"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_tweets_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.text "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

なるほど、tweetテーブル側でuser_idにindex的なものがあるなぁ。
しまった、sqlのindexについての知識が乏しい。。。
なんか早くなるっぽいくらいの認識しかない。辛み。

一旦保留にして、referencesじゃなくて、それぞれ別のものとしてuserテーブルとtweetテーブルを作ったものを見てみる。

be rails g model user name:text
be rails g model tweet content:text user_id:integer
be rake db:migrate

db/schema.rb

ActiveRecord::Schema.define(version: 2018_07_18_142813) do

  create_table "tweets", force: :cascade do |t|
    t.text "content"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.text "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

うーむ、tweetsテーブルのuser_idはusersテーブルとは無関係のように見えるな。
これは適当だがreferencesを使ったほうがindex的なものを使うからテーブル感のリレーションを使いたいときには高速。的なことがありそうな気がしてきた。がわからない。
そんなゴリゴリにsqlチューニングする必要ある勢じゃないから、いいや、気にしない。タイムカプセルに入れておこう。

と、やはり気になってteratailにアカウント作って質問の文章を書いている最中にダメ押しでもう少し調べてみたら、多分理解した。

Ruby on Rails - railsのindexとforeign_keyについておしえてください。(63406)|teratail

やっぱり普通にindexを使っているんですね。
そしてindexも概念は理解しました。
sqlのindexの説明で「複製」とかっていう単語を使っていたので、腑に落ちていなかったが、どうやらこういうことらしい。

indexについて理解があいまいなのですが、 --------+--------+---------+

| id | name | shoku |

|1 | taro | ramen |

| 2 | ken | somen |

こんなデータがあったとして、nameフィールドにindexをつけたとすると、よみだすときこのデータ全体を読み込むのではなく、nameフィールドだけを絞って読み込むということですか? だとしたら find_by があるのでindexが不要だと思ったのですが。。。

という質問に対して

たとえば、ユーザーのデータが数百万件を超えるような膨大な数になったとしましょう。 その中から、「メールアドレスがhoge@example.comのデータを探す」となれば、インデックスがない場合だと「全部のデータを当たって、一致するメールアドレスを探す」という処理が必要になります。一方、インデックスでは「B木」のような完全に同一のデータ、あるいはデータ範囲を検索しやすいような特殊な構造でデータを入れてあるので、メールアドレスが一致するデータを、全探索より遥かに高速に引き出すことができます。

Ruby on Rails - railsのindexとforeign_keyについておしえてください。(63406)|teratail

なるほど、普通に複製するだけじゃなくて、B木とかの検索しやすいデータ構造にしておくのか。
いやーCS力足りないなぁ。。。
ということは、結局テーブル同士のリレーションは結構な数の呼び出しが起こりそうな気がしているので、indexを貼ってくれているrefernces型を使うのがbetterという結論でよろしいですかね。

twitter分析アプリ②

バッチ処理をするにはrakeタスクってのを使うみたいだ。

rakeタスクってのは、いままでおまじない的に使っていた

$ be rake db:migrate

※ alias be='bundle exec'
とかで呼び出されるタスクのことらしい。

作り方は簡単。

$ be rails g task task_sample

これで生成されたファイルlib/tasks/task_sample.rake

namespace :task_sample do
end

を、次のように書き換えてみる。

namespace :task_sample do
    desc "実行処理の説明"
    task :sample do
      puts "Hello World"
    end
end

これで、呼び出すときはコンソールから

$ be rake task_sample:sample
Hello World

→OK。Hello Worldが表示されました。
コロンの前が、rakeファイルの名前で、
コロンの後が、taskの名前ということか!

今までの

$ be rake db:migrate

も、dbというrakeファイルの、migrateというtaskを実行していたに過ぎなかったのか〜!
これは学び。
ちなみにrakeタスク一覧はこれで見られると。

be rake -vT
rake about                              # List versions of all Rails frameworks and the environment
rake active_storage:install             # Copy over the migration needed to the application
rake active_storage:install:migrations  # Copy migrations from active_storage to application
rake app:template                       # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake app:update                         # Update configs and some other initially generated files (or use just update:configs ...
rake assets:clean[keep]                 # Remove old compiled assets
rake assets:clobber                     # Remove compiled assets
rake assets:environment                 # Load asset compile environment
rake assets:precompile                  # Compile all the assets named in config.assets.precompile
rake cache_digests:dependencies         # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_commen...
rake cache_digests:nested_dependencies  # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake db:create                          # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_...
rake db:drop                            # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_EN...
rake db:environment:set                 # Set the environment value for the database
rake db:fixtures:load                   # Loads fixtures into the current environment's database
rake db:migrate                         # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status                  # Display status of migrations
rake db:rollback                        # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:cache:clear              # Clears a db/schema_cache.yml file
rake db:schema:cache:dump               # Creates a db/schema_cache.yml file
rake db:schema:dump                     # Creates a db/schema.rb file that is portable against any DB supported by Active Record
rake db:schema:load                     # Loads a schema.rb file into the database
rake db:seed                            # Loads the seed data from db/seeds.rb
rake db:setup                           # Creates the database, loads the schema, and initializes with the seed data (use db:r...
rake db:structure:dump                  # Dumps the database structure to db/structure.sql
rake db:structure:load                  # Recreates the databases from the structure.sql file
rake db:version                         # Retrieves the current schema version number
rake dev:cache                          # Toggle development mode caching on/off
rake initializers                       # Print out all defined initializers in the order they are invoked by Rails
rake log:clear                          # Truncates all/specified *.log files in log/ to zero bytes (specify which logs with L...
rake middleware                         # Prints out your Rack middleware stack
rake notes                              # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
rake notes:custom                       # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
rake restart                            # Restart app by touching tmp/restart.txt
rake routes                             # Print out all defined routes in match order, with names
rake secret                             # Generate a cryptographically secure secret key (this is typically used to generate a...
rake stats                              # Report code statistics (KLOCs, etc) from the application or engine
rake task_sample:sample                 # 実行処理の説明
rake test                               # Runs all tests in test folder except system ones
rake test:db                            # Run tests quickly, but also reset db
rake test:system                        # Run system tests only
rake time:zones[country_or_offset]      # List all time zones, list by two-letter country code (`rails time:zones[US]`), or li...
rake tmp:clear                          # Clear cache, socket and screenshot files from tmp/ (narrow w/ tmp:cache:clear, tmp:s...
rake tmp:create                         # Creates tmp directories for cache, sockets, and pids
rake yarn:install                       # Install all JavaScript dependencies as specified via Yarn

確かにdb:migrateとかありますね。
db:migrateのタスクの中身見てみようとしたけど、パット見ファイルの場所が見つからなかったので、またの機会にします。

wheneverを使用

whenever Gemfileに

gem 'whenever', require: false

を追加して、

$ bundle install
$ be wheneverize .
[add] writing `./config/schedule.rb'
[done] wheneverized!

config/schedule.rbを開いて

set :output, "log/development.log"
every 1.minute do
    rake "task_sample:sample"
end

と書いて、cronを登録する

# 登録します
$ bundle exec whenever --update-crontab

#これで確認できます
$ be whenever
* * * * * /bin/bash -l -c 'cd /Users/my0shym/hogeApp && RAILS_ENV=production bundle exec rake task_sample:sample --silent >> log/development.log 2>&1'

$ be rails sしておいて、別タブで$ tail -f log/development.logで確認する。
すると、見事Hello Worldが表示されました〜パチパチ

rakeタスクからDBとかにアクセスしたいときは

task :hoge => :environment do

が必要らしい。
と、ここでコロンとアローの使い方がうまく整理でいていないことが発覚したので、今度やる。

twitter分析アプリ作成①

railsにて実装。
グラフの描画はchart.jsを使用。

とりあえずモデル作成
alias be='bundle exec'

$ be rails g model Person name:text twitter:text university_name:text entry_no:integer grade:integer age:integer
$ be rails g model Follower num_of_follower:integer date:datetime person:references
$ be rails g model Follow num_of_follow:integer date:datetime person:references
$ be rails g model Tweet num_of_tweet:integer date:datetime person:references

twitterAPI

require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "YOUR_CONSUMER_KEY"
  config.consumer_secret     = "YOUR_CONSUMER_SECRET"
  config.access_token        = "YOUR_ACCESS_TOKEN"
  config.access_token_secret = "YOUR_ACCESS_SECRET"
end


user = client.user('@hogehogetarou')
followers = user.followers_count
followers = user.friends_count
tweets = user.tweets_count

やっぱりDB設計変えたので、こちらは削除する

$ be rails destory model follow
$ be rails destroy model follow
$ be rails destroy model follower
$ be rails destroy model tweet
$ be rails g model Status followers:integer followings:integer tweets:integer favs:integer date:datetime person:references
# dbがゴチャついたと思うので、リセット
# これはレコードのリセット
$ be rake db:reset

# これはmigrationを全部最初からやり直す。
$ be rake db:migrate:reset
$ be rails g controller management show new edit

React奮闘記①

メロスと同じくReactがわからぬ。

チュートリアルと、ドットインストールは写経してみたものの、雰囲気しかわからなかった。。。

バスケットをやっているのをルール知らないで外から見ていて、「どうやらあのかごをボールが通ると点数が入るらしい」「なんか移動するときはぼーるをばうんどさせなきゃいけないらしい」くらいにしかつかめなかった。

そもそものJavaScriptが怪しかったので、progateで一通りサクッと復習しといた。

からの、

作りながら学ぶ React入門

作りながら学ぶ React入門

これだ。

環境構築する。

Mac
High Sierra
10.13.5

一応もう環境はできているはずだが、改めて。

Node.jsのinstall

$ node -v
v10.5.0

→OK。すでにあった。

npmのinstall

$ npm -v
6.1.0

→OK。すでにあった。

init

# 作業用ディレクトリにて
$ npm init -y

npmパッケージinstall

npm install react react-dom
npm install webpack webpack-cli webpack-dev-server --save-dev
npm install babel-cli babel-loader babel-preset-env babel-preset-react --save-dev
npm install eslint eslint-loader eslint-plugin-react --save-dev
npm install css-loader style-loader babel-loader --save-dev

あとは別途設定ファイルを設定。 詳しくは

react_book/install_mac.md at master · yuumi3/react_book · GitHub

より。

webpackとは、、、JavaScriptcssとかのライブラリ、自分で書いたやつをまとめて一つのJSファイルにしてくれるやつ…!!? . . . 2,3時間かけて、

ようやくJSXの基本的なことはわかったような気がする。

コンポーネント - クラスコンポーネントと - 関数コンポーネントがある。

関数コンポーネントはあまり複雑なことはやらせない。

tigでvimじゃなく謎エディタnanoが開いてしまう現象解消

友人に進められシェルを魔改造zshにしたのが原因でtig(やってないけどgit commitコマンドも)のコミットメッセージ編集のところがmac標準搭載らしい謎エディタnanoになってしまっていた。

うまく保存できずにキャッシュファイル的なやつができてaddとかができなくなったりしてしまって面倒くさいので修正。

これを解消するには

git config --global core.editor 'vim -c "set fenc=utf-8"'

これだけ。

macのvagrantでcentos7.2にansible2をinstallしてplay-book実行するまで

Vagrant メモ (1) – 1Q77 Vagrant メモ (2) – 1Q77

これにしたがってvirtualbox,vagrantをインストールしました。

VirtualBoxVagrantはすでに設定済とします。

VagrantでCentOS7.2を使う。

http://www.vagrantbox.es/

こちらからほしいOSを探してリンクをコピーします
今回はなんとなくCentOS7.2を使ってみようと思うので、それを探します。・

$ vagrant box add centos7.2 https://github.com/CommanderK5/packer-centos-template/releases/download/0.7.2/vagrant-centos-7.2.box
$ vagrant init CentOS7.2 # 適当なディレクトリで実行する
$ vagrant up # 起動する
$ vagrant ssh # 接続する

これでCentOSサーバに入れました。

参考: VagrantでCentOS7をインストール - Qiita

一方こんなやり方でもいいみたいです。

$ vagrant init --minimal ubuntu/trusty64 # minimalオプションを付けると設定ファイルが最低限のものだけになる。
$ vagrant up
$ vagrant ssh

参考: VagrantとDockerについて名前しか知らなかったので試した - Qiita

一度取得したOSは追加のプロセスを経ないで立ち上げることができるっぽいですね。
ただ、どちらの方法でも、すでにboxがローカルになければ、セットアップに数十分くらいかかります。(ました。) 直接urlを指定してboxを追加するのは、CentOSでいうrmp、
名前を指定してboxを(追加した後)iした後するのは、yumで管理する的なことだと勝手に思ってます。

その一覧はどうやって見られるのでしょうか。

$ vagrant box list
centos6   (virtualbox, 0)
centos7.2 (virtualbox, 0)

ではこのOSたちはどこに保存されているのでしょうか。 標準では/Users/<user>/.vagrant.d/boxesの下に配置されるようです。

$ pwd
/Users/<user>/.vagrant.d/boxes
$ tree -s
.
├── [         96]  centos6
│   └── [         96]  0
│       └── [        192]  virtualbox
│           ├── [        258]  Vagrantfile
│           ├── [      11372]  box.ovf
│           ├── [         26]  metadata.json
│           └── [  423996416]  packer-centos-6.5-x86_64-disk1.vmdk
└── [         96]  centos7.2
    └── [         96]  0
        └── [        192]  virtualbox
            ├── [        258]  Vagrantfile
            ├── [      12065]  box.ovf
            ├── [  650415104]  centos-vm-disk1.vmdk
            └── [         26]  metadata.json

確かにCentOSと思しき容量大きいファイルが存在しました。これらしいですね。

さて、今からansibleを使って一方のサーバーからもう一方のサーバーに処理を流したいので、sshで接続する必要があります。
IPアドレスはどうなっているのでしょうか。 デフォルトではプライベートIPの設定はできていないみたいなので、Vagrantfileを弄る必要があります。

# 以下のように設定
config.vm.network :private_network, ip: "192.168.33.10"

編集後、vagrantを再起動します。
$ vagrant reload
で再起動します。

同様にして同じ構成のサーバーをansibledというディレクトリの中に作ります。
ただしプライベートIPアドレスは192.168.33.11としておきます。

CentOSにansibleをinstallする

ansible用サーバーにて

# yum install epel-release # epelレポジトリを追加
# yum install ansible --enablerepo=epel-testing # ansibleをinstall
# ansible --version # versionを確認
ansible 2.4.2.0

enablerepo=epel-testingにしないと、古いversionのansibleがinstallされてしまうということがあるみたいです。(検証してないですが、)

ansibleで構成管理をする

早速ansibleで変更を流してみましょう。
まずは疎通確認から。

ansible 192.168.33.11 -m ping
[WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available

 [WARNING]: Could not match supplied host pattern, ignoring: 192.168.33.11

 [WARNING]: No hosts matched, nothing to do

おっとこれは早漏でした。
受ける側の設定を何もしていなかったです。
いくら同じネットワーク内だからといって、無許可で流されたらたまったもんじゃないですね。
結局sshで接続することになるので、sshのconfigあたりを設定変更すればいけそうですね。
と思ったら、その前にansible側でもhostの情報を書かないとだめらしいです。
$ vi /etc/ansible/hostsここにホスト情報を追加します。 さて今一度疎通確認をしてみる。(受け側の設定何も変えてないのでだめだと思うが。)

# ansible 192.168.33.11 -m ping
The authenticity of host '192.168.33.11 (192.168.33.11)' can't be established.
ECDSA key fingerprint is 7a:f6:47:1b:a9:04:c8:c6:90:d5:8c:48:14:03:d3:64.
Are you sure you want to continue connecting (yes/no)? yes
192.168.33.11 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.33.11' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n",
    "unreachable": true
}

案の定だめでした。認証の部分で失敗しましたね。
認証方法は色々ありますが、今回はansible側の公開鍵を受け側に登録しておくことにします。
ansibleサーバにて

# vagrantユーザーで実行する
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa): # 特に何も入力しないでEnterする
Enter passphrase (empty for no passphrase): # 特に何も入力しないでEnterする
Enter same passphrase again: # 特に何も入力しないでEnterする
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
6f:9d:ed:39:06:75:70:9b:ff:c0:71:51:ff:00:8a:dc vagrant@localhost.localdomain
The key's randomart image is:
+--[ RSA 2048]----+
|            .   o|
|       . o . ...o|
|        o E   .o=|
|              o++|
|        S    o +o|
|         . ..oo .|
|          o o....|
|         .   .o..|
|             .o. |
+-----------------+

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsUSJIsKgxiOiFxh2obSw0RCOmODtyyFS60qr1/phQa35sDJwjcoy1kpBdLvIFeQZiWVmKU+vuz1lNjWekKvSOPmxmW54LAJSnqBpy4JgIXzRrm7kEEi0Ol9rkSG3LShtC89CXqOm+PVgXXXXXXXXXXXXXXXXXXXXXXXXXX0Hj+wBTUYrcvQZ7RDAYA1FQeav0Hj+wBTXXXXXXXXXXXXXXXXXXXXXXXXDAYA1FQeav0HnL1CAnseZtBdxXewh/0A9MU9kygj5G3E1j0ZG0q6l9Pt vagrant@localhost.localdomain

# これをコピっとく

受けサーバ側

$ vi ~/.ssh/authorized_keys

# 先程コピーした公開鍵を追加する

これにて完了です。
それでは満を持して疎通確認。

$ ansible 192.168.33.11 -m ping
192.168.33.11 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

OKなようです。
ということは、普通にssh接続もできるということですね。

$ ssh -i ~/.ssh/id_rsa vagrant@192.168.33.11
The authenticity of host '192.168.33.11 (192.168.33.11)' can't be established.
ECDSA key fingerprint is 7a:f6:47:1b:a9:04:c8:c6:90:d5:8c:48:14:03:d3:64.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.33.11' (ECDSA) to the list of known hosts.
Last login: Fri Jan  5 19:42:58 2018 from 10.0.2.2

入れました。

さてここからがansibleの出番です。

ansibleのplaybookを作成してみます。 slコマンドをyumでinstallするだけのplaybookとします。
yum.yml

- hosts: all
  tasks:
    - name: slコマンドのyumインストール
      yum: name=sl
$ ansible-playbook yum.yml

PLAY [all] ******************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.33.11]

TASK [slコマンドのyumインストール] *****************************************************************************
fatal: [192.168.33.11]: FAILED! => {"changed": false, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}
    to retry, use: --limit @/home/vagrant/yum.retry

PLAY RECAP ******************************************************************************************
192.168.33.11              : ok=1    changed=0    unreachable=0    failed=1

おっと、yumはrootじゃないと使えないみたいです。
sudo: yesを書き加えるだけでいいみたいです。

- hosts: all
  sudo: yes
  tasks:
    - name: slコマンドのyumインストール
      yum: name=sl

さて、いざ実行

$ ansible-playbook yum.yml
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method
 is 'sudo' (default). This feature will be removed in version 2.6. Deprecation warnings can be
disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] ******************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.33.11]

TASK [slコマンドのyumインストール] *****************************************************************************
changed: [192.168.33.11]

PLAY RECAP ******************************************************************************************
192.168.33.11              : ok=2    changed=1    unreachable=0    failed=0

OKっぽいですね。
実際に受け側のサーバーで確認してみます。






                  (@@) (  ) (@)  ( )  @@    ()    @     O     @     O      @
             (   )
         (@@@@)
      (    )

    (@@@)
 ====        ________                ___________
_|  |_______/        \__I_I_____===__|_________|
_)---  |   H\________/ |   |        =|___ ___|      _________________
    |  |   H  |  |     |   |         ||_| |_||     _|                \_____A
    |  |   H  |__--------------------| [___] |   =|                        |
_______|___H__/__|_____/[][]~\_______|       |   -|                        |
|   |-----------I_____I [][] []  D   |=======|____|________________________|_
| o |=-~~\  /~~\  /~~\  /~~\ ____Y___________|__|__________________________|_
|___|=    ||    ||    ||    |_____/~\___/          |_D__D__D_|  |_D__D__D_|
      \O=====O=====O=====O_/      \_/               \_/   \_/    \_/   \_/



ちゃんとシュポってますね。

ということで、一旦ansible導入は一区切りつけたいと思います。

次回はplaybookのroleとかの使い分けを整理していきたいです。

macでインカメをコマンドラインから使用する方法を探したい①

Macのカメラからjpg画像を定期的に取得してローカルなりなんなりに保存したい。

検索する
mac カメラ 定期的に」
ぱっと見

コマンドライン

Mac OSX で web カメラ撮影をコマンドラインで行う方法のメモ|毎日の向こうに
このサイトで、以下のツールが紹介されている。
isightcapture for Mac : Free Download : MacUpdate
しかしまあ、OSのバージョン10.3の時代かぁ。。。レオパードだと。
記事を見てみると、2005年だしなぁ。
しかもなんか怪しいとも言えなくもない。
毎度のことなのだが、こういうソフトの怪しい怪しくない、大丈夫なのかだめなのかの見極め方がつかめていないマン。
でも31,455もダウンロードされている。これは信じていいのか。

シェルスクリプト

Macで自動で定期的にスクリーンショットを撮るシェルスクリプト - yumulog | 社会人博士の日記
細かく見てないけど、できそうだということだけ認識しておく。

これはカメラじゃなくて、スクリーンショットだった。

Automater

http://apple.memoblog.net/205/
これ普通に簡単そう。
記事もあまり読まずに、直感でAutomater触ってみることに。


Automaterの闇にハマった。
よくわからない。
photoboothの位置付がわからなくなってきた。
Automaterはフォルダアクションとかには便利っぽいけど、特定のアプリの特定の動きみたいな小難しいことは面倒くさいっぽい。

もうシェルスクリプトで書いてみちゃったほうがいい気がする。
調査に時間をかけすぎている。


それもなかなか情報がない。
そもそもphotoboothなんて使っているやつはこの世界にはいない。

そもそもの方針が間違っているような気がしてきた。

「カメラ mac 自動」
再検索。 微妙に違う。

「インカメラ mac 自動」

これもあまりいい情報がない。

やはり一番最初のコマンドラインツールを使ったほうがいいのではないか。

ダウンロードページへ

おいおい、ちょっと待てなんか登録しないとだめなのか?
↓ しゃあなし「isightcapture」でググってみる。
GitHub - RandyMcMillan/iSightCapture


それっぽいのダウンロードできそうなページがあったが、これは流石にきな臭すぎる。
[ttp://download.cnet.com/isightcapture/3000-2150_4-72546.html]

vgyazo: gyazoとisightcapture組み合わせてみた - はてなの鴨澤
求めていたものとは違うが有益っぽい情報出てきた。
そうか、Macのカメラのことをhttp://d.hatena.ne.jp/kamosawa/20091222/1261473923

isightcapture(http://www.intergalactic.de/pages/iSight.html)ってのは、Macに付属しまくってるiSightってカメラで写真を1枚撮ってセーブする、というだけのコマンドラインツール。

残念ながらurlは無効だった。
デフォルトで使えるコマンドってわけではなさそうだった。
それっぽいのがあるかなと検索してみる。
「isightcapture mac

GitHub - RandyMcMillan/iSightCapture
これクローンしたらいいのかなーと思って読んでみると、これは動画からキャプチャするやつってことか...?

isight

Macコマンドラインツールということで、「brew isightcapture」で検索

isight からコマンドラインで画像をとりこむには imagesnap - tokuhirom's blog

ありがた過ぎる情報出現!

$ brew install imagesnap

これにて解決できそうです!

$ imagesnap
Capturing image from device "<AVCaptureDALDevice: 0x9fc4daf24440 [FaceTime HD Camera][0x1410000005ac8908]>"...

さてはて、保存先はどこなのだろうか。
「"imagesnap" 保存先」

調べによると、/usr/local/bin/imagesnapと同じフォルダにsnapshot.jpgという名前のファイルができるとな。
できないとな。

永遠にできなかったので調べてみると

Does not work on High Sierra · Issue #16 · rharder/imagesnap · GitHub
やはりHigh Sierraでは保存されないとのissueが上がっている。

OSとXcodeを再インストールしたらできたと言ってる人がいたが、OS再インストールは辛い。

自分もissue投げてみるか。。。

結局解決できず、諦めることに。。。

macOSのアプリを作ってみるかな・・・

頓挫しました。 このissueが解決されるまで待ちます。