アーカイブ

‘Rails’ カテゴリーのアーカイブ

belongs_to_required_by_defaultが効かない。 このエントリーを含むはてなブックマーク はてなブックマーク - belongs_to_required_by_defaultが効かない。

2016 年 8 月 17 日 Comments off

Rails 5.0.0からbelongs_toがデフォルトで必須になったはずなのだけれど、そうなっていなかったので調べた。
結論から言うと「config/initializers/new_framework_defaults.rb」に置いたままではダメで、「config/application.rb」に書かないといけない。
理由は読まれるタイミングと使われるタイミング。(またこれか)

最初に、belongs_toの必須が定義されるのはここで、「model.belongs_to_required_by_default」が元になっている。
activerecord-5.0.0.1/lib/active_record/associations/builder/belongs_to.rb

128       if reflection.options[:optional].nil?
129         required = model.belongs_to_required_by_default
130       else
131         required = !reflection.options[:optional]
132       end

で、上の「model.belongs_to_required_by_default」がどこでセットされるかというと、ここ。追加したコメントはあとで出てきます。
activesupport-5.0.0.1/lib/active_support/core_ext/module/attribute_accessors.rb

123       class_eval(<<-EOS, __FILE__, __LINE__ + 1)
124         @@#{sym} = nil unless defined? @@#{sym}
125
126         def self.#{sym}=(obj)
127 #{if sym == :belongs_to_required_by_default
128 'puts "#{name}.#{__method__} # ここで決まる!"'
129 end}
130           @@#{sym} = obj
131         end
132       EOS

って書いても分かりにくいので、コメントを仕込んでタイミングを表示してみた。こんな感じ。

config/application.rb

20 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"
 21   config.active_record.belongs_to_required_by_default = true # ここに書かないとダメ!
 22 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"

config/initializers/new_framework_defaults.rb

17 # Require `belongs_to` associations by default. Previous versions had false.
 18 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"
 19 Rails.application.config.active_record.belongs_to_required_by_default = true
 20 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"

まずは「config/application.rb」に書かなかった場合

% bundle exec rails c
config/application.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/application.rb:22 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/initializers/new_framework_defaults.rb:18 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/initializers/new_framework_defaults.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=> true
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> ActiveRecord::Base.belongs_to_required_by_default
=> nil

つぎに「config/application.rb」に書いた場合

% bundle exec rails c
config/application.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/application.rb:22 Rails.application.config.active_record.belongs_to_required_by_default #=> true
ActiveRecord::Base.belongs_to_required_by_default= # ここで決まる!
config/initializers/new_framework_defaults.rb:18 Rails.application.config.active_record.belongs_to_required_by_default #=> true
config/initializers/new_framework_defaults.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=> true
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> ActiveRecord::Base.belongs_to_required_by_default
=> true

config/application.rb」に書かないとnilのままなんだね・・・。

って、すぐ遭遇しそうなことなので、自分が基本的なことを見落としている気がしてきたぞ。

カテゴリー: Rails タグ:

time_zoneはconfig/initializersに移しちゃ(まだ?)ダメっぽい。 このエントリーを含むはてなブックマーク はてなブックマーク - time_zoneはconfig/initializersに移しちゃ(まだ?)ダメっぽい。

2016 年 8 月 11 日 Comments off
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'

config/application.rbにあったこのコメントがRails 5.0.0から消えたので、config/initializersに移動すべきと解釈してファイルを分けたら効かなくなってしまった。
理由は、config/initializersが読まれる前にconfig.time_zoneが使われてしまうから。

なので、まだconfig.time_zoneはconfig/application.rbに書いておかないといけない。
気になってconfig.i18n.default_localeも調べたらこっちは大丈夫だった。

それぞれの読まれる/使われる順番はこんな感じ。

% bundle exec rails c
config/application.rb:21
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/railtie.rb:32 Time.zone #=>
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/railtie.rb:33 Time.zone #=> (GMT+00:00) UTC
config/initializers/i18n.rb:1
config/initializers/time_zone.rb:1
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/i18n_railtie.rb:49 I18n.default_locale #=> en
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/i18n_railtie.rb:51 I18n.default_locale #=> ja
Loading development environment (Rails 5.0.0)
irb(main):001:0> Time.zone
=> #<ActiveSupport::TimeZone:0x007f97bc001968 @name="UTC", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>>
irb(main):002:0> I18n.default_locale
=> :ja
カテゴリー: Rails タグ:

