Closed MoAI522 closed 2 months ago
普段使っているものを事前に挙げてきました
rails周り
WordPress
フロントエンド直近使っているもの
ツール
Chrome拡張
社内
以前心当たりがあった、active hashのバグ?を掘り下げる 覚えていること:active_hash同士でリレーションを張ったときに、取得できなかったことがある
https://github.com/active-hash/active_hash
公式のドキュメントでそもそもその動作について言及があるか調べる
https://github.com/active-hash/active_hash?tab=readme-ov-file#-important-notice-
ここで書かれているv3.0.0からの変更、「戻り値がチェーン可能になった」ってその話かもしれない
この変更についての記載を探す
https://github.com/active-hash/active_hash/blob/master/CHANGELOG.md
を見た v3.0.0の変更で「#whereをチェーン可能にする」とある
↑はまだ読んでいない
フィードバック:
あとよくよく考えたら、↑で言われているのは「whereをチェーンできるようになった」なので、ActiveHash同士でRelation張れないとは別問題
手元で簡易的に動かしてみることにする
動作確認メモ:
rails new active-hash-relation-test
でrailsプロジェクトを作成bin/rails s
でrails起動確認 loalhost:3000からデフォルトのページが見れるgem "active_hash"
を追加
bundle install
を実行 v3.3.1が入った模様
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Fetching active_hash 3.3.1
Installing active_hash 3.3.1
Bundle complete! 17 Gemfile dependencies, 99 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
方針:今回は一旦以下のモデルを作ってみる
Book(ActiveRecord) -belongs_to :author_id-> Author(ActiveHash) -belongs_to :prefecture_id-> Prefecture(ActiveHash)
↑でPrefectureに紐づくBookを全て取得(逆も)できれば問題ない (できたら困るな)
bin/rails generate model Book title:string author_id:integer
invoke active_record
create db/migrate/20240921030534_create_books.rb
create app/models/book.rb
invoke test_unit
create test/models/book_test.rb
create test/fixtures/books.yml
bin/rails db:migrate
== 20240921030534 CreateBooks: migrating ======================================
-- create_table(:books)
-> 0.0022s
== 20240921030534 CreateBooks: migrated (0.0023s) =============================
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one: title: MyString author_id: 1
two: title: MyString author_id: 1
- `bin/rails db:seed` を実行 <- なにも起こらなかった これはseeds.rb読み込むやつだったわ
- https://techblog.kayac.com/2016/12/01/000000 によると、`bin/rails db:fixtures:load`らしい
- `bin/rails c` で中身確認 入っている
active-hash-relation-test(dev)> Book.all Book Load (0.2ms) SELECT "books". FROM "books" / loading for pp */ LIMIT ? [["LIMIT", 11]] => [#<Book:0x00007fb3c6d3b1a8 id: 298486374, title: "MyString", author_id: 1, created_at: "2024-09-21 03:17:10.084083000 +0000", updated_at: "2024-09-21 03:17:10.084083000 +0000">,
id: 980190962, title: "MyString", author_id: 1, created_at: "2024-09-21 03:17:10.084083000 +0000", updated_at: "2024-09-21 03:17:10.084083000 +0000">]
そもそもbin/rails db:fixtures:load
とかgeneratorとかの記載がRailsガイドでまとまってないの不親切では?
AuthorとCountryを作成し、Book含めリレーションを書いた
# app/models/author.rb
class Author < ActiveHash::Base
include ActiveHash::Associations
has_many :books
belongs_to :country
self.data = [
{ id: 1, name: 'Author A', country_id: 1 },
{ id: 2, name: 'Author B', country_id: 2 },
]
end
class Country < ActiveHash::Base include ActiveHash::Associations
has_many :authors has_many :books, through: :authors
self.data = [ { id: 1, name: 'USA' }, { id: 2, name: 'Japan' }, ] end
class Book < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :author delegate :country, to: :author end
- `bin/rails c` BookからCountryが取得できるか -> できる(deligateなので実質Authorに対したたいているからそれはそう。)
active-hash-relation-test(dev)> Book.first.country Book Load (0.7ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? [["LIMIT", 1]] => #<Country:0x00007fb3c6c97fa8 @attributes={:id=>1, :name=>"USA"}>
- CountryからBookが取得できるか -> できない!再現した!
active-hash-relation-test(dev)> Country.first.books
Book Load (0.7ms) SELECT "books". FROM "books" WHERE "books"."country_id" = ? / loading for pp */ LIMIT ? [["country_id", 1], ["LIMIT", 11]]
An error occurred when inspecting the object: #
SQLが全然間違っている
SELECT "books".* FROM "books" WHERE "books"."country_id" = ? /* loading for pp */ LIMIT ? [["country_id", 1], ["LIMIT", 11]]
本来はcountry_idはDB上に存在しないので、Authorのdataを元に読み替える必要がある
具体的には以下のようになればいい
SELECT "books".* FROM "books" WHERE "books"."author_id" = 1
countributingを読む CONTRIBUTING.mdは無く、README.mdの下の方に記載あり https://github.com/active-hash/active_hash?tab=readme-ov-file#contributing
If you'd like to become an ActiveHash contributor, the easiest way it to fork this repo, make your changes, run the specs and submit a pull request once they pass.
To run specs, run:
bundle install bundle exec rspec spec If your changes seem reasonable and the specs pass I'll give you commit rights to this repo and add you to the list of people who can push the gem.
Issueについては記載ないか?
Issueテンプレートなし
30分切れ負けでバグなおせるか、手元にクローンして試してみる 試すにはgemをいちいちビルドしなきゃならんか?
railsのgem開発用のgeneratorがあるので、そこにコードコピーしながら試す https://railsguides.jp/plugins.html
bin/rails plugin new active-hash
gem "active_hash"
を削除mv ~/active_hash ./active_hash
bundle install
❯ bundle install
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Bundle complete! 17 Gemfile dependencies, 99 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
(よく見たら、plugin newのときにgem "active_hash", path: "active_hash"
がGemfileに追加されていた)
この状態でもさっきのバグは再現する
ダメだったコードは
Country.first.books
CountryはActiveHash::Base
firstはallへdelegateされている https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/base.rb#L192
allはActiveHash::Relationを返す https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/base.rb#L186-L190
ActiveHash::Relationのfirstはrecordsへdelegate https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/relation.rb#L7
recordsの中身は filterの適用 -> order byの適用 apply_order_valuesの返り値は? https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/relation.rb#L83-L88
引数recordsと同じ型 apply_conditionsの返り値は? https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/relation.rb#L203-L222
これも引数ママ それはそう なので、all_recordsである https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/relation.rb#L180-L186
all_recordsはattr_readerである 代入は初期化のタイミングでしかされていない https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/relation.rb#L10-L17
期待結果を書き忘れ 入力
> Country.first.books
出力
[#<Book:0x00007faef7873860
id: 298486374,
title: "MyString",
author_id: 1,
created_at: "2024-09-21 03:17:10.084083000 +0000",
updated_at: "2024-09-21 03:17:10.084083000 +0000">,
#<Book:0x00007faef7873720
id: 980190962,
title: "MyString",
author_id: 1,
created_at: "2024-09-21 03:17:10.084083000 +0000",
updated_at: "2024-09-21 03:17:10.084083000 +0000">]
(Author.find(1).booksと同じになる)
ActiveHash::Base#allに戻る @recordsを代入していた https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/base.rb#L186-L190
追加される処理はinsertだけ https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/base.rb#L119-L127
insertにデータ渡すのはここ data=でhashの中身定義するのであってそう すなわち@recordsはActiveHash::Baseのインスタンスの配列 https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/active_hash/base.rb#L92-L104
あれ@recordsがクラスメソッド内に書かれてる なんだこれ -> クラスインスタンス変数 https://zenn.dev/kanazawa/articles/671a0928a4962c まあ一旦読めるので放置
で、結局Baseに対してbooksが呼ばれている Baseにはhas_manyによってbooks関数が追加されることが期待されるので、そこを見に行く
困難 Issue報告をすぐにしたほうがいい
報告内容の整理
ActiveRecord -> ActiveHash -> ActiveHashのリレーションを貼った時に、右端のリレーションから左端のActiveRecordをhas_many :throughを用いて参照できない 例:
# app/models/author.rb
class Author < ActiveHash::Base
include ActiveHash::Associations
has_many :books
belongs_to :country
self.data = [
{ id: 1, name: 'Author A', country_id: 1 },
...
]
end
# app/models/country.rb
class Country < ActiveHash::Base
include ActiveHash::Associations
has_many :authors
has_many :books, through: :authors
self.data = [
{ id: 1, name: 'USA' },
...
]
end
# app/models/book.rb
class Book < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :author
delegate :country, to: :author
end
というモデルがあるとき、
Country.first.books
を実行すると、
Book Load (0.7ms) SELECT "books".* FROM "books" WHERE "books"."country_id" = ? /* loading for pp */ LIMIT ? [["country_id", 1], ["LIMIT", 11]]
An error occurred when inspecting the object: #<ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: books.country_id>
Result of Kernel#inspect: #<Book::ActiveRecord_Relation:0x00007fb3c5382ef0 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @table=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, @values={:where=>#<ActiveRecord::Relation::WhereClause:0x00007fb3c56d1cc8 @predicates=[#<Arel::Nodes::Equality:0x00007fb3c56d1d40 @left=#<struct Arel::Attributes::Attribute relation=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, name="country_id">, @right=#<ActiveRecord::Relation::QueryAttribute:0x00007fb3c54bcb90 @name="country_id", @value_before_type_cast=1, @type=#<ActiveModel::Type::Value:0x00007fb3c5ec6938 @precision=nil, @scale=nil, @limit=nil>, @original_attribute=nil, @value=1, @_unboundable=nil, @value_for_database=1>>]>}, @loaded=nil, @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 @table=#<ActiveRecord::TableMetadata:0x00007fb3c572f238 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @arel_table=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, @reflection=nil>, @handlers=[[Set, #<ActiveRecord::PredicateBuilder::ArrayHandler:0x00007fb3c572f080 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [Array, #<ActiveRecord::PredicateBuilder::ArrayHandler:0x00007fb3c572f0d0 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [ActiveRecord::Relation, #<ActiveRecord::PredicateBuilder::RelationHandler:0x00007fb3c572f120>], [Range, #<ActiveRecord::PredicateBuilder::RangeHandler:0x00007fb3c572f170 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [BasicObject, #<ActiveRecord::PredicateBuilder::BasicObjectHandler:0x00007fb3c572f1c0 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>]]>, @delegate_to_klass=false, @future_result=nil, @records=nil, @async=false, @none=false, @should_eager_load=nil, @arel=nil, @to_sql=nil, @take=nil, @offsets=nil, @cache_keys=nil, @cache_versions=nil>
=>
というエラーが起きてしまう
ちょい足し 分かっていること
Issueのフォーマット全部バラバラ これが一番淡々と状況報告できそうなので参考にする https://github.com/active-hash/active_hash/issues/239
なんだかんだfluentdのbug reportの形の方が語り切れるのでこっちにする バグの説明が入る https://github.com/fluent/fluentd/issues/new?assignees=&labels=waiting-for-triage&projects=&template=bug_report.yml
タイトルは一旦「has_many ActiveRecord though ActiveHash generates invalid SQL」としてみました
これでいってみようと思います
I can't retrieve ActiveRecord records from ActiveHash object when the ActiveHash is related to the ActiveRecord through another ActiveHash.
books
table below.
# db/schema.rb
ActiveRecord::Schema[7.2].define(version: 2024_09_21_030534) do create_table "books", force: :cascade do |t| t.string "title" t.integer "author_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end end
one: title: MyString author_id: 1
two: title: MyString author_id: 1
2. Execute `bin/rails db:fixtures:load`
3. Create models below.
```ruby
# app/models/book.rb
class Book < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :author
delegate :country, to: :author
end
# app/models/author.rb
class Author < ActiveHash::Base
include ActiveHash::Associations
has_many :books
belongs_to :country
self.data = [
{ id: 1, name: 'Author A', country_id: 1 },
{ id: 2, name: 'Author B', country_id: 2 },
]
end
# app/models/country.rb
class Country < ActiveHash::Base
include ActiveHash::Associations
has_many :authors
has_many :books, through: :authors
self.data = [
{ id: 1, name: 'USA' },
{ id: 2, name: 'Japan' },
]
end
Country.find(1).books
Returns all Book records.(They are all have author_id=1, and the author relates to country 1.)
[#<Book:0x00007faef7873860
id: 298486374,
title: "MyString",
author_id: 1,
created_at: "2024-09-21 03:17:10.084083000 +0000",
updated_at: "2024-09-21 03:17:10.084083000 +0000">,
#<Book:0x00007faef7873720
id: 980190962,
title: "MyString",
author_id: 1,
created_at: "2024-09-21 03:17:10.084083000 +0000",
updated_at: "2024-09-21 03:17:10.084083000 +0000">]
The output is below.
Book Load (0.7ms) SELECT "books".* FROM "books" WHERE "books"."country_id" = ? /* loading for pp */ LIMIT ? [["country_id", 1], ["LIMIT", 11]]
An error occurred when inspecting the object: #<ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: books.country_id>
Result of Kernel#inspect: #<Book::ActiveRecord_Relation:0x00007fb3c5382ef0 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @table=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, @values={:where=>#<ActiveRecord::Relation::WhereClause:0x00007fb3c56d1cc8 @predicates=[#<Arel::Nodes::Equality:0x00007fb3c56d1d40 @left=#<struct Arel::Attributes::Attribute relation=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, name="country_id">, @right=#<ActiveRecord::Relation::QueryAttribute:0x00007fb3c54bcb90 @name="country_id", @value_before_type_cast=1, @type=#<ActiveModel::Type::Value:0x00007fb3c5ec6938 @precision=nil, @scale=nil, @limit=nil>, @original_attribute=nil, @value=1, @_unboundable=nil, @value_for_database=1>>]>}, @loaded=nil, @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 @table=#<ActiveRecord::TableMetadata:0x00007fb3c572f238 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @arel_table=#<Arel::Table:0x00007fb3c53ff6a8 @name="books", @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x00007fb3c572f2d8 @klass=Book(id: integer, title: string, author_id: integer, created_at: datetime, updated_at: datetime)>, @table_alias=nil>, @reflection=nil>, @handlers=[[Set, #<ActiveRecord::PredicateBuilder::ArrayHandler:0x00007fb3c572f080 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [Array, #<ActiveRecord::PredicateBuilder::ArrayHandler:0x00007fb3c572f0d0 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [ActiveRecord::Relation, #<ActiveRecord::PredicateBuilder::RelationHandler:0x00007fb3c572f120>], [Range, #<ActiveRecord::PredicateBuilder::RangeHandler:0x00007fb3c572f170 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>], [BasicObject, #<ActiveRecord::PredicateBuilder::BasicObjectHandler:0x00007fb3c572f1c0 @predicate_builder=#<ActiveRecord::PredicateBuilder:0x00007fb3c572f210 ...>>]]>, @delegate_to_klass=false, @future_result=nil, @records=nil, @async=false, @none=false, @should_eager_load=nil, @arel=nil, @to_sql=nil, @take=nil, @offsets=nil, @cache_keys=nil, @cache_versions=nil>
=>
OS: Ubuntsu 20.04.3(WSL) Ruby version: ruby 3.2.4 (2024-04-23 revision af471c0e01) [x86_64-linux] Rails version: 7.2.1 active-hash version: 3.3.1
The rails uses sqlite3 as database. sqlite3 version: 3.37.2 2022-01-06 13:25:41 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5dalt1
よさそうらしい 追加のコメントで、現状しらべたことを補足しておいたほうがいい
開発者もバグ調査一からやるのはつらいため
以下をコメントする
I think it occurs by line below: https://github.com/active-hash/active_hash/blob/0b3701c54cfcd195423885cd5df70e6959a3a2c0/lib/associations/associations.rb#L18
join_model
is treated without identifying whether it is ActiveRecord or ActiveHash.
But I couldn't find the way to fix it.
https://github.com/active-hash/active_hash/issues/323 提出しました!経過をみます。ありがとうございました!
おつかれさまでした!
ワークショップの終了にともないissueを閉じますが、このまま作業メモとして使っても構いません :ok_hand:
ワークショップの感想を集めています!
ブログなどに書かれた際は、このページへリンクの追加をお願いします :pray:
またの参加をお待ちしています!
This is a work log of a "OSS Gate workshop". "OSS Gate workshop" is an activity to increase OSS developers. Here's been discussed in Japanese. Thanks.
作業ログ作成時の説明
以下のテンプレートを埋めてタイトルに設定します。埋め方例はスクロールすると見えてきます。
タイトル例↓:
OSS Gateワークショップ関連情報