active_record-json_associations 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Appraisals +4 -0
- data/README.md +8 -0
- data/active_record-json_associations.gemspec +1 -0
- data/gemfiles/rails_6.1.gemfile +7 -0
- data/lib/active_record/json_associations.rb +31 -2
- data/lib/active_record/json_associations/version.rb +1 -1
- data/spec/json_associations_spec.rb +116 -3
- data/spec/spec_helper.rb +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82a8dd8639c2897e523652a7415754704227798f98e753681d0199cc70ec055d
|
4
|
+
data.tar.gz: bf59af54b968501e6b087a65ba7d95b5783fe5ec20c4c41182b5882bbdb9604d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fbc06fdc2cd8dbf92bc5d336ab5d1b1dc45f393e26ae47e45573f346be04912544dee4b31097a3fa1767ce1981c05fa5eb046fd5428947afd9cfeb90cf9827c
|
7
|
+
data.tar.gz: 402f51353a4b4865e67ae982cc4794aa60658e2afc3e97c1c8faf3eb03f93ed0306020b4618dc870e62aa34c184d85dd3e424b647254bedaf5436b92a7af0199
|
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -43,6 +43,14 @@ And a scope method for finding records assocatied with an id:
|
|
43
43
|
Parent.child_ids_including(2) # => [<Parent child_ids: [1,2,3]>]
|
44
44
|
```
|
45
45
|
|
46
|
+
Or any of specified array of ids:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Parent.child_ids_including([2,4,5]) # => [<Parent child_ids: [1,2,3]>]
|
50
|
+
```
|
51
|
+
|
52
|
+
`touch: true` can be specified on belongs_to_many to touch the associated records' timestamps when the record is modified.
|
53
|
+
|
46
54
|
It also adds an `json_foreign_key` option to `has_many` for specifying that the foreign keys are in a json array.
|
47
55
|
|
48
56
|
```ruby
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
private_constant :FIELD_INCLUDE_SCOPE_BUILDER_PROC
|
13
13
|
|
14
|
-
def belongs_to_many(many, class_name: nil)
|
14
|
+
def belongs_to_many(many, class_name: nil, touch: nil)
|
15
15
|
one = many.to_s.singularize
|
16
16
|
one_ids = :"#{one}_ids"
|
17
17
|
one_ids_equals = :"#{one_ids}="
|
@@ -22,10 +22,39 @@ module ActiveRecord
|
|
22
22
|
|
23
23
|
serialize one_ids, JSON
|
24
24
|
|
25
|
+
if touch
|
26
|
+
after_commit do
|
27
|
+
if respond_to?(:saved_changes)
|
28
|
+
old_ids, new_ids = saved_changes[one_ids.to_s]
|
29
|
+
else
|
30
|
+
old_ids, new_ids = previous_changes[one_ids.to_s]
|
31
|
+
end
|
32
|
+
ids = Array(old_ids) | Array(new_ids)
|
33
|
+
scope = class_name.constantize.where(self.class.primary_key => ids)
|
34
|
+
|
35
|
+
if scope.respond_to?(:touch) # AR 6.0+
|
36
|
+
scope.touch_all
|
37
|
+
elsif self.class.respond_to?(:touch_attributes_with_time) # AR 5.1+
|
38
|
+
scope.update_all self.class.touch_attributes_with_time
|
39
|
+
else # AR 5.0
|
40
|
+
attributes = timestamp_attributes_for_update_in_model.inject({}) do |attributes, key|
|
41
|
+
attributes.merge(key => current_time_from_proper_timezone)
|
42
|
+
end
|
43
|
+
scope.update_all attributes
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
25
48
|
extend Module.new {
|
26
49
|
define_method :"#{one_ids}_including" do |id|
|
27
50
|
raise "can't query for a record that does not have an id!" if id.blank?
|
28
|
-
|
51
|
+
if id.is_a?(Hash)
|
52
|
+
Array(id[:any]).inject(none) do |context, id|
|
53
|
+
context.or(FIELD_INCLUDE_SCOPE_BUILDER_PROC.call(self, one_ids, id))
|
54
|
+
end
|
55
|
+
else
|
56
|
+
FIELD_INCLUDE_SCOPE_BUILDER_PROC.call(self, one_ids, id)
|
57
|
+
end
|
29
58
|
end
|
30
59
|
}
|
31
60
|
|
@@ -9,16 +9,21 @@ describe ActiveRecord::JsonAssociations do
|
|
9
9
|
create_table :parents do |t|
|
10
10
|
t.text :child_ids
|
11
11
|
t.text :fuzzy_ids
|
12
|
+
t.timestamps
|
12
13
|
end
|
13
14
|
|
14
|
-
create_table :children
|
15
|
+
create_table :children do |t|
|
16
|
+
t.timestamps
|
17
|
+
end
|
15
18
|
|
16
|
-
create_table :pets
|
19
|
+
create_table :pets do |t|
|
20
|
+
t.timestamps
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
20
25
|
class Parent < ActiveRecord::Base
|
21
|
-
belongs_to_many :children
|
26
|
+
belongs_to_many :children, touch: true
|
22
27
|
belongs_to_many :fuzzies, class_name: "Pet"
|
23
28
|
end
|
24
29
|
|
@@ -38,6 +43,7 @@ describe ActiveRecord::JsonAssociations do
|
|
38
43
|
|
39
44
|
describe ".belongs_to_many :children" do
|
40
45
|
subject { Parent.new }
|
46
|
+
let!(:winner) { Parent.create! }
|
41
47
|
|
42
48
|
describe ".child_ids_including" do
|
43
49
|
context "finds records with the specified id" do
|
@@ -63,6 +69,113 @@ describe ActiveRecord::JsonAssociations do
|
|
63
69
|
expect(Parent.child_ids_including(child.id)).to eq [parent]
|
64
70
|
end
|
65
71
|
end
|
72
|
+
|
73
|
+
context "finds records including any of the specified array of ids" do
|
74
|
+
let(:peter) { Child.create! }
|
75
|
+
let(:paul) { Child.create! }
|
76
|
+
|
77
|
+
it "both as the whole json array" do
|
78
|
+
parent = Parent.create(children: [peter, paul])
|
79
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "one as the whole json array" do
|
83
|
+
parent = Parent.create(children: [peter])
|
84
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "the other as the whole json array" do
|
88
|
+
parent = Parent.create(children: [paul])
|
89
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "both at the beginning of the json array" do
|
93
|
+
parent = Parent.create(children: [peter, paul, Child.create!])
|
94
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "one at the beginning of the json array" do
|
98
|
+
parent = Parent.create(children: [peter, Child.create!])
|
99
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "the other at the beginning of the json array" do
|
103
|
+
parent = Parent.create(children: [paul, Child.create!])
|
104
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "both in the middle of the json array" do
|
108
|
+
parent = Parent.create(children: [Child.create!, peter, paul, Child.create!])
|
109
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "one in the middle of the json array" do
|
113
|
+
parent = Parent.create(children: [Child.create!, peter, Child.create!])
|
114
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "the other in the middle of the json array" do
|
118
|
+
parent = Parent.create(children: [Child.create!, paul, Child.create!])
|
119
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
120
|
+
end
|
121
|
+
|
122
|
+
it "both at the end of the json array" do
|
123
|
+
parent = Parent.create(children: [Child.create!, peter, paul])
|
124
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
125
|
+
end
|
126
|
+
|
127
|
+
it "one at the end of the json array" do
|
128
|
+
parent = Parent.create(children: [Child.create!, peter])
|
129
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
130
|
+
end
|
131
|
+
|
132
|
+
it "the other at the end of the json array" do
|
133
|
+
parent = Parent.create(children: [Child.create!, paul])
|
134
|
+
expect(Parent.child_ids_including(any: [peter.id, paul.id])).to eq [parent]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "touch: true" do
|
140
|
+
around do |example|
|
141
|
+
old_zone = Time.zone
|
142
|
+
Time.zone = "UTC"
|
143
|
+
example.run
|
144
|
+
Time.zone = old_zone
|
145
|
+
end
|
146
|
+
|
147
|
+
let(:old_time) { 1.year.ago.round }
|
148
|
+
let(:new_time) { 1.second.ago.round }
|
149
|
+
|
150
|
+
around do |example|
|
151
|
+
Timecop.freeze(new_time) do
|
152
|
+
example.run
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
it "touches associated records" do
|
157
|
+
children = [Child.create!(updated_at: old_time), Child.create!(updated_at: old_time)]
|
158
|
+
fuzzies = [Pet.create!(updated_at: old_time), Pet.create!(updated_at: old_time)]
|
159
|
+
parent = Parent.create!(children: children, fuzzies: fuzzies)
|
160
|
+
expect(children.each(&:reload).map(&:updated_at)).to eq [new_time, new_time] # touch: true
|
161
|
+
expect(fuzzies.each(&:reload).map(&:updated_at)).to eq [old_time, old_time] # touch: nil
|
162
|
+
end
|
163
|
+
|
164
|
+
it "touches removed associated records" do
|
165
|
+
peter, paul, mary = Child.create!, Child.create!, Child.create!
|
166
|
+
parent = Parent.create!(children: [peter, paul, mary])
|
167
|
+
[peter, paul, mary].each { |child| child.update_column :updated_at, old_time }
|
168
|
+
parent.update!(children: [peter, paul])
|
169
|
+
expect([peter, paul, mary].each(&:reload).map(&:updated_at)).to eq [new_time, new_time, new_time]
|
170
|
+
end
|
171
|
+
|
172
|
+
it "touches added associated records" do
|
173
|
+
peter, paul, mary = Child.create!, Child.create!, Child.create!
|
174
|
+
parent = Parent.create!(children: [peter, paul])
|
175
|
+
[peter, paul, mary].each { |child| child.update_column :updated_at, old_time }
|
176
|
+
parent.update!(children: [peter, paul, mary])
|
177
|
+
expect([peter, paul, mary].each(&:reload).map(&:updated_at)).to eq [new_time, new_time, new_time]
|
178
|
+
end
|
66
179
|
end
|
67
180
|
|
68
181
|
describe "#child_ids" do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record-json_associations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micah Geisel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: timecop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: Instead of a many-to-many join table, serialize the ids into a JSON array.
|
112
126
|
email:
|
113
127
|
- micah@botandrose.com
|
@@ -130,6 +144,7 @@ files:
|
|
130
144
|
- gemfiles/rails_5.1.gemfile
|
131
145
|
- gemfiles/rails_5.2.gemfile
|
132
146
|
- gemfiles/rails_6.0.gemfile
|
147
|
+
- gemfiles/rails_6.1.gemfile
|
133
148
|
- lib/active_record/json_associations.rb
|
134
149
|
- lib/active_record/json_associations/version.rb
|
135
150
|
- spec/json_associations_spec.rb
|