rom-repository 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -5
- data/CHANGELOG.md +24 -0
- data/Gemfile +21 -5
- data/README.md +6 -110
- data/lib/rom/repository/changeset/create.rb +26 -0
- data/lib/rom/repository/changeset/pipe.rb +40 -0
- data/lib/rom/repository/changeset/update.rb +82 -0
- data/lib/rom/repository/changeset.rb +99 -0
- data/lib/rom/repository/class_interface.rb +142 -0
- data/lib/rom/repository/command_compiler.rb +214 -0
- data/lib/rom/repository/command_proxy.rb +22 -0
- data/lib/rom/repository/header_builder.rb +13 -16
- data/lib/rom/repository/mapper_builder.rb +7 -14
- data/lib/rom/repository/{loading_proxy → relation_proxy}/wrap.rb +7 -7
- data/lib/rom/repository/relation_proxy.rb +225 -0
- data/lib/rom/repository/root.rb +110 -0
- data/lib/rom/repository/struct_attributes.rb +46 -0
- data/lib/rom/repository/struct_builder.rb +31 -14
- data/lib/rom/repository/version.rb +1 -1
- data/lib/rom/repository.rb +192 -31
- data/lib/rom/struct.rb +13 -8
- data/rom-repository.gemspec +9 -10
- data/spec/integration/changeset_spec.rb +86 -0
- data/spec/integration/command_macros_spec.rb +175 -0
- data/spec/integration/command_spec.rb +224 -0
- data/spec/integration/multi_adapter_spec.rb +3 -3
- data/spec/integration/repository_spec.rb +97 -2
- data/spec/integration/root_repository_spec.rb +88 -0
- data/spec/shared/database.rb +47 -3
- data/spec/shared/mappers.rb +35 -0
- data/spec/shared/models.rb +41 -0
- data/spec/shared/plugins.rb +66 -0
- data/spec/shared/relations.rb +76 -0
- data/spec/shared/repo.rb +38 -17
- data/spec/shared/seeds.rb +19 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/support/mapper_registry.rb +1 -3
- data/spec/unit/changeset_spec.rb +58 -0
- data/spec/unit/header_builder_spec.rb +34 -35
- data/spec/unit/relation_proxy_spec.rb +170 -0
- data/spec/unit/sql/relation_spec.rb +5 -5
- data/spec/unit/struct_builder_spec.rb +7 -4
- data/spec/unit/struct_spec.rb +22 -0
- metadata +38 -41
- data/lib/rom/plugins/relation/key_inference.rb +0 -31
- data/lib/rom/repository/loading_proxy/combine.rb +0 -158
- data/lib/rom/repository/loading_proxy.rb +0 -182
- data/spec/unit/loading_proxy_spec.rb +0 -147
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec.shared_context 'mappers' do
|
2
|
+
let(:users) { rom.relation(:users).mappers[:user] }
|
3
|
+
let(:tasks) { rom.relation(:tasks).mappers[:task] }
|
4
|
+
let(:tags) { rom.relation(:tags).mappers[:tag] }
|
5
|
+
|
6
|
+
before do
|
7
|
+
configuration.mappers do
|
8
|
+
define(:users) do
|
9
|
+
model Test::Models::User
|
10
|
+
register_as :user
|
11
|
+
|
12
|
+
attribute :id
|
13
|
+
attribute :name
|
14
|
+
end
|
15
|
+
|
16
|
+
define(:tasks) do
|
17
|
+
model Test::Models::Task
|
18
|
+
register_as :task
|
19
|
+
|
20
|
+
attribute :id
|
21
|
+
attribute :user_id
|
22
|
+
attribute :title
|
23
|
+
end
|
24
|
+
|
25
|
+
define(:tags) do
|
26
|
+
model Test::Models::Tag
|
27
|
+
register_as :tag
|
28
|
+
|
29
|
+
attribute :id
|
30
|
+
attribute :task_id
|
31
|
+
attribute :name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
RSpec.shared_context 'models' do
|
2
|
+
let(:user_model) { Test::Models::User }
|
3
|
+
let(:task_model) { Test::Models::Task }
|
4
|
+
let(:tag_model) { Test::Models::Tag }
|
5
|
+
|
6
|
+
before do
|
7
|
+
module Test
|
8
|
+
module Models
|
9
|
+
class User
|
10
|
+
include Dry::Equalizer(:id, :name)
|
11
|
+
|
12
|
+
attr_reader :id, :name
|
13
|
+
|
14
|
+
def initialize(attrs)
|
15
|
+
@id, @name = attrs[:id], attrs[:name]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Task
|
20
|
+
include Dry::Equalizer(:id, :user_id, :title)
|
21
|
+
|
22
|
+
attr_reader :id, :user_id, :title
|
23
|
+
|
24
|
+
def initialize(attrs)
|
25
|
+
@id, @name, @title = attrs[:id], attrs[:name], attrs[:title]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Tag
|
30
|
+
include Dry::Equalizer(:id, :task_id, :name)
|
31
|
+
|
32
|
+
attr_reader :id, :task_id, :name
|
33
|
+
|
34
|
+
def initialize(attrs)
|
35
|
+
@id, @task_id, @name = attrs[:id], attrs[:task_id], attrs[:name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
RSpec.shared_context 'plugins' do
|
2
|
+
before do
|
3
|
+
module Test
|
4
|
+
class WrappingInput
|
5
|
+
def initialize(input)
|
6
|
+
@input = input || Hash
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Timestamps
|
11
|
+
class InputWithTimestamp < WrappingInput
|
12
|
+
def [](value)
|
13
|
+
v = @input[value]
|
14
|
+
now = Time.now
|
15
|
+
|
16
|
+
if v[:created_at]
|
17
|
+
v.merge(updated_at: now)
|
18
|
+
else
|
19
|
+
v.merge(created_at: now, updated_at: now)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassInterface
|
25
|
+
def build(relation, options = {})
|
26
|
+
super(relation, options.merge(input: InputWithTimestamp.new(input)))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.included(klass)
|
31
|
+
super
|
32
|
+
|
33
|
+
klass.extend ClassInterface
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module UpcaseName
|
38
|
+
class UpcaseNameInput < WrappingInput
|
39
|
+
def [](value)
|
40
|
+
v = @input[value]
|
41
|
+
v.merge(name: value.fetch(:name).upcase)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ClassInterface
|
46
|
+
def build(relation, options = {})
|
47
|
+
super(relation, options.merge(input: UpcaseNameInput.new(options.fetch(:input))))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.included(klass)
|
52
|
+
super
|
53
|
+
|
54
|
+
klass.extend ClassInterface
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ROM.plugins do
|
60
|
+
adapter :sql do
|
61
|
+
register :timestamps, Test::Timestamps, type: :command
|
62
|
+
register :upcase_name, Test::UpcaseName, type: :command
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/shared/relations.rb
CHANGED
@@ -2,9 +2,31 @@ RSpec.shared_context 'relations' do
|
|
2
2
|
let(:users) { rom.relation(:users) }
|
3
3
|
let(:tasks) { rom.relation(:tasks) }
|
4
4
|
let(:tags) { rom.relation(:tags) }
|
5
|
+
let(:posts) { rom.relation(:posts) }
|
6
|
+
let(:books) { rom.relation(:books) }
|
5
7
|
|
6
8
|
before do
|
9
|
+
configuration.relation(:books) do
|
10
|
+
schema(:books) do
|
11
|
+
attribute :id, ROM::SQL::Types::Serial
|
12
|
+
attribute :title, ROM::SQL::Types::String
|
13
|
+
attribute :created_at, ROM::SQL::Types::Time
|
14
|
+
attribute :updated_at, ROM::SQL::Types::Time
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
7
18
|
configuration.relation(:users) do
|
19
|
+
schema(infer: true) do
|
20
|
+
associations do
|
21
|
+
has_many :posts
|
22
|
+
has_many :labels, through: :posts
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def by_name(name)
|
27
|
+
where(name: name)
|
28
|
+
end
|
29
|
+
|
8
30
|
def all
|
9
31
|
select(:id, :name).order(:name, :id)
|
10
32
|
end
|
@@ -15,6 +37,12 @@ RSpec.shared_context 'relations' do
|
|
15
37
|
end
|
16
38
|
|
17
39
|
configuration.relation(:tasks) do
|
40
|
+
schema(infer: true) do
|
41
|
+
associations do
|
42
|
+
belongs_to :user
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
18
46
|
def find(criteria)
|
19
47
|
where(criteria)
|
20
48
|
end
|
@@ -25,5 +53,53 @@ RSpec.shared_context 'relations' do
|
|
25
53
|
end
|
26
54
|
|
27
55
|
configuration.relation(:tags)
|
56
|
+
|
57
|
+
configuration.relation(:labels) do
|
58
|
+
schema(infer: true) do
|
59
|
+
associations do
|
60
|
+
has_many :posts_labels
|
61
|
+
has_many :posts, through: :posts_labels
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
configuration.relation(:posts) do
|
67
|
+
schema(:posts, infer: true) do
|
68
|
+
associations do
|
69
|
+
has_many :labels, through: :posts_labels
|
70
|
+
belongs_to :user, as: :author
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
configuration.relation(:posts_labels) do
|
76
|
+
schema(infer: true) do
|
77
|
+
associations do
|
78
|
+
belongs_to :post
|
79
|
+
belongs_to :label
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
configuration.relation(:comments) do
|
85
|
+
register_as :comments
|
86
|
+
|
87
|
+
schema(:messages, infer: true) do
|
88
|
+
associations do
|
89
|
+
has_many :reactions, relation: :likes
|
90
|
+
has_many :reactions, relation: :likes, as: :emotions
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
configuration.relation(:likes) do
|
96
|
+
register_as :likes
|
97
|
+
|
98
|
+
schema(:reactions, infer: true) do
|
99
|
+
associations do
|
100
|
+
belongs_to :message, relation: :comments
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
28
104
|
end
|
29
105
|
end
|
data/spec/shared/repo.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
RSpec.shared_context('repo') do
|
2
|
+
include_context 'models'
|
3
|
+
include_context 'mappers'
|
4
|
+
|
2
5
|
let(:repo) { repo_class.new(rom) }
|
3
6
|
|
4
7
|
let(:repo_class) do
|
5
|
-
Class.new(ROM::Repository) do
|
6
|
-
relations :
|
8
|
+
Class.new(ROM::Repository[:users]) do
|
9
|
+
relations :tasks, :tags, :posts, :labels
|
7
10
|
|
8
11
|
def find_users(criteria)
|
9
12
|
users.find(criteria)
|
@@ -13,6 +16,26 @@ RSpec.shared_context('repo') do
|
|
13
16
|
users.all
|
14
17
|
end
|
15
18
|
|
19
|
+
def all_users_as_users
|
20
|
+
users.as(:user).all
|
21
|
+
end
|
22
|
+
|
23
|
+
def users_with_tasks
|
24
|
+
aggregate(many: { all_tasks: tasks.for_users })
|
25
|
+
end
|
26
|
+
|
27
|
+
def users_with_tasks_and_tags
|
28
|
+
aggregate(many: { all_tasks: tasks_with_tags(tasks.for_users) })
|
29
|
+
end
|
30
|
+
|
31
|
+
def users_with_task
|
32
|
+
aggregate(one: tasks)
|
33
|
+
end
|
34
|
+
|
35
|
+
def users_with_task_by_title(title)
|
36
|
+
aggregate(one: tasks.find(title: title))
|
37
|
+
end
|
38
|
+
|
16
39
|
def tasks_for_users(users)
|
17
40
|
tasks.for_users(users)
|
18
41
|
end
|
@@ -25,29 +48,27 @@ RSpec.shared_context('repo') do
|
|
25
48
|
tasks.find(id: 2).combine_parents(one: { owner: users })
|
26
49
|
end
|
27
50
|
|
28
|
-
def users_with_tasks
|
29
|
-
users.combine_children(many: { all_tasks: tasks.for_users })
|
30
|
-
end
|
31
|
-
|
32
|
-
def users_with_tasks_and_tags
|
33
|
-
users.combine_children(many: { all_tasks: tasks_with_tags(tasks.for_users) })
|
34
|
-
end
|
35
|
-
|
36
51
|
def tasks_with_tags(tasks = self.tasks)
|
37
52
|
tasks.combine_children(many: tags)
|
38
53
|
end
|
39
54
|
|
40
|
-
def
|
41
|
-
|
55
|
+
def tag_with_wrapped_task
|
56
|
+
tags.wrap_parent(task: tasks)
|
42
57
|
end
|
58
|
+
end
|
59
|
+
end
|
43
60
|
|
44
|
-
|
45
|
-
|
61
|
+
let(:comments_repo) do
|
62
|
+
Class.new(ROM::Repository[:comments]) do
|
63
|
+
relations :likes
|
64
|
+
|
65
|
+
def comments_with_likes
|
66
|
+
aggregate(many: { likes: likes })
|
46
67
|
end
|
47
68
|
|
48
|
-
def
|
49
|
-
|
69
|
+
def comments_with_emotions
|
70
|
+
root.combine(:emotions)
|
50
71
|
end
|
51
|
-
end
|
72
|
+
end.new(rom)
|
52
73
|
end
|
53
74
|
end
|
data/spec/shared/seeds.rb
CHANGED
@@ -7,5 +7,24 @@ RSpec.shared_context 'seeds' do
|
|
7
7
|
task_id = conn[:tasks].insert user_id: jane_id, title: 'Jane Task'
|
8
8
|
|
9
9
|
conn[:tags].insert task_id: task_id, name: 'red'
|
10
|
+
|
11
|
+
jane_post_id = conn[:posts].insert author_id: jane_id, title: 'Hello From Jane', body: 'Jane Post'
|
12
|
+
joe_post_id = conn[:posts].insert author_id: joe_id, title: 'Hello From Joe', body: 'Joe Post'
|
13
|
+
|
14
|
+
red_id = conn[:labels].insert name: 'red'
|
15
|
+
green_id = conn[:labels].insert name: 'green'
|
16
|
+
blue_id = conn[:labels].insert name: 'blue'
|
17
|
+
|
18
|
+
conn[:posts_labels].insert post_id: jane_post_id, label_id: red_id
|
19
|
+
conn[:posts_labels].insert post_id: jane_post_id, label_id: blue_id
|
20
|
+
|
21
|
+
conn[:posts_labels].insert post_id: joe_post_id, label_id: green_id
|
22
|
+
|
23
|
+
conn[:messages].insert author: 'Jane', body: 'Hello folks'
|
24
|
+
conn[:messages].insert author: 'Joe', body: 'Hello Jane'
|
25
|
+
|
26
|
+
conn[:reactions].insert message_id: 1, author: 'Joe'
|
27
|
+
conn[:reactions].insert message_id: 1, author: 'Anonymous'
|
28
|
+
conn[:reactions].insert message_id: 2, author: 'Jane'
|
10
29
|
end
|
11
30
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
require "bundler"
|
5
5
|
Bundler.setup
|
6
6
|
|
7
|
-
if RUBY_ENGINE == "
|
7
|
+
if RUBY_ENGINE == "ruby" && RUBY_VERSION == '2.3.1'
|
8
8
|
require "codeclimate-test-reporter"
|
9
9
|
CodeClimate::TestReporter.start
|
10
10
|
end
|
@@ -28,6 +28,9 @@ Dir[root.join('shared/*.rb').to_s].each do |f|
|
|
28
28
|
require f
|
29
29
|
end
|
30
30
|
|
31
|
+
require 'rom/support/deprecations'
|
32
|
+
ROM::Deprecations.set_logger!(root.join('../log/deprecations.log'))
|
33
|
+
|
31
34
|
# Namespace holding all objects created during specs
|
32
35
|
module Test
|
33
36
|
def self.remove_constants
|
@@ -0,0 +1,58 @@
|
|
1
|
+
RSpec.describe ROM::Changeset do
|
2
|
+
let(:jane) { { id: 2, name: "Jane" } }
|
3
|
+
let(:relation) { double(ROM::Relation, primary_key: :id) }
|
4
|
+
|
5
|
+
describe '#diff' do
|
6
|
+
it 'returns a hash with changes' do
|
7
|
+
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
8
|
+
|
9
|
+
changeset = ROM::Changeset::Update.new(relation, { name: "Jane Doe" }, primary_key: 2)
|
10
|
+
|
11
|
+
expect(changeset.diff).to eql(name: "Jane Doe")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#diff?' do
|
16
|
+
it 'returns true when data differs from the original tuple' do
|
17
|
+
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
18
|
+
|
19
|
+
changeset = ROM::Changeset::Update.new(relation, { name: "Jane Doe" }, primary_key: 2)
|
20
|
+
|
21
|
+
expect(changeset).to be_diff
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns false when data are equal to the original tuple' do
|
25
|
+
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
26
|
+
|
27
|
+
changeset = ROM::Changeset::Update.new(relation, { name: "Jane" }, primary_key: 2)
|
28
|
+
|
29
|
+
expect(changeset).to_not be_diff
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'quacks like a hash' do
|
34
|
+
subject(:changeset) { ROM::Changeset::Create.new(relation, data) }
|
35
|
+
|
36
|
+
let(:data) { instance_double(Hash) }
|
37
|
+
|
38
|
+
it 'delegates to its data hash' do
|
39
|
+
expect(data).to receive(:[]).with(:name).and_return('Jane')
|
40
|
+
|
41
|
+
expect(changeset[:name]).to eql('Jane')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'maintains its own type' do
|
45
|
+
expect(data).to receive(:merge).with(foo: 'bar').and_return(foo: 'bar')
|
46
|
+
|
47
|
+
new_changeset = changeset.merge(foo: 'bar')
|
48
|
+
|
49
|
+
expect(new_changeset).to be_instance_of(ROM::Changeset::Create)
|
50
|
+
expect(new_changeset.options).to eql(changeset.options)
|
51
|
+
expect(new_changeset.to_h).to eql(foo: 'bar')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises NoMethodError when an unknown message was sent' do
|
55
|
+
expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,18 +1,25 @@
|
|
1
1
|
RSpec.describe 'header builder', '#call' do
|
2
2
|
subject(:builder) { ROM::Repository::HeaderBuilder.new }
|
3
3
|
|
4
|
-
let(:user_struct)
|
5
|
-
|
6
|
-
|
4
|
+
let(:user_struct) do
|
5
|
+
builder.struct_builder[:users, [:header, [[:attribute, :id], [:attribute, :name]]]]
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:task_struct) do
|
9
|
+
builder.struct_builder[:tasks, [:header, [[:attribute, :user_id], [:attribute, :title]]]]
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:tag_struct) do
|
13
|
+
builder.struct_builder[:tags, [:header, [[:attribute, :user_id], [:attribute, :tag]]]]
|
14
|
+
end
|
7
15
|
|
8
16
|
describe 'with a relation' do
|
9
17
|
let(:ast) do
|
10
|
-
[
|
11
|
-
:
|
12
|
-
|
13
|
-
],
|
14
|
-
|
15
|
-
]
|
18
|
+
[:relation, [
|
19
|
+
:users,
|
20
|
+
{ dataset: :users, combine_name: :users },
|
21
|
+
[:header, [[:attribute, :id], [:attribute, :name]]]
|
22
|
+
]]
|
16
23
|
end
|
17
24
|
|
18
25
|
it 'produces a valid header' do
|
@@ -24,32 +31,25 @@ RSpec.describe 'header builder', '#call' do
|
|
24
31
|
|
25
32
|
describe 'with a graph' do
|
26
33
|
let(:ast) do
|
27
|
-
[
|
28
|
-
:
|
34
|
+
[:relation, [
|
35
|
+
:users,
|
36
|
+
{ dataset: :users, combine_name: :users },
|
37
|
+
[
|
29
38
|
:header, [
|
30
39
|
[:attribute, :id],
|
31
40
|
[:attribute, :name],
|
32
|
-
[
|
33
|
-
:
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
{
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
[:attribute, :user_id],
|
45
|
-
[:attribute, :tag]
|
46
|
-
]
|
47
|
-
],
|
48
|
-
{ base_name: :tags, keys: { id: :user_id }, combine_type: :many }
|
49
|
-
]
|
50
|
-
]
|
51
|
-
],
|
52
|
-
base_name: :users
|
41
|
+
[:relation, [
|
42
|
+
:tasks,
|
43
|
+
{ dataset: :tasks, keys: { id: :user_id }, combine_type: :many, combine_name: :tasks },
|
44
|
+
[:header, [[:attribute, :user_id], [:attribute, :title]]]
|
45
|
+
]],
|
46
|
+
[:relation, [
|
47
|
+
:tags,
|
48
|
+
{ dataset: :tags, keys: { id: :user_id }, combine_type: :many, combine_name: :tags },
|
49
|
+
[:header, [[:attribute, :user_id], [:attribute, :tag]]]
|
50
|
+
]]
|
51
|
+
]]
|
52
|
+
]
|
53
53
|
]
|
54
54
|
end
|
55
55
|
|
@@ -63,9 +63,8 @@ RSpec.describe 'header builder', '#call' do
|
|
63
63
|
header: ROM::Header.coerce([[:user_id], [:tag]], model: tag_struct)]
|
64
64
|
]
|
65
65
|
|
66
|
-
header = ROM::Header.coerce(
|
67
|
-
|
68
|
-
model: builder.struct_builder[:users, [:id, :name, :tasks, :tags]]
|
66
|
+
header = ROM::Header.coerce(attributes,
|
67
|
+
model: builder.struct_builder[:users, ast[1][2]]
|
69
68
|
)
|
70
69
|
|
71
70
|
expect(builder[ast]).to eql(header)
|