protip 0.20.7 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/protip.rb +10 -0
- data/lib/protip/{wrapper.rb → decorator.rb} +98 -82
- data/lib/protip/extensions.rb +0 -1
- data/lib/protip/resource.rb +56 -32
- data/lib/protip/resource/extra_methods.rb +5 -3
- data/lib/protip/tasks/compile.rake +2 -2
- data/lib/protip/transformer.rb +14 -0
- data/lib/protip/transformers/abstract_transformer.rb +11 -0
- data/lib/protip/transformers/active_support/time_with_zone_transformer.rb +35 -0
- data/lib/protip/transformers/decorating_transformer.rb +33 -0
- data/lib/protip/transformers/default_transformer.rb +22 -0
- data/lib/protip/transformers/delegating_transformer.rb +44 -0
- data/lib/protip/transformers/deprecated_transformer.rb +90 -0
- data/lib/protip/transformers/enum_transformer.rb +93 -0
- data/lib/protip/transformers/primitives_transformer.rb +78 -0
- data/lib/protip/transformers/timestamp_transformer.rb +31 -0
- data/test/functional/protip/decorator_test.rb +204 -0
- data/test/unit/protip/decorator_test.rb +665 -0
- data/test/unit/protip/resource_test.rb +76 -92
- data/test/unit/protip/transformers/decorating_transformer_test.rb +92 -0
- data/test/unit/protip/transformers/delegating_transformer_test.rb +83 -0
- data/test/unit/protip/transformers/deprecated_transformer_test.rb +139 -0
- data/test/unit/protip/transformers/enum_transformer_test.rb +157 -0
- data/test/unit/protip/transformers/primitives_transformer_test.rb +139 -0
- data/test/unit/protip/transformers/timestamp_transformer_test.rb +46 -0
- metadata +23 -12
- data/definitions/google/protobuf/wrappers.proto +0 -99
- data/lib/google/protobuf/descriptor.rb +0 -1
- data/lib/google/protobuf/wrappers.rb +0 -48
- data/lib/protip/converter.rb +0 -22
- data/lib/protip/standard_converter.rb +0 -185
- data/test/unit/protip/standard_converter_test.rb +0 -502
- data/test/unit/protip/wrapper_test.rb +0 -646
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 751ca27cb6c6edc2bb6e39614b7ad5dedf4509d4
|
4
|
+
data.tar.gz: 8f1fe4aa42f18348ed8f3edb7e886c045cf31dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bce93588ea39831f7ac6e548ee489863e2e590be4f138b9b0be0337a6c9290890edfb215f9f51e91b95d19fae0663869f076159c1541281e752ca3805770c66
|
7
|
+
data.tar.gz: 52960419aae018faf89f4fb572875d2b81f5f9286da4434a110d2ca826082eb95bb3ec1e6e34cca816bb36ceb721e54a5e8cc4d763f676d109dcff7f5c5510be
|
data/lib/protip.rb
CHANGED
@@ -5,3 +5,13 @@ require 'protip/resource'
|
|
5
5
|
if defined?(Mime::Type)
|
6
6
|
Mime::Type.register 'application/x-protobuf', :protobuf
|
7
7
|
end
|
8
|
+
|
9
|
+
module Protip
|
10
|
+
def self.default_transformer
|
11
|
+
@default_transformer ||= Protip::Transformers::DefaultTransformer.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.decorate(message, transformer = Protip.default_transfomer)
|
15
|
+
Protip::Decorator.new(message, transformer)
|
16
|
+
end
|
17
|
+
end
|
@@ -1,39 +1,36 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
|
3
|
+
require 'protip/transformers/enum_transformer'
|
4
|
+
|
3
5
|
module Protip
|
4
6
|
|
5
7
|
# Wraps a protobuf message to allow:
|
6
|
-
# - getting/setting of
|
8
|
+
# - getting/setting of message fields as if they were more complex Ruby objects
|
7
9
|
# - mass assignment of attributes
|
8
10
|
# - standardized creation of nested messages that can't be converted to/from Ruby objects
|
9
|
-
class
|
11
|
+
class Decorator
|
10
12
|
|
11
|
-
attr_reader :message, :
|
13
|
+
attr_reader :message, :transformer, :nested_resources
|
12
14
|
|
13
|
-
def initialize(message,
|
15
|
+
def initialize(message, transformer, nested_resources={})
|
14
16
|
@message = message
|
15
|
-
@
|
17
|
+
@transformer = transformer
|
16
18
|
@nested_resources = nested_resources
|
17
19
|
end
|
18
20
|
|
19
21
|
def respond_to?(name)
|
20
22
|
if super
|
21
|
-
|
23
|
+
true
|
22
24
|
else
|
23
25
|
# Responds to calls to oneof groups by name
|
24
26
|
return true if message.class.descriptor.lookup_oneof(name.to_s)
|
25
27
|
|
26
|
-
# Responds to field getters, setters, and
|
28
|
+
# Responds to field getters, setters, and query methods for all fieldsfa
|
27
29
|
field = message.class.descriptor.lookup(name.to_s.gsub(/[=?]$/, ''))
|
28
30
|
return false if !field
|
29
|
-
|
30
|
-
|
31
|
-
return self.class.matchable?(field)
|
32
|
-
else
|
33
|
-
return true
|
34
|
-
end
|
31
|
+
|
32
|
+
true
|
35
33
|
end
|
36
|
-
false
|
37
34
|
end
|
38
35
|
|
39
36
|
def method_missing(name, *args)
|
@@ -74,52 +71,69 @@ module Protip
|
|
74
71
|
#
|
75
72
|
# We could create an inner message using:
|
76
73
|
#
|
77
|
-
# wrapper = Protip::Wrapper.new(Outer.new,
|
74
|
+
# wrapper = Protip::Wrapper.new(Outer.new, transformer)
|
78
75
|
# wrapper.inner # => nil
|
79
76
|
# wrapper.build(:inner, str: 'example')
|
80
77
|
# wrapper.inner.str # => 'example'
|
81
78
|
#
|
82
|
-
#
|
83
|
-
#
|
79
|
+
# Assigns values by decorating an instance of the inner message,
|
80
|
+
# passing in our transformer, and calling +assign_attributes+ on
|
81
|
+
# the created decorator object.
|
82
|
+
#
|
83
|
+
# Rebuilds the field if it's already present. Raises an error if
|
84
|
+
# the name of a primitive field is given.
|
85
|
+
#
|
86
|
+
# TODO: do we still need this or is it enough to just use
|
87
|
+
# +decorator.field_name = hash+?
|
84
88
|
#
|
85
89
|
# @param field_name [String|Symbol] The field name to build
|
86
|
-
# @param attributes [Hash] The initial attributes to set on the
|
87
|
-
#
|
90
|
+
# @param attributes [Hash] The initial attributes to set on the
|
91
|
+
# field (as parsed by +assign_attributes+) @return
|
92
|
+
# [Protip::Wrapper] The created field
|
88
93
|
def build(field_name, attributes = {})
|
89
94
|
|
90
|
-
field = message.class.descriptor.detect
|
95
|
+
field = message.class.descriptor.detect do |f|
|
96
|
+
f.name.to_sym == field_name.to_sym
|
97
|
+
end
|
98
|
+
|
91
99
|
if !field
|
92
100
|
raise "No field named #{field_name}"
|
93
101
|
elsif field.type != :message
|
94
102
|
raise "Can only build message fields: #{field_name}"
|
95
|
-
elsif converter.convertible?(field.subtype.msgclass)
|
96
|
-
raise "Cannot build a convertible field: #{field.name}"
|
97
103
|
end
|
98
104
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
105
|
+
built = field.subtype.msgclass.new
|
106
|
+
built_wrapper = self.class.new(built, transformer)
|
107
|
+
built_wrapper.assign_attributes attributes
|
108
|
+
message[field_name.to_s] = built_wrapper.message
|
109
|
+
|
110
|
+
get(field)
|
103
111
|
end
|
104
112
|
|
105
|
-
# Mass assignment of message attributes. Nested messages will be
|
106
|
-
# if they already exist.
|
113
|
+
# Mass assignment of message attributes. Nested messages will be
|
114
|
+
# built if necessary, but not overwritten if they already exist.
|
107
115
|
#
|
108
|
-
# @param attributes [Hash] The attributes to set. Keys are field
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
116
|
+
# @param attributes [Hash] The attributes to set. Keys are field
|
117
|
+
# names. For primitive fields and message fields which are
|
118
|
+
# convertible to/from Ruby objects, values are the same as you'd
|
119
|
+
# pass to the field's setter method. For nested message fields
|
120
|
+
# which can't be converted to/from Ruby objects, values are
|
121
|
+
# attribute hashes which will be passed down to
|
122
|
+
# +assign_attributes+ on the nested field. @return [NilClass]
|
113
123
|
def assign_attributes(attributes)
|
114
124
|
attributes.each do |field_name, value|
|
115
125
|
field = message.class.descriptor.lookup(field_name.to_s) ||
|
116
126
|
(raise ArgumentError.new("Unrecognized field: #{field_name}"))
|
117
|
-
|
118
|
-
#
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
#
|
127
|
+
# Message fields can be set directly by Hash, which either
|
128
|
+
# builds or updates them as appropriate.
|
129
|
+
#
|
130
|
+
# TODO: This kind of oddly assumes that the built message
|
131
|
+
# responds to +assign_attributes+ (as it does when a
|
132
|
+
# +DecoratingTransformer+ is used for the transformation). Can
|
133
|
+
# be removed if we decide the update behavior is unnecessary,
|
134
|
+
# since +DecoratingTransformer+ supports assignment by hash.
|
135
|
+
if field.type == :message && value.is_a?(Hash)
|
136
|
+
(get(field) || build(field.name)).assign_attributes value
|
123
137
|
else
|
124
138
|
set(field, value)
|
125
139
|
end
|
@@ -132,56 +146,52 @@ module Protip
|
|
132
146
|
to_h.as_json
|
133
147
|
end
|
134
148
|
|
135
|
-
# @return [Hash] A hash whose keys are the fields of our message,
|
136
|
-
#
|
149
|
+
# @return [Hash] A hash whose keys are the fields of our message,
|
150
|
+
# and whose values are the Ruby representations (either nested
|
151
|
+
# hashes or transformed messages) of the field values.
|
137
152
|
def to_h
|
138
153
|
hash = {}
|
154
|
+
|
155
|
+
# Use nested +to_h+ on fields which are also decorated messages
|
156
|
+
transform = ->(v) { v.is_a?(self.class) ? v.to_h : v }
|
157
|
+
|
139
158
|
message.class.descriptor.each do |field|
|
140
|
-
value =
|
159
|
+
value = get(field)
|
141
160
|
if field.label == :repeated
|
142
|
-
value.map!{|v| v
|
161
|
+
value.map!{|v| transform[v]}
|
143
162
|
else
|
144
|
-
value =
|
163
|
+
value = transform[value]
|
145
164
|
end
|
146
165
|
hash[field.name.to_sym] = value
|
147
166
|
end
|
148
167
|
hash
|
149
168
|
end
|
150
169
|
|
151
|
-
def ==(
|
152
|
-
|
153
|
-
|
154
|
-
message.class.encode(message) ==
|
155
|
-
|
170
|
+
def ==(decorator)
|
171
|
+
decorator.class == self.class &&
|
172
|
+
decorator.message.class == message.class &&
|
173
|
+
message.class.encode(message) == decorator.message.class.encode(decorator.message) &&
|
174
|
+
transformer == decorator.transformer
|
156
175
|
end
|
157
176
|
|
158
177
|
class << self
|
159
|
-
# Whether the given field returns boolean values. When true, wrappers will respond to +field_name?+
|
160
|
-
# query methods.
|
161
|
-
def boolean?(field)
|
162
|
-
field.type == :bool || (field.type == :message && field.subtype.name == 'google.protobuf.BoolValue')
|
163
|
-
end
|
164
|
-
|
165
178
|
def enum_for_field(field)
|
179
|
+
return nil if field.label == :repeated
|
166
180
|
if field.type == :enum
|
167
181
|
field.subtype
|
168
182
|
elsif field.type == :message && field.subtype.name == 'protip.messages.EnumValue'
|
169
|
-
Protip::
|
183
|
+
Protip::Transformers::EnumTransformer.enum_for_field(field)
|
170
184
|
else
|
171
185
|
nil
|
172
186
|
end
|
173
187
|
end
|
174
|
-
|
175
|
-
# Semi-private check for whether a field should have an associated query method (e.g. +field_name?+).
|
176
|
-
# @return [Boolean] Whether the field should have an associated query method on wrappers.
|
177
|
-
def matchable?(field)
|
178
|
-
return false if field.label == :repeated
|
179
|
-
(nil != enum_for_field(field)) || boolean?(field)
|
180
|
-
end
|
181
188
|
end
|
182
189
|
|
183
190
|
private
|
184
191
|
|
192
|
+
# Get the transformed value of the given field on our message.
|
193
|
+
#
|
194
|
+
# @param field [::Google::Protobuf::FieldDescriptor]
|
185
195
|
def get(field)
|
186
196
|
if field.label == :repeated
|
187
197
|
message[field.name].map{|value| to_ruby_value field, value}
|
@@ -190,19 +200,23 @@ module Protip
|
|
190
200
|
end
|
191
201
|
end
|
192
202
|
|
193
|
-
# Helper for getting values - converts the value for the given
|
203
|
+
# Helper for getting values - converts the value for the given
|
204
|
+
# field to one that we can return to the user
|
205
|
+
#
|
206
|
+
# @param field [::Google::Protobuf::FieldDescriptor] The
|
207
|
+
# descriptor for the field we're fetching.
|
208
|
+
# @param value [Object] The message or primitive value of the
|
209
|
+
# field.
|
194
210
|
def to_ruby_value(field, value)
|
195
211
|
if field.type == :message
|
196
212
|
field_name_sym = field.name.to_sym
|
197
213
|
if nil == value
|
198
214
|
nil
|
199
|
-
elsif converter.convertible?(field.subtype.msgclass)
|
200
|
-
converter.to_object value, field
|
201
215
|
elsif nested_resources.has_key?(field_name_sym)
|
202
216
|
resource_klass = nested_resources[field_name_sym]
|
203
217
|
resource_klass.new value
|
204
218
|
else
|
205
|
-
|
219
|
+
transformer.to_object value, field
|
206
220
|
end
|
207
221
|
else
|
208
222
|
value
|
@@ -217,21 +231,20 @@ module Protip
|
|
217
231
|
end
|
218
232
|
end
|
219
233
|
|
220
|
-
# Helper for setting values - converts the value for the given
|
234
|
+
# Helper for setting values - converts the value for the given
|
235
|
+
# field to one that we can set directly
|
221
236
|
def to_protobuf_value(field, value)
|
222
237
|
if field.type == :message
|
223
238
|
if nil == value
|
224
239
|
nil
|
225
|
-
# This check must happen before the nested_resources check to
|
226
|
-
# are set properly
|
240
|
+
# This check must happen before the nested_resources check to
|
241
|
+
# ensure nested messages are set properly
|
227
242
|
elsif value.is_a?(field.subtype.msgclass)
|
228
243
|
value
|
229
|
-
elsif converter.convertible?(field.subtype.msgclass)
|
230
|
-
converter.to_message value, field.subtype.msgclass, field
|
231
244
|
elsif nested_resources.has_key?(field.name.to_sym)
|
232
245
|
value.message
|
233
246
|
else
|
234
|
-
|
247
|
+
transformer.to_message(value, field)
|
235
248
|
end
|
236
249
|
elsif field.type == :enum
|
237
250
|
value.is_a?(Fixnum) ? value : value.to_sym
|
@@ -241,7 +254,7 @@ module Protip
|
|
241
254
|
end
|
242
255
|
|
243
256
|
def matches?(field, value)
|
244
|
-
enum = Protip::
|
257
|
+
enum = Protip::Decorator.enum_for_field(field)
|
245
258
|
if value.is_a?(Fixnum)
|
246
259
|
sym = enum.lookup_value(value)
|
247
260
|
else
|
@@ -250,7 +263,6 @@ module Protip
|
|
250
263
|
end
|
251
264
|
raise RangeError.new("#{field} has no value #{value}") if nil == sym
|
252
265
|
get(field) == sym
|
253
|
-
|
254
266
|
end
|
255
267
|
|
256
268
|
def method_missing_oneof(oneof, *args)
|
@@ -267,14 +279,18 @@ module Protip
|
|
267
279
|
|
268
280
|
def method_missing_query(name, *args)
|
269
281
|
field = message.class.descriptor.lookup(name[0, name.length - 1])
|
270
|
-
raise NoMethodError
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
elsif
|
275
|
-
|
282
|
+
raise NoMethodError unless field
|
283
|
+
|
284
|
+
if nil != Protip::Decorator.enum_for_field(field) && args.length == 1
|
285
|
+
matches?(field, args[0])
|
286
|
+
elsif args.length == 0
|
287
|
+
value = get(field)
|
288
|
+
|
289
|
+
# Copied in from ActiveSupport +.blank?+
|
290
|
+
blank = (value.respond_to?(:empty?) ? !!value.empty? : !value)
|
291
|
+
!blank
|
276
292
|
else
|
277
|
-
raise
|
293
|
+
raise ArgumentError
|
278
294
|
end
|
279
295
|
end
|
280
296
|
|
data/lib/protip/extensions.rb
CHANGED
data/lib/protip/resource.rb
CHANGED
@@ -18,9 +18,11 @@ require 'active_model/dirty'
|
|
18
18
|
|
19
19
|
require 'forwardable'
|
20
20
|
|
21
|
+
require 'protip'
|
22
|
+
require 'protip/client'
|
21
23
|
require 'protip/error'
|
22
|
-
require 'protip/
|
23
|
-
require 'protip/
|
24
|
+
require 'protip/decorator'
|
25
|
+
require 'protip/transformers/default_transformer'
|
24
26
|
|
25
27
|
require 'protip/messages/array'
|
26
28
|
|
@@ -48,8 +50,8 @@ module Protip
|
|
48
50
|
extend ActiveModel::Translation
|
49
51
|
extend Forwardable
|
50
52
|
|
51
|
-
def_delegator :@
|
52
|
-
def_delegator :@
|
53
|
+
def_delegator :@decorator, :message
|
54
|
+
def_delegator :@decorator, :as_json
|
53
55
|
|
54
56
|
# Initialize housekeeping variables
|
55
57
|
@belongs_to_associations = Set.new
|
@@ -64,7 +66,7 @@ module Protip
|
|
64
66
|
|
65
67
|
attr_reader :message, :nested_resources, :belongs_to_associations, :belongs_to_polymorphic_associations
|
66
68
|
|
67
|
-
attr_writer :base_path, :
|
69
|
+
attr_writer :base_path, :transformer
|
68
70
|
|
69
71
|
def base_path
|
70
72
|
if @base_path == nil
|
@@ -74,8 +76,8 @@ module Protip
|
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
def
|
78
|
-
@
|
79
|
+
def transformer
|
80
|
+
@transformer || ::Protip.default_transformer
|
79
81
|
end
|
80
82
|
|
81
83
|
private
|
@@ -119,21 +121,19 @@ module Protip
|
|
119
121
|
# Allow calls to oneof groups to get the set oneof field
|
120
122
|
def define_oneof_group_methods(message)
|
121
123
|
message.descriptor.each_oneof do |oneof_field|
|
122
|
-
def_delegator :@
|
124
|
+
def_delegator :@decorator, :"#{oneof_field.name}"
|
123
125
|
end
|
124
126
|
end
|
125
127
|
|
126
128
|
# Define attribute readers/writers
|
127
129
|
def define_attribute_accessors(message)
|
128
130
|
message.descriptor.each do |field|
|
129
|
-
def_delegator :@
|
130
|
-
|
131
|
-
def_delegator :@wrapper, :"#{field.name}?"
|
132
|
-
end
|
131
|
+
def_delegator :@decorator, :"#{field.name}"
|
132
|
+
def_delegator :@decorator, :"#{field.name}?"
|
133
133
|
|
134
134
|
define_method "#{field.name}=" do |new_value|
|
135
135
|
old_value = self.message[field.name] # Only compare the raw values
|
136
|
-
@
|
136
|
+
@decorator.send("#{field.name}=", new_value)
|
137
137
|
new_value = self.message[field.name]
|
138
138
|
|
139
139
|
# Need to check that types are the same first, otherwise protobuf gets mad comparing
|
@@ -152,17 +152,29 @@ module Protip
|
|
152
152
|
if query
|
153
153
|
if actions.include?(:show)
|
154
154
|
define_singleton_method :find do |id, query_params = {}|
|
155
|
-
|
156
|
-
|
157
|
-
|
155
|
+
message = nil
|
156
|
+
if query_params.is_a?(query)
|
157
|
+
message = query_params
|
158
|
+
else
|
159
|
+
decorator = ::Protip::Decorator.new(query.new, transformer)
|
160
|
+
decorator.assign_attributes query_params
|
161
|
+
message = decorator.message
|
162
|
+
end
|
163
|
+
::Protip::Resource::SearchMethods.show(self, id, message)
|
158
164
|
end
|
159
165
|
end
|
160
166
|
|
161
167
|
if actions.include?(:index)
|
162
168
|
define_singleton_method :all do |query_params = {}|
|
163
|
-
|
164
|
-
|
165
|
-
|
169
|
+
message = nil
|
170
|
+
if query_params.is_a?(query)
|
171
|
+
message = query_params
|
172
|
+
else
|
173
|
+
decorator = ::Protip::Decorator.new(query.new, transformer)
|
174
|
+
decorator.assign_attributes query_params
|
175
|
+
message = decorator.message
|
176
|
+
end
|
177
|
+
::Protip::Resource::SearchMethods.index(self, message)
|
166
178
|
end
|
167
179
|
end
|
168
180
|
else
|
@@ -183,9 +195,16 @@ module Protip
|
|
183
195
|
def member(action:, method:, request: nil, response: nil)
|
184
196
|
if request
|
185
197
|
define_method action do |request_params = {}|
|
186
|
-
|
187
|
-
|
188
|
-
|
198
|
+
message = nil
|
199
|
+
if request_params.is_a?(request) # Message provided directly
|
200
|
+
message = request_params
|
201
|
+
else # Parameters provided by hash
|
202
|
+
decorator = ::Protip::Decorator.new(request.new, self.class.transformer)
|
203
|
+
decorator.assign_attributes request_params
|
204
|
+
message = decorator.message
|
205
|
+
end
|
206
|
+
::Protip::Resource::ExtraMethods.member self,
|
207
|
+
action, method, message, response
|
189
208
|
end
|
190
209
|
else
|
191
210
|
define_method action do
|
@@ -197,13 +216,16 @@ module Protip
|
|
197
216
|
def collection(action:, method:, request: nil, response: nil)
|
198
217
|
if request
|
199
218
|
define_singleton_method action do |request_params = {}|
|
200
|
-
|
201
|
-
|
219
|
+
message = nil
|
220
|
+
if request_params.is_a?(request) # Message provided directly
|
221
|
+
message = request_params
|
222
|
+
else # Parameters provided by hash
|
223
|
+
decorator = ::Protip::Decorator.new(request.new, transformer)
|
224
|
+
decorator.assign_attributes request_params
|
225
|
+
message = decorator.message
|
226
|
+
end
|
202
227
|
::Protip::Resource::ExtraMethods.collection self,
|
203
|
-
|
204
|
-
method,
|
205
|
-
wrapper.message,
|
206
|
-
response
|
228
|
+
action, method, message, response
|
207
229
|
end
|
208
230
|
else
|
209
231
|
define_singleton_method action do
|
@@ -220,8 +242,9 @@ module Protip
|
|
220
242
|
end
|
221
243
|
|
222
244
|
def belongs_to_polymorphic(association_name, options = {}, &block)
|
223
|
-
# We evaluate the block in the context of a wrapper that
|
224
|
-
# as they're being
|
245
|
+
# We evaluate the block in the context of a wrapper that
|
246
|
+
# stores simple belongs-to associations as they're being
|
247
|
+
# created.
|
225
248
|
nested_association_creator = Class.new do
|
226
249
|
attr_reader :associations
|
227
250
|
def initialize(resource_class)
|
@@ -274,7 +297,7 @@ module Protip
|
|
274
297
|
# since we might just assign attributes to the current instance of the message directly
|
275
298
|
old_attributes[key] = field && field.type == :message && value ? value.clone : value
|
276
299
|
end
|
277
|
-
@
|
300
|
+
@decorator.assign_attributes attributes
|
278
301
|
keys.each do |key|
|
279
302
|
old_value = old_attributes[key]
|
280
303
|
new_value = message[key]
|
@@ -286,7 +309,8 @@ module Protip
|
|
286
309
|
end
|
287
310
|
|
288
311
|
def message=(message)
|
289
|
-
@
|
312
|
+
@decorator = Protip::Decorator.new(message,
|
313
|
+
self.class.transformer, self.class.nested_resources)
|
290
314
|
end
|
291
315
|
|
292
316
|
def save
|