protip 0.20.7 → 0.30.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/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
|