tint 0.0.3 → 0.0.4
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/lib/tint/decorator.rb +53 -71
- data/lib/tint/version.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- data/spec/tint/decorator_spec.rb +118 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b243827913ffe5dd0696d1a4c43c4279f2cbe35
|
4
|
+
data.tar.gz: c112cffa3c4b79cbb6f540c7c41e4e5d998f70d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13aa4b8db03c71954930d71dfe6ce16401c6cce0f4b41df22728a2c2f231ff8e955982ab3e8b62e0e9affc09dec07a606d11d843ea625b0afe2d7216c235f63a
|
7
|
+
data.tar.gz: 920ae9f4d9cbd7bee7afe69e2d83772a2de9174fc7b693ccbe47a51777402f8fbbe855ed7e9d03e9eccdc40e51594748934516018f71b8a889968e5963b61b2c
|
data/lib/tint/decorator.rb
CHANGED
@@ -7,7 +7,9 @@ module Tint
|
|
7
7
|
include JsonConversion
|
8
8
|
|
9
9
|
def initialize(object, options = {})
|
10
|
-
super(object, options)
|
10
|
+
super(object, options.except(:parent_decorator, :parent_association))
|
11
|
+
|
12
|
+
@context = @context.merge(options.slice(:parent_decorator, :parent_association))
|
11
13
|
end
|
12
14
|
|
13
15
|
def column_names
|
@@ -18,6 +20,23 @@ module Tint
|
|
18
20
|
object.persisted?
|
19
21
|
end
|
20
22
|
|
23
|
+
def decorate_as_association(association_name, association, options = {})
|
24
|
+
options.assert_valid_keys(:with)
|
25
|
+
|
26
|
+
association_decorator = options[:with]
|
27
|
+
|
28
|
+
association_context = options.except(:with).merge(context: context.
|
29
|
+
merge(parent_decorator: self, parent_association: association_name.to_sym))
|
30
|
+
|
31
|
+
self.class.eager_load(association_name => {})
|
32
|
+
|
33
|
+
if association.respond_to?(:each)
|
34
|
+
association_decorator.decorate_collection(association, association_context)
|
35
|
+
else
|
36
|
+
association_decorator.decorate(association, association_context)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
21
40
|
class << self
|
22
41
|
attr_accessor :_attributes, :parent_decorator, :parent_association
|
23
42
|
|
@@ -50,8 +69,6 @@ module Tint
|
|
50
69
|
end
|
51
70
|
|
52
71
|
def eager_load(*schema)
|
53
|
-
@_attributes ||= Set.new
|
54
|
-
|
55
72
|
new_eager_loads =
|
56
73
|
schema.inject({}) do |memo, schema_item|
|
57
74
|
if schema_item.kind_of?(Hash)
|
@@ -64,39 +81,32 @@ module Tint
|
|
64
81
|
end
|
65
82
|
|
66
83
|
self.eager_loads = self.eager_loads.deeper_merge(new_eager_loads)
|
67
|
-
|
84
|
+
end
|
68
85
|
|
69
86
|
def decorates_association(association_name, options = {})
|
70
87
|
options[:with] ||= (association_name.to_s.camelize.singularize + 'Decorator').constantize
|
71
88
|
|
72
|
-
association_alias = options.delete(:as)
|
73
|
-
|
74
|
-
options[:context] ||= {}
|
75
|
-
options[:context][:parent_decorator] = self
|
76
|
-
options[:context][:parent_association] = association_name
|
77
|
-
|
78
|
-
ap "association_name:"
|
79
|
-
ap association_name
|
89
|
+
association_alias = options.delete(:as) || association_name
|
80
90
|
|
81
|
-
|
82
|
-
ap options
|
91
|
+
options.assert_valid_keys(:with, :scope, :context)
|
83
92
|
|
93
|
+
define_method(association_alias) do
|
94
|
+
context_with_association = context.merge({
|
95
|
+
parent_decorator: self,
|
96
|
+
parent_association: association_name
|
97
|
+
})
|
84
98
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
decorated_associations[association_alias].call
|
91
|
-
end
|
92
|
-
|
93
|
-
attributes(association_alias)
|
94
|
-
else
|
95
|
-
super(association_name, options)
|
99
|
+
decorated_associations[association_alias] ||= Draper::DecoratedAssociation.new(
|
100
|
+
self,
|
101
|
+
association_name,
|
102
|
+
options.merge(context: context_with_association)
|
103
|
+
)
|
96
104
|
|
97
|
-
|
105
|
+
decorated_associations[association_alias].call
|
98
106
|
end
|
99
107
|
|
108
|
+
attributes(association_alias)
|
109
|
+
|
100
110
|
association_eager_loads = options[:with].eager_loads
|
101
111
|
|
102
112
|
if association_eager_loads.present?
|
@@ -107,6 +117,7 @@ module Tint
|
|
107
117
|
end
|
108
118
|
|
109
119
|
def decorates_associations(*arguments)
|
120
|
+
|
110
121
|
options = arguments.extract_options!
|
111
122
|
association_list = arguments
|
112
123
|
|
@@ -116,25 +127,16 @@ module Tint
|
|
116
127
|
end
|
117
128
|
|
118
129
|
def decorate_collection(collection, options = {})
|
119
|
-
ap "options:"
|
120
|
-
ap options
|
121
|
-
|
122
|
-
collection_with_eager_loads = collection
|
123
|
-
|
124
|
-
if options[:context] && options[:context][:parent_decorator] && options[:context][:parent_association]
|
125
|
-
@parent_decorator = options[:context][:parent_decorator]
|
126
|
-
@parent_association = options[:context][:parent_association]
|
127
130
|
|
131
|
+
collection_with_eager_loads =
|
128
132
|
if collection.respond_to?(:includes) &&
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
# ap "decorate_collection eager_loading eager_loads:"
|
133
|
-
# ap eager_loads
|
133
|
+
eager_loads.present? &&
|
134
|
+
!parent_eager_loads_include_own?(options[:context])
|
134
135
|
|
135
|
-
|
136
|
+
collection.includes(eager_loads)
|
137
|
+
else
|
138
|
+
collection
|
136
139
|
end
|
137
|
-
end
|
138
140
|
|
139
141
|
super(collection_with_eager_loads, options)
|
140
142
|
end
|
@@ -142,42 +144,22 @@ module Tint
|
|
142
144
|
def decorate(object, options = {})
|
143
145
|
object_class = object.class
|
144
146
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
147
|
+
unless already_eager_loaded_associations?(object)
|
148
|
+
object =
|
149
|
+
if responds_to_methods?(object_class, :includes, :find) && eager_loads.present?
|
150
|
+
object_class.includes(*eager_loads).find(object.id)
|
151
|
+
else
|
152
|
+
object
|
153
|
+
end
|
154
|
+
end
|
153
155
|
|
154
156
|
super(object, options)
|
155
157
|
end
|
156
158
|
|
157
|
-
def parent_eager_loads_include_own?(
|
158
|
-
|
159
|
-
association_chain.push(association) if association
|
160
|
-
|
161
|
-
eager_loads_include?(@parent_decorator.eager_loads, association_chain) ||
|
162
|
-
@parent_decorator.parent_eager_loads_include_own?(association_chain)
|
159
|
+
def parent_eager_loads_include_own?(context = {})
|
160
|
+
!!(context && context[:parent_decorator] && context[:parent_decorator].class.eager_loads[context[:parent_association]])
|
163
161
|
end
|
164
162
|
|
165
|
-
def eager_loads_include?(hash, key_list)
|
166
|
-
if key_list.length > 1
|
167
|
-
next_key = key_list.shift
|
168
|
-
|
169
|
-
if (next_value = hash[next_key])
|
170
|
-
eager_loads_include?(next_value, key_list)
|
171
|
-
else
|
172
|
-
next_value
|
173
|
-
end
|
174
|
-
else
|
175
|
-
hash[key_list.first]
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
|
181
163
|
def already_eager_loaded_associations?(object)
|
182
164
|
if object.respond_to?(:association_cache)
|
183
165
|
object.association_cache.any?
|
data/lib/tint/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
data/spec/tint/decorator_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'tint'
|
2
2
|
require_relative '../support/active_record_mock'
|
3
3
|
|
4
|
+
require 'spec_helper'
|
5
|
+
|
4
6
|
RSpec.describe Tint::Decorator do
|
5
7
|
subject{ decorator_class.decorate(object).as_json }
|
6
8
|
|
@@ -23,7 +25,7 @@ RSpec.describe Tint::Decorator do
|
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
it "returns an empty object"
|
28
|
+
it "returns an empty object" do
|
27
29
|
expect(subject).to eql({})
|
28
30
|
end
|
29
31
|
end
|
@@ -209,4 +211,119 @@ RSpec.describe Tint::Decorator do
|
|
209
211
|
end
|
210
212
|
end
|
211
213
|
end
|
214
|
+
|
215
|
+
describe "::parent_eager_loads_include_own?" do
|
216
|
+
let(:decorator_class) do
|
217
|
+
Class.new(Tint::Decorator) do
|
218
|
+
eager_load :products
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
before(:each) do
|
223
|
+
Object.const_set('AssociatedDecorator', decorator_class)
|
224
|
+
end
|
225
|
+
|
226
|
+
context "when the decorator has no parent" do
|
227
|
+
let(:parent_decorator_class) { nil }
|
228
|
+
|
229
|
+
it "returns false" do
|
230
|
+
expect(decorator_class.parent_eager_loads_include_own?).to eql(false)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "when the decorator has a parent set via decorates_association" do
|
235
|
+
let(:parent_decorator_class) do
|
236
|
+
Class.new(Tint::Decorator) do
|
237
|
+
decorates_association :associated
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "returns true" do
|
242
|
+
context = {
|
243
|
+
parent_decorator: parent_decorator_class.decorate(object_class),
|
244
|
+
parent_association: :associated
|
245
|
+
}
|
246
|
+
|
247
|
+
expect(decorator_class.parent_eager_loads_include_own?(context)).to eql(true)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "when a decorator has a parent set via #decorate_association" do
|
252
|
+
let(:parent_decorator_class) do
|
253
|
+
Class.new(Tint::Decorator) do
|
254
|
+
attributes :associated
|
255
|
+
|
256
|
+
def associated
|
257
|
+
decorate_as_association(:association, object, with: AssociatedDecorator)
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
it "returns true" do
|
264
|
+
decorated_class = parent_decorator_class.decorate(object_class.new('foo', 'bar'))
|
265
|
+
|
266
|
+
context = decorated_class.send(:associated).context
|
267
|
+
expect(decorator_class.parent_eager_loads_include_own?(context)).to eql(true)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context "when a decorator has a parent manually set" do
|
272
|
+
context "and the parent doesn't manually eager load the association" do
|
273
|
+
let(:parent_decorator_class) do
|
274
|
+
Class.new(Tint::Decorator) do
|
275
|
+
attributes :associated
|
276
|
+
|
277
|
+
def associated
|
278
|
+
AssociatedDecorator.decorate(object, context: {
|
279
|
+
parent_decorator: self,
|
280
|
+
parent_association: :associated
|
281
|
+
})
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
it "returns false" do
|
289
|
+
decorated_class = parent_decorator_class.decorate(object_class.new('foo', 'bar'))
|
290
|
+
|
291
|
+
context = decorated_class.send(:associated).context
|
292
|
+
expect(decorator_class.parent_eager_loads_include_own?(context)).to eql(false)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context "and the parent manually loads the eager association" do
|
297
|
+
let(:parent_decorator_class) do
|
298
|
+
Class.new(Tint::Decorator) do
|
299
|
+
attributes :associated
|
300
|
+
|
301
|
+
eager_load :associated
|
302
|
+
|
303
|
+
def associated
|
304
|
+
AssociatedDecorator.decorate(object, context: {
|
305
|
+
parent_decorator: self,
|
306
|
+
parent_association: :associated
|
307
|
+
})
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
it "returns true" do
|
315
|
+
decorated_class = parent_decorator_class.decorate(object_class.new('foo', 'bar'))
|
316
|
+
|
317
|
+
context = decorated_class.send(:associated).context
|
318
|
+
expect(decorator_class.parent_eager_loads_include_own?(context)).to eql(true)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
after(:each) do
|
324
|
+
Object.send(:remove_const, 'AssociatedDecorator')
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
|
212
329
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aleck Greenham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: draper
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- lib/tint/decorator.rb
|
99
99
|
- lib/tint/json_conversion.rb
|
100
100
|
- lib/tint/version.rb
|
101
|
+
- spec/spec_helper.rb
|
101
102
|
- spec/support/active_record_mock.rb
|
102
103
|
- spec/tint/decorator_spec.rb
|
103
104
|
- tint.gemspec
|
@@ -126,5 +127,6 @@ signing_key:
|
|
126
127
|
specification_version: 4
|
127
128
|
summary: Declarative object decorators for JSON APIs
|
128
129
|
test_files:
|
130
|
+
- spec/spec_helper.rb
|
129
131
|
- spec/support/active_record_mock.rb
|
130
132
|
- spec/tint/decorator_spec.rb
|