per_form_csrf_tokensはどう影響するのか。 このエントリーを含むはてなブックマーク はてなブックマーク - per_form_csrf_tokensはどう影響するのか。

2016 年 8 月 10 日 Comments off

Configuring Rails Applications — Ruby on Rails Guides

config.action_controller.per_form_csrf_tokens configures whether CSRF tokens are only valid for the method/action they were generated for.

5.0から加わったこれ、tokenがmethodとactionに関連付くようになるオプションということで、自分でform_authenticity_tokenを直に呼んでいるところでは影響を受けるのかが気になってコードを読んでみた。
結果から言うと、自分でform_authenticity_tokenを呼んでいる場合には影響を受けない。

rails/request_forgery_protection.rb at master · rails/rails

# Sets the token value for the current session.
def form_authenticity_token(form_options: {})
  masked_authenticity_token(session, form_options: form_options)
end

form_authenticity_tokenはform_optionsを引数で受け取っていて、デフォルトは空になってる。

rails/request_forgery_protection.rb at master · rails/rails

# Creates a masked version of the authenticity token that varies
# on each request. The masking is used to mitigate SSL attacks
# like BREACH.
def masked_authenticity_token(session, form_options: {})
  action, method = form_options.values_at(:action, :method)
  raw_token = if per_form_csrf_tokens && action && method
    action_path = normalize_action_path(action)
    per_form_csrf_token(session, action_path, method)
  else
    real_csrf_token(session)
  end

form_authenticity_tokenで受け取った引数はこんな感じで使われる。
ここのper_form_csrf_tokensは、config/initializers/new_framework_defaults.rbにあるやつそのもの。

ではform_optionsはどこから来るのかっていうと、ここ。

rails/url_helper.rb at master · rails/rails

def token_tag(token=nil, form_options: {})
  if token != false && protect_against_forgery?
    token ||= form_authenticity_token(form_options: form_options)
    tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
  else
    "".freeze
  end
end

url_helperの中だ。

ひとまず影響がないことが分かったけれど変わりやすそうな場所ではあるので、注意が必要かな。

カテゴリー: Rails タグ:

カラムの順番について。 このエントリーを含むはてなブックマーク はてなブックマーク - カラムの順番について。

2016 年 2 月 8 日 Comments off

細かいことが気になるシリーズ。
MySQLのADD COLUMNにはAFTERがあるがPostgreSQLにはないため、add_columnに:afterを付けても無視される。
そのためカラムは追加した順に並ぶので、順番を意識したい場合にどうするかという話。たとえばテーブル定義書を作るとき。
Active Recordを通して使うのでいつもは気にする必要がないのだけれど、定義書の順番とdb/schema.rbの順番が異なっているのは気持ち悪い。

PostgreSQLでも出来なくはないけれど別にテーブルつくって云々て、MySQLならオプション1個で済むことをそこまで手間かけてやるの?て感じはしてる。

さて、どうしましょ。

MySQL :: MySQL 5.7 Reference Manual :: 13.1.8 ALTER TABLE Syntax

13.1.8 ALTER TABLE Syntax

PostgreSQL: Documentation: 9.5: ALTER TABLE

ALTER TABLE — change the definition of a table

ActiveRecord::ConnectionAdapters::SchemaStatements

Adds a new column to the named table.

カテゴリー: MySQL, PostgreSQL, Rails タグ:

tryとtry!と。 このエントリーを含むはてなブックマーク はてなブックマーク - tryとtry!と。

2015 年 11 月 5 日 Comments off

koicさんのスライド(Safe navigation operator in Ruby)を、Rubyにもtryができるのかーとありがたく読んでいたのですが、この9枚目、まさにまだtryを使って(!!)いました。。。今日からtry!に切り替えようと思います。

Object

Same as try, but will raise a NoMethodError exception if the receiver is not nil and does not implement the tried method.

Ruby 2.3.0以降でRailsを使うときは「.try!」を使うか「.?」を使うかが気になりましたが、これは「RailsではなくRubyの」メソッドである「.?」に切り替えていくべきですよね。たぶ、ん。

ところでSlideShareって各スライドを開く直リンクがあった気がするんですが、気のせいかな。

って、http://www.slideshare.net/koic/safe-navigation-operator-in-ruby/#9←これで行けるんですね!(教えていただきました。)

[2015-11-06 追記] 「&.」に変わったそうです。

