terrestrial 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/Gemfile.lock +44 -53
- data/README.md +3 -6
- data/bin/test +1 -1
- data/features/env.rb +12 -2
- data/features/example.feature +23 -26
- data/lib/terrestrial.rb +31 -0
- data/lib/terrestrial/adapters/abstract_adapter.rb +6 -0
- data/lib/terrestrial/adapters/memory_adapter.rb +82 -6
- data/lib/terrestrial/adapters/sequel_postgres_adapter.rb +191 -0
- data/lib/terrestrial/configurations/conventional_association_configuration.rb +65 -35
- data/lib/terrestrial/configurations/conventional_configuration.rb +280 -124
- data/lib/terrestrial/configurations/mapping_config_options_proxy.rb +97 -0
- data/lib/terrestrial/deleted_record.rb +12 -8
- data/lib/terrestrial/dirty_map.rb +17 -9
- data/lib/terrestrial/functional_pipeline.rb +64 -0
- data/lib/terrestrial/inspection_string.rb +6 -1
- data/lib/terrestrial/lazy_object_proxy.rb +1 -0
- data/lib/terrestrial/many_to_many_association.rb +34 -20
- data/lib/terrestrial/many_to_one_association.rb +11 -3
- data/lib/terrestrial/one_to_many_association.rb +9 -0
- data/lib/terrestrial/public_conveniencies.rb +65 -82
- data/lib/terrestrial/record.rb +106 -0
- data/lib/terrestrial/relation_mapping.rb +43 -12
- data/lib/terrestrial/relational_store.rb +33 -11
- data/lib/terrestrial/upsert_record.rb +54 -0
- data/lib/terrestrial/version.rb +1 -1
- data/spec/automatic_timestamps_spec.rb +339 -0
- data/spec/changes_api_spec.rb +81 -0
- data/spec/config_override_spec.rb +28 -19
- data/spec/custom_serializers_spec.rb +3 -2
- data/spec/database_default_fields_spec.rb +213 -0
- data/spec/database_generated_id_spec.rb +291 -0
- data/spec/database_owned_fields_and_timestamps_spec.rb +200 -0
- data/spec/deletion_spec.rb +1 -1
- data/spec/error_handling/factory_error_handling_spec.rb +1 -4
- data/spec/error_handling/serialization_error_spec.rb +1 -4
- data/spec/error_handling/upsert_error_spec.rb +7 -11
- data/spec/graph_persistence_spec.rb +52 -18
- data/spec/ordered_association_spec.rb +10 -12
- data/spec/predefined_queries_spec.rb +14 -12
- data/spec/readme_examples_spec.rb +1 -1
- data/spec/sequel_query_efficiency_spec.rb +19 -16
- data/spec/spec_helper.rb +6 -1
- data/spec/support/blog_schema.rb +7 -3
- data/spec/support/object_graph_setup.rb +30 -39
- data/spec/support/object_store_setup.rb +16 -196
- data/spec/support/seed_data_setup.rb +15 -149
- data/spec/support/seed_records.rb +141 -0
- data/spec/support/sequel_test_support.rb +46 -13
- data/spec/terrestrial/abstract_record_spec.rb +138 -106
- data/spec/terrestrial/adapters/sequel_postgres_adapter_spec.rb +138 -0
- data/spec/terrestrial/deleted_record_spec.rb +0 -27
- data/spec/terrestrial/dirty_map_spec.rb +52 -77
- data/spec/terrestrial/functional_pipeline_spec.rb +153 -0
- data/spec/terrestrial/inspection_string_spec.rb +61 -0
- data/spec/terrestrial/upsert_record_spec.rb +29 -0
- data/terrestrial.gemspec +7 -8
- metadata +43 -40
- data/MissingFeatures.md +0 -64
- data/lib/terrestrial/abstract_record.rb +0 -99
- data/lib/terrestrial/association_loaders.rb +0 -52
- data/lib/terrestrial/upserted_record.rb +0 -15
- data/spec/terrestrial/public_conveniencies_spec.rb +0 -63
- data/spec/terrestrial/upserted_record_spec.rb +0 -59
@@ -10,7 +10,7 @@ require "support/object_store_setup"
|
|
10
10
|
require "support/seed_data_setup"
|
11
11
|
require "terrestrial"
|
12
12
|
|
13
|
-
RSpec.describe "README examples" do
|
13
|
+
RSpec.describe "README examples", backend: "sequel" do
|
14
14
|
readme_contents = File.read("README.md")
|
15
15
|
|
16
16
|
code_samples = readme_contents
|
@@ -13,11 +13,17 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
13
13
|
let(:user) { user_query.first }
|
14
14
|
|
15
15
|
context "when loading the root node" do
|
16
|
+
before { preload_user_store_to_avoid_counting_startup_queries }
|
17
|
+
|
16
18
|
it "only performs one read" do
|
17
19
|
expect {
|
18
|
-
|
20
|
+
user_query.first
|
19
21
|
}.to change { query_counter.read_count }.by(1)
|
20
22
|
end
|
23
|
+
|
24
|
+
def preload_user_store_to_avoid_counting_startup_queries
|
25
|
+
user_store
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
context "when traversing associations (lazily)" do
|
@@ -89,20 +95,17 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
89
95
|
user.email = modified_email
|
90
96
|
end
|
91
97
|
|
92
|
-
it "performs 1
|
98
|
+
it "performs 1 write" do
|
93
99
|
expect {
|
94
100
|
user_store.save(user)
|
95
|
-
}.to change { query_counter.
|
101
|
+
}.to change { query_counter.write_count }.by(1)
|
96
102
|
end
|
97
103
|
|
98
104
|
it "sends only the updated fields to the datastore" do
|
99
105
|
user_store.save(user)
|
100
|
-
|
106
|
+
upsert_sql = query_counter.upserts.last
|
101
107
|
|
102
|
-
|
103
|
-
expect(update_sql).to eq(
|
104
|
-
%{UPDATE "users" SET "email" = '#{modified_email}' WHERE ("id" = '#{user.id}')}
|
105
|
-
)
|
108
|
+
expect(upsert_sql).not_to include(user.first_name, user.last_name)
|
106
109
|
end
|
107
110
|
end
|
108
111
|
end
|
@@ -117,7 +120,7 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
117
120
|
it "performs 1 update" do
|
118
121
|
expect {
|
119
122
|
user_store.save(user)
|
120
|
-
}.to change { query_counter.
|
123
|
+
}.to change { query_counter.write_count }.by(1)
|
121
124
|
end
|
122
125
|
|
123
126
|
it "performs 0 deletes" do
|
@@ -126,7 +129,7 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
126
129
|
}.to change { query_counter.delete_count }.by(0)
|
127
130
|
end
|
128
131
|
|
129
|
-
|
132
|
+
xit "performs 0 additional reads" do
|
130
133
|
expect {
|
131
134
|
user_store.save(user)
|
132
135
|
}.to change { query_counter.read_count }.by(0)
|
@@ -146,7 +149,7 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
146
149
|
it "performs 1 write" do
|
147
150
|
expect {
|
148
151
|
user_store.save(user)
|
149
|
-
}.to change { query_counter.
|
152
|
+
}.to change { query_counter.write_count }.by(1)
|
150
153
|
end
|
151
154
|
end
|
152
155
|
|
@@ -160,7 +163,7 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
160
163
|
it "performs 1 update" do
|
161
164
|
expect {
|
162
165
|
user_store.save(user)
|
163
|
-
}.to change { query_counter.
|
166
|
+
}.to change { query_counter.write_count }.by(1)
|
164
167
|
end
|
165
168
|
end
|
166
169
|
|
@@ -175,7 +178,7 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
175
178
|
it "performs 2 updates" do
|
176
179
|
expect {
|
177
180
|
user_store.save(user)
|
178
|
-
}.to change { query_counter.
|
181
|
+
}.to change { query_counter.write_count }.by(2)
|
179
182
|
end
|
180
183
|
end
|
181
184
|
end
|
@@ -191,12 +194,12 @@ RSpec.describe "Sequel query efficiency", backend: "sequel" do
|
|
191
194
|
it "performs 1 write" do
|
192
195
|
expect {
|
193
196
|
user_store.save(user)
|
194
|
-
}.to change { query_counter.
|
197
|
+
}.to change { query_counter.write_count }.by(1)
|
195
198
|
end
|
196
199
|
end
|
197
200
|
|
198
|
-
context "when traversing
|
199
|
-
context "laoding `#all` from the object store with one
|
201
|
+
context "when traversing association (eagerly)" do
|
202
|
+
context "laoding `#all` from the object store with one association" do
|
200
203
|
it "performs 1 read per table rather than n + 1" do
|
201
204
|
expect {
|
202
205
|
user_store.eager_load(:posts => []).all.map { |user|
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,8 @@ require "support/sequel_test_support"
|
|
3
3
|
require "support/memory_adapter_test_support"
|
4
4
|
require "support/blog_schema"
|
5
5
|
|
6
|
+
Warning[:deprecated] = false
|
7
|
+
|
6
8
|
RSpec.configure do |config|
|
7
9
|
config.expect_with :rspec do |expectations|
|
8
10
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
@@ -44,7 +46,10 @@ RSpec.configure do |config|
|
|
44
46
|
end
|
45
47
|
|
46
48
|
RSpec.shared_context "adapter setup" do
|
47
|
-
|
49
|
+
define_method(:datastore) do
|
50
|
+
@datastore ||= adapter_support.build_datastore(schema)
|
51
|
+
end
|
52
|
+
|
48
53
|
let(:query_counter) { adapter_support.query_counter }
|
49
54
|
end
|
50
55
|
|
data/spec/support/blog_schema.rb
CHANGED
@@ -12,6 +12,7 @@ BLOG_SCHEMA = {
|
|
12
12
|
{ name: :body, type: String },
|
13
13
|
{ name: :author_id, type: String},
|
14
14
|
{ name: :created_at, type: DateTime },
|
15
|
+
{ name: :updated_at, type: DateTime },
|
15
16
|
],
|
16
17
|
comments: [
|
17
18
|
{ name: :id, type: String, options: { primary_key: true } },
|
@@ -24,15 +25,18 @@ BLOG_SCHEMA = {
|
|
24
25
|
{ name: :name, type: String },
|
25
26
|
],
|
26
27
|
categories_to_posts: [
|
27
|
-
{ name: :post_id, type: String },
|
28
|
-
{ name: :category_id, type: String },
|
28
|
+
{ name: :post_id, type: String, options: { null: false } },
|
29
|
+
{ name: :category_id, type: String, options: { null: false } },
|
29
30
|
],
|
30
31
|
},
|
32
|
+
unique_indexes: [
|
33
|
+
# [:categories_to_posts, :post_id, :category_id]
|
34
|
+
],
|
31
35
|
foreign_keys: [
|
32
36
|
[:posts, :author_id, :users, :id],
|
33
37
|
[:comments, :post_id, :posts, :id],
|
34
38
|
[:comments, :commenter_id, :users, :id],
|
35
|
-
[:categories_to_posts, :post_id, :posts, :id],
|
39
|
+
[:categories_to_posts, :post_id, :posts, :id, on_delete: :cascade],
|
36
40
|
[:categories_to_posts, :category_id, :categories, :id],
|
37
41
|
]
|
38
42
|
}
|
@@ -8,6 +8,14 @@ RSpec.shared_context "object graph setup" do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def setup_circular_references_avoiding_stack_overflow
|
11
|
+
biscuits_post.author = hansel
|
12
|
+
sleep_post.author = hansel
|
13
|
+
|
14
|
+
hansel.posts = [
|
15
|
+
biscuits_post,
|
16
|
+
sleep_post,
|
17
|
+
]
|
18
|
+
|
11
19
|
biscuits_post_comment.commenter = hansel
|
12
20
|
cat_biscuits_category.posts = [ biscuits_post ]
|
13
21
|
end
|
@@ -26,9 +34,16 @@ RSpec.shared_context "object graph setup" do
|
|
26
34
|
end
|
27
35
|
|
28
36
|
def initialize(attrs)
|
29
|
-
members.sort
|
30
|
-
|
31
|
-
|
37
|
+
if members.sort != attrs.keys.sort
|
38
|
+
missing = (members - attrs.keys).sort
|
39
|
+
unexpected = (attrs.keys - members).sort
|
40
|
+
raise(ArgumentError.new(
|
41
|
+
"#{self.class} initialized with incorrect arguments." \
|
42
|
+
"Missing: #{missing}. " \
|
43
|
+
"Unexpected: #{unexpected}. " \
|
44
|
+
"Received: #{attrs.keys}."
|
45
|
+
))
|
46
|
+
end
|
32
47
|
|
33
48
|
members.each { |member| send("#{member}=", attrs.fetch(member)) }
|
34
49
|
end
|
@@ -43,51 +58,24 @@ RSpec.shared_context "object graph setup" do
|
|
43
58
|
end
|
44
59
|
|
45
60
|
User ||= PlainObject.with_members(:id, :first_name, :last_name, :email, :posts)
|
46
|
-
Post ||= PlainObject.with_members(:id, :subject, :body, :comments, :categories, :created_at)
|
61
|
+
Post ||= PlainObject.with_members(:id, :author, :subject, :body, :comments, :categories, :created_at, :updated_at)
|
47
62
|
Comment ||= PlainObject.with_members(:id, :commenter, :body)
|
48
63
|
Category ||= PlainObject.with_members(:id, :name, :posts)
|
49
64
|
|
50
|
-
let(:factories) {
|
51
|
-
{
|
52
|
-
users: User.method(:new),
|
53
|
-
posts: Post.method(:new),
|
54
|
-
comments: Comment.method(:new),
|
55
|
-
categories: Category.method(:new),
|
56
|
-
categories_to_posts: ->(x){x},
|
57
|
-
noop: ->(x){x},
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
let(:default_serializer) {
|
62
|
-
->(fields) {
|
63
|
-
->(object) {
|
64
|
-
Terrestrial::Serializer.new(fields, object).to_h
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
let(:null_serializer) {
|
70
|
-
->(_fields) {
|
71
|
-
->(x){x}
|
72
|
-
}
|
73
|
-
}
|
74
|
-
|
75
65
|
let(:hansel) {
|
76
|
-
|
66
|
+
User.new(
|
77
67
|
id: "users/1",
|
78
68
|
first_name: "Hansel",
|
79
69
|
last_name: "Trickett",
|
80
70
|
email: "hansel@tricketts.org",
|
81
|
-
posts: [
|
82
|
-
biscuits_post,
|
83
|
-
sleep_post,
|
84
|
-
],
|
71
|
+
posts: [],
|
85
72
|
)
|
86
73
|
}
|
87
74
|
|
88
75
|
let(:biscuits_post) {
|
89
|
-
|
76
|
+
Post.new(
|
90
77
|
id: "posts/1",
|
78
|
+
author: nil,
|
91
79
|
subject: "Biscuits",
|
92
80
|
body: "I like them",
|
93
81
|
comments: [
|
@@ -97,12 +85,14 @@ RSpec.shared_context "object graph setup" do
|
|
97
85
|
cat_biscuits_category,
|
98
86
|
],
|
99
87
|
created_at: Time.parse("2015-09-05T15:00:00+01:00"),
|
88
|
+
updated_at: Time.parse("2015-09-05T15:00:00+01:00"),
|
100
89
|
)
|
101
90
|
}
|
102
91
|
|
103
92
|
let(:sleep_post) {
|
104
|
-
|
93
|
+
Post.new(
|
105
94
|
id: "posts/2",
|
95
|
+
author: nil,
|
106
96
|
subject: "Sleeping",
|
107
97
|
body: "I do it three times purrr day",
|
108
98
|
comments: [],
|
@@ -110,11 +100,12 @@ RSpec.shared_context "object graph setup" do
|
|
110
100
|
chilling_category,
|
111
101
|
],
|
112
102
|
created_at: Time.parse("2015-09-02T15:00:00+01:00"),
|
103
|
+
updated_at: Time.parse("2015-09-02T15:00:00+01:00"),
|
113
104
|
)
|
114
105
|
}
|
115
106
|
|
116
107
|
let(:biscuits_post_comment) {
|
117
|
-
|
108
|
+
Comment.new(
|
118
109
|
id: "comments/1",
|
119
110
|
body: "oh noes",
|
120
111
|
commenter: nil,
|
@@ -122,7 +113,7 @@ RSpec.shared_context "object graph setup" do
|
|
122
113
|
}
|
123
114
|
|
124
115
|
let(:cat_biscuits_category) {
|
125
|
-
|
116
|
+
Category.new(
|
126
117
|
id: "categories/1",
|
127
118
|
name: "Cat biscuits",
|
128
119
|
posts: [],
|
@@ -130,7 +121,7 @@ RSpec.shared_context "object graph setup" do
|
|
130
121
|
}
|
131
122
|
|
132
123
|
let(:chilling_category) {
|
133
|
-
|
124
|
+
Category.new(
|
134
125
|
id: "categories/2",
|
135
126
|
name: "Chillaxing",
|
136
127
|
posts: [],
|
@@ -15,207 +15,27 @@ require "support/object_graph_setup"
|
|
15
15
|
RSpec.shared_context "object store setup" do
|
16
16
|
include_context "object graph setup"
|
17
17
|
|
18
|
+
let(:user_store) { object_store[:users] }
|
19
|
+
|
18
20
|
let(:object_store) {
|
19
|
-
Terrestrial.object_store(
|
21
|
+
Terrestrial.object_store(config: mappings)
|
20
22
|
}
|
21
23
|
|
22
|
-
let(:user_store) { object_store[:users] }
|
23
|
-
|
24
24
|
let(:mappings) {
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
associations = config.fetch(:associations).map { |assoc_name, assoc_config|
|
30
|
-
[
|
31
|
-
assoc_name,
|
32
|
-
case assoc_config.fetch(:type)
|
33
|
-
when :one_to_many
|
34
|
-
Terrestrial::OneToManyAssociation.new(
|
35
|
-
**assoc_defaults.merge(
|
36
|
-
assoc_config.dup.tap { |h| h.delete(:type) }
|
37
|
-
)
|
38
|
-
)
|
39
|
-
when :many_to_one
|
40
|
-
Terrestrial::ManyToOneAssociation.new(
|
41
|
-
assoc_config.dup.tap { |h| h.delete(:type) }
|
42
|
-
)
|
43
|
-
when :many_to_many
|
44
|
-
Terrestrial::ManyToManyAssociation.new(
|
45
|
-
**assoc_defaults
|
46
|
-
.merge(
|
47
|
-
join_mapping_name: assoc_config.fetch(:join_mapping_name),
|
48
|
-
)
|
49
|
-
.merge(
|
50
|
-
assoc_config.dup.tap { |h|
|
51
|
-
h.delete(:type)
|
52
|
-
h.delete(:join_namespace)
|
53
|
-
}
|
54
|
-
)
|
55
|
-
)
|
56
|
-
else
|
57
|
-
raise "Association type not supported"
|
58
|
-
end
|
59
|
-
]
|
60
|
-
}
|
61
|
-
|
62
|
-
[
|
63
|
-
name,
|
64
|
-
Terrestrial::RelationMapping.new(
|
65
|
-
name: name,
|
66
|
-
namespace: config.fetch(:namespace),
|
67
|
-
fields: config.fetch(:fields),
|
68
|
-
primary_key: config.fetch(:primary_key),
|
69
|
-
serializer: serializers.fetch(config.fetch(:serializer)).call(fields),
|
70
|
-
associations: Hash[associations],
|
71
|
-
factory: factories.fetch(name),
|
72
|
-
subsets: Terrestrial::SubsetQueriesProxy.new(config.fetch(:subsets, {}))
|
73
|
-
)
|
74
|
-
]
|
25
|
+
Terrestrial.config(datastore)
|
26
|
+
.setup_mapping(:users) { |users|
|
27
|
+
users.has_many(:posts, foreign_key: :author_id)
|
75
28
|
}
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
->(query:, loader:, mapping_name:) {
|
87
|
-
Terrestrial::CollectionMutabilityProxy.new(
|
88
|
-
Terrestrial::LazyCollection.new(
|
89
|
-
query,
|
90
|
-
loader,
|
91
|
-
mappings.fetch(mapping_name).subsets,
|
92
|
-
)
|
93
|
-
)
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
let(:many_to_one_proxy_factory) {
|
98
|
-
->(query:, loader:, preloaded_data:) {
|
99
|
-
Terrestrial::LazyObjectProxy.new(
|
100
|
-
->{ loader.call(query.first) },
|
101
|
-
preloaded_data,
|
102
|
-
)
|
103
|
-
}
|
104
|
-
}
|
105
|
-
|
106
|
-
let(:serializers) {
|
107
|
-
{
|
108
|
-
default: default_serializer,
|
109
|
-
null: null_serializer,
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
let(:configs) {
|
114
|
-
{
|
115
|
-
users: {
|
116
|
-
namespace: :users,
|
117
|
-
primary_key: [:id],
|
118
|
-
fields: [
|
119
|
-
:id,
|
120
|
-
:first_name,
|
121
|
-
:last_name,
|
122
|
-
:email,
|
123
|
-
],
|
124
|
-
factory: :user,
|
125
|
-
serializer: :default,
|
126
|
-
associations: {
|
127
|
-
posts: {
|
128
|
-
type: :one_to_many,
|
129
|
-
mapping_name: :posts,
|
130
|
-
foreign_key: :author_id,
|
131
|
-
key: :id,
|
132
|
-
proxy_factory: has_many_proxy_factory,
|
133
|
-
}
|
134
|
-
},
|
135
|
-
},
|
136
|
-
|
137
|
-
posts: {
|
138
|
-
namespace: :posts,
|
139
|
-
primary_key: [:id],
|
140
|
-
fields: [
|
141
|
-
:id,
|
142
|
-
:subject,
|
143
|
-
:body,
|
144
|
-
:created_at,
|
145
|
-
],
|
146
|
-
factory: :post,
|
147
|
-
serializer: :default,
|
148
|
-
associations: {
|
149
|
-
comments: {
|
150
|
-
type: :one_to_many,
|
151
|
-
mapping_name: :comments,
|
152
|
-
foreign_key: :post_id,
|
153
|
-
key: :id,
|
154
|
-
proxy_factory: has_many_proxy_factory,
|
155
|
-
},
|
156
|
-
categories: {
|
157
|
-
type: :many_to_many,
|
158
|
-
mapping_name: :categories,
|
159
|
-
key: :id,
|
160
|
-
foreign_key: :post_id,
|
161
|
-
association_foreign_key: :category_id,
|
162
|
-
association_key: :id,
|
163
|
-
join_mapping_name: :categories_to_posts,
|
164
|
-
proxy_factory: has_many_proxy_factory,
|
165
|
-
},
|
166
|
-
},
|
167
|
-
},
|
168
|
-
|
169
|
-
comments: {
|
170
|
-
namespace: :comments,
|
171
|
-
primary_key: [:id],
|
172
|
-
fields: [
|
173
|
-
:id,
|
174
|
-
:body,
|
175
|
-
],
|
176
|
-
factory: :comment,
|
177
|
-
serializer: :default,
|
178
|
-
associations: {
|
179
|
-
commenter: {
|
180
|
-
type: :many_to_one,
|
181
|
-
mapping_name: :users,
|
182
|
-
key: :id,
|
183
|
-
foreign_key: :commenter_id,
|
184
|
-
proxy_factory: many_to_one_proxy_factory,
|
185
|
-
},
|
186
|
-
},
|
187
|
-
},
|
188
|
-
|
189
|
-
categories: {
|
190
|
-
namespace: :categories,
|
191
|
-
primary_key: [:id],
|
192
|
-
fields: [
|
193
|
-
:id,
|
194
|
-
:name,
|
195
|
-
],
|
196
|
-
factory: :comment,
|
197
|
-
serializer: :default,
|
198
|
-
associations: {
|
199
|
-
posts: {
|
200
|
-
type: :many_to_many,
|
201
|
-
mapping_name: :posts,
|
202
|
-
key: :id,
|
203
|
-
foreign_key: :category_id,
|
204
|
-
association_foreign_key: :post_id,
|
205
|
-
association_key: :id,
|
206
|
-
join_mapping_name: :categories_to_posts,
|
207
|
-
proxy_factory: has_many_proxy_factory,
|
208
|
-
},
|
209
|
-
},
|
210
|
-
},
|
211
|
-
|
212
|
-
categories_to_posts: {
|
213
|
-
namespace: :categories_to_posts,
|
214
|
-
primary_key: [:category_id, :post_id],
|
215
|
-
fields: [],
|
216
|
-
serializer: :null,
|
217
|
-
associations: {},
|
29
|
+
.setup_mapping(:posts) { |posts|
|
30
|
+
posts.belongs_to(:author, mapping_name: :users)
|
31
|
+
posts.has_many(:comments)
|
32
|
+
posts.has_many_through(:categories)
|
33
|
+
}
|
34
|
+
.setup_mapping(:comments) { |comments|
|
35
|
+
comments.belongs_to(:commenter, mapping_name: :users)
|
36
|
+
}
|
37
|
+
.setup_mapping(:categories) { |categories|
|
38
|
+
categories.has_many_through(:posts)
|
218
39
|
}
|
219
|
-
}
|
220
40
|
}
|
221
41
|
end
|