change DOTQ · ruby/ruby@837babd

from “.?” to “&.”. [ruby-core:71363] [Feature #11537]

カテゴリー: Rails, Ruby タグ:

react-railsとbrowserify-railsを併用することにした。 このエントリーを含むはてなブックマーク はてなブックマーク - react-railsとbrowserify-railsを併用することにした。

2015 年 10 月 13 日 Comments off

サイキョウライン

現時点ではreact-railsのやり方に寄せてモジュールのソースを取り込むことにしました。

やっぱりnodeモジュールの管理はnpmに任せることにしました。

reactjs/react-rails

place the following in your application.js:

//= require react
//= require react_ujs
//= require components

react-railsのインストール時に追記するこれをそのまま使って、埋まっているreactはこんなファイルを置いて置き換えています。(その後のreact_ujsでエラーが出るのを防ぐため)
app/assets/javascripts/react.js

if (typeof window !== 'undefined' && window !== null) {
  window.React = require('react');
}

windowの存在判定はCoffeeScriptの「window?」のコンパイル結果をパクりました。
副作用がでちゃったりイカしたやり方を見つけたらバリバリ変えていく所存です。

カテゴリー: JavaScript, Rails タグ:

react-railsを使ったアプリでnodeモジュールを使う。 このエントリーを含むはてなブックマーク はてなブックマーク - react-railsを使ったアプリでnodeモジュールを使う。

2015 年 10 月 7 日 Comments off

react-railsでは実行環境に合わせて、

react-rails/asset_variant.rb at master · reactjs/react-rails

@react_directory = GEM_ROOT.join(‘lib/assets/react-source/’).join(@react_build).to_s

react-rails/railtie.rb at master · reactjs/react-rails

app.config.assets.paths << asset_variant.react_directory

こんな感じでgemに含んでいるreact.jsを使っています。ということは、npmの管理下にありません。
そうなると困るのが、他のnodeモジュールを使いたい場合にどうするかで、だいぶググってあちこちでソースを読んで悩んだのですが、現時点ではreact-railsのやり方に寄せてモジュールのソースを取り込むことにしました。
それぞれこんな感じです。(コードは抜粋)

app/assets/javascripts/application.js

//= require modules

app/assets/javascripts/modules.js

//= require_tree ./modules

js/gulpfile.babel.js

let paths = {
  module_sources: [
    './node_modules/react-redux/dist/react-redux.min.js',
    './node_modules/redux/dist/redux.min.js'
  ],
  modules_dir: '../app/assets/javascripts/modules/'
};
gulp.task('copy_modules', () => {
  gulp.src(paths.module_sources)
    .pipe(gulp.dest(paths.modules_dir));
});

js/package.json

{
  "devDependencies": {
    "react-redux": "^3.1.0",
    "redux": "^3.0.2"
  }
}

※jsディレクトリについてはこちら。

react-railsを使ったアプリでコンポーネントのテストを走らせてみた。 » サイキョウライン

Rails.rootの下に「js」というディレクトリを掘ってjs部屋

nodeモジュールはnpmで一括管理するのが相応しいと思っているのですが、肝心のreactがreact-railsの中にいるので、これだけ例外になるのが気持ち悪く感じています。
実際にreact-railsを使ってアプリを書いている方はどの様な方法でこの問題を解決しているでしょうか。
そもそもreact-railsを使わずにbrowserify-railsを使って汎用的(かな?)な方法で管理するべきなんですかね。

カテゴリー: JavaScript, Rails タグ:

react-railsを使ったアプリでコンポーネントのテストを走らせてみた。 このエントリーを含むはてなブックマーク はてなブックマーク - react-railsを使ったアプリでコンポーネントのテストを走らせてみた。

2015 年 10 月 1 日 Comments off

いままでNode.jsをまともに触ったことがなく、JavaScriptはほぼjQueryを使うための言語だった程度のへっぽこもいいところからのスタートです。

まずは一人React.js Advent Calendar 2014入門 Reactをひと通り読みました。
つぎにreact-railsを使ってReactのチュートリアルをやってみたらなんとなく動いた(ほぼコピペ)んですが、react-railsで使われているReactはこんな感じでgemに含まれていてnpmでは管理されておらず、その他のnodeモジュールを使ったりコンポーネントのテストをどう書いたらいいのか分からず進めなくなってしまいました。

そこで、分からないなりに試行錯誤してなんとかテストを走らせることが出来るまでのもろもろです。
結論から言うと、Rails.rootの下に「js」というディレクトリを掘ってjs部屋とし、そこにコンポーネントのソースをコピーしてテストを走らせています。
このディレクトリ構成はmizchiさんの記事にあったサンプルソースを参考にさせていただきました。
その他もググりまくって書いたり消したり方々から拝借してきたのですが、方々過ぎて分からなくなってしまいました。(ごめんなさい…)

ソースはここにあります。

だいぶ右往左往したのですが、ほぼ2週間に渡る試行錯誤の経緯はこんな感じです。
1.gulpってのを使って処理をまとめるのがいまの普通みたいなので、gulpタスク内でテストを走らせることを目指す。
2.gulp自体やプラグインの使い方を調べながらひたすらタスクを書く。
3.なんとか出来たところで、ReactのTestUtilsがDOMを必要としていることに気が付く。(※1)
4.前覚えたKarmaを使えばいいのでは?と思いつく。(※2)
5.Karmaを使ってPhantomJSに走らせてもらう。
6.動いた!(とりあえず!)

※1

Test Utilities | React

Render a component into a detached DOM node in the document. This function requires a DOM.

※2

WebStormでpower-assertが動くまで。 » サイキョウライン

ランナーにKarmaを、フレームワークにMochaを、ブラウザにPhantomJSを使いました。

テストが走るまでの概要
・assetsの下からコンポーネントのソース(*.coffee)をコピー
・coffee→jsの変換
・jsx→jsの変換
・テストのjsをespowerして(日本語合ってる?)mochaで実行
・ここで「ReferenceError: document is not defined」と怒られる
・espowerした(日本語合ってる?)テストKarmaで実行

コードについて考えたこと
・コンポーネントのソースはcoffeeで書くが、使うのはES5までに留める。
ES6の機能を持ち込む(とくにarrow関数)とコンパイル結果が予測できず混乱してしまった。
ブラウザが読む(外に出る)部分なので、ブラウザ依存のような躓きは防ぎたい。
・テストのソースはjsで書いてES6の機能を出来るだけ使っていく。
正式公開されているので、実際に使って慣れていきたい
外に出ない部分なので、なにかあっても対策がしやすい。

テスト対象で対策をしたこと
コンポーネントのソースはブラウザでもNode.jsでも走るので、両方に向けて依存解決のためのコードを埋めた。

Foo = {}

if module?.exports
  `var $ = require('jquery')` unless $?
  `var React = require('react')` unless React?
  module.exports = Foo
else
  `var $ = this.$` unless $?
  `var React = this.React` unless React?
  @Foo = Foo

Foo.MessageBox = React.createClass

ビューではreact-railsのメソッドを通してこんな感じで使ってます。

= react_component('Foo.MessageBox', url: 'http://example.com/messages')

残っている問題
・アプリ内で使っているReactとテストで使っているReactのソースが異なる。
・動いてはいるがいろいろ普通ではない、気がとてもしている。

その他思ったこと
こういう時、昼メシついでになんとなく聞けるような同僚がいない、ぼっち系個人事業主つらい。

カテゴリー: JavaScript, Rails タグ:

belongs_toにrequiredが付いてた。 このエントリーを含むはてなブックマーク はてなブックマーク - belongs_toにrequiredが付いてた。

2014 年 12 月 25 日 Comments off

えっ、てことで確認したら付いてました。

Add a `required` option to singular associations · 00f5551 · rails/rails

belongs_to :account
validates_presence_of :account

これだいぶ気持ち悪かったのでスッキリできて嬉しい。

カテゴリー: Rails タグ:

Time.nowとTime.currentの違い。 このエントリーを含むはてなブックマーク はてなブックマーク - Time.nowとTime.currentの違い。

2014 年 12 月 8 日 Comments off

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い – Qiita

基本的に Time.current や Time.zone.xxx、xxx.in_time_zone 等のメソッドを使うと、TimeWithZoneクラスのインスタンスが返却され、application.rbのタイムゾーンが使用されるようです。 Time.now や Time.parse など、Timeクラスから元から存在しているメソッドはRuby標準の挙動と同じく、環境変数のタイムゾーンを使用しています。

Time.currentを知らなかったー。いままではTime.nowを使っていたのですが、これからはTime.currentに寄せていこうと思います。

カテゴリー: Rails, Ruby タグ: