active_model_serializers_binary 0.2.0 → 0.3.1

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.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +10 -8
  3. data/Gemfile +5 -20
  4. data/Gemfile.lock +33 -140
  5. data/MIT-LICENSE +1 -1
  6. data/Rakefile +2 -31
  7. data/active_model_serializers_binary.gemspec +2 -17
  8. data/bin/test +5 -0
  9. data/lib/active_model_serializers_binary/active_model_serializers_binary.rb +142 -124
  10. data/lib/active_model_serializers_binary/base_type.rb +1 -1
  11. data/lib/active_model_serializers_binary/data_types.rb +261 -250
  12. data/lib/active_model_serializers_binary/railtie.rb +4 -0
  13. data/lib/active_model_serializers_binary/version.rb +1 -1
  14. data/test/active_model_serializers_binary_test.rb +3 -3
  15. data/test/dummy/Rakefile +1 -1
  16. data/test/dummy/app/assets/stylesheets/application.css +1 -15
  17. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  18. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  19. data/test/dummy/app/controllers/application_controller.rb +0 -3
  20. data/test/dummy/app/jobs/application_job.rb +7 -0
  21. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  22. data/test/dummy/app/models/application_record.rb +3 -0
  23. data/test/dummy/app/models/product.rb +28 -0
  24. data/test/dummy/app/models/silo.rb +6 -0
  25. data/test/dummy/app/models/type.rb +7 -0
  26. data/test/dummy/app/views/layouts/application.html.erb +10 -9
  27. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  28. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  29. data/test/dummy/bin/rails +3 -3
  30. data/test/dummy/bin/rake +2 -2
  31. data/test/dummy/bin/setup +33 -0
  32. data/test/dummy/config/application.rb +12 -13
  33. data/test/dummy/config/boot.rb +3 -3
  34. data/test/dummy/config/cable.yml +10 -0
  35. data/test/dummy/config/database.yml +3 -3
  36. data/test/dummy/config/environment.rb +1 -1
  37. data/test/dummy/config/environments/development.rb +46 -15
  38. data/test/dummy/config/environments/production.rb +38 -33
  39. data/test/dummy/config/environments/test.rb +34 -13
  40. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  41. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -2
  42. data/test/dummy/config/initializers/inflections.rb +4 -4
  43. data/test/dummy/config/initializers/permissions_policy.rb +11 -0
  44. data/test/dummy/config/locales/en.yml +13 -3
  45. data/test/dummy/config/puma.rb +43 -0
  46. data/test/dummy/config/routes.rb +3 -53
  47. data/test/dummy/config/storage.yml +34 -0
  48. data/test/dummy/config.ru +3 -1
  49. data/test/dummy/db/migrate/20170830125642_create_silos.rb +10 -0
  50. data/test/dummy/db/schema.rb +5 -6
  51. data/test/dummy/lib/test_parser.rb +23 -5
  52. data/test/dummy/public/404.html +6 -6
  53. data/test/dummy/public/422.html +6 -6
  54. data/test/dummy/public/500.html +6 -6
  55. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  56. data/test/dummy/public/apple-touch-icon.png +0 -0
  57. data/test/dummy/test/fixtures/silos.yml +9 -0
  58. data/test/dummy/test/models/silo_test.rb +7 -0
  59. data/test/test_helper.rb +7 -8
  60. metadata +52 -72
  61. data/test/dummy/app/models/producto.rb +0 -47
  62. data/test/dummy/app/models/tipo.rb +0 -18
  63. data/test/dummy/db/migrate/20160608024807_create_productos.rb +0 -16
  64. data/test/dummy/db/migrate/20170817015810_create_tipos.rb +0 -9
@@ -16,10 +16,7 @@ module ActiveModel
16
16
  class_attribute :attr_config
17
17
  class_attribute :serialize_options_global
18
18
  self.attr_config = []
19
- self.serialize_options_global = {
20
- align: false, # Don't align data to byte/word/dword boundary
21
- endianess: :little
22
- }
19
+ self.serialize_options_global = {}
23
20
 
24
21
  def attributes
25
22
  keys = self.attr_config.select{ |attr| attr[:virtual]==true }.map{ |attr| attr[:name] }
@@ -37,6 +34,9 @@ module ActiveModel
37
34
  end
38
35
 
39
36
  after_initialize :initialize_serializer rescue nil
37
+
38
+ endianess :little
39
+ align :dword
40
40
  end
41
41
 
42
42
  module ClassMethods
@@ -49,7 +49,11 @@ module ActiveModel
49
49
  true
50
50
  else
51
51
  attr_name = attr[:name].to_s
52
- if !instance.respond_to? attr_name
52
+ if (instance.class.respond_to? :column_names) && (!instance.class.column_names.include? attr_name)
53
+ attr[:virtual] = true
54
+ attr_accessor attr_name
55
+ true
56
+ elsif !instance.respond_to? attr_name
53
57
  attr[:virtual] = true
54
58
  attr_accessor attr_name
55
59
  true
@@ -59,14 +63,6 @@ module ActiveModel
59
63
  end
60
64
  end
61
65
 
62
- # todo: agrupar parametros en hash (rompe la compatibilidad hacia atras)
63
- def serialize_options( attr_name, coder, count=1, length=1, virtual=false, &block )
64
- self.attr_config.push({:coder => coder, :count => count, :length => length, :block => block, :name => attr_name.to_s, :virtual => virtual})
65
- if virtual==true
66
- attr_accessor attr_name
67
- end
68
- end
69
-
70
66
  def serialize_attribute_options( attr_name, options, &block )
71
67
  self.attr_config.push(options.merge({:name => attr_name.to_s, block: block }))
72
68
  if options[:virtual]==true
@@ -75,12 +71,12 @@ module ActiveModel
75
71
  end
76
72
 
77
73
  def int8( attr_name, options = {}, &block )
78
- options = serialize_options_global.merge(options)
74
+ options = self.serialize_options_global.merge(options)
79
75
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::Int8}), &block
80
76
  end
81
77
 
82
78
  def int16( attr_name, options = {}, &block )
83
- options = serialize_options_global.merge(options)
79
+ options = self.serialize_options_global.merge(options)
84
80
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::Int16}), &block
85
81
  end
86
82
 
@@ -93,7 +89,7 @@ module ActiveModel
93
89
  end
94
90
 
95
91
  def int32( attr_name, options = {}, &block )
96
- options = serialize_options_global.merge(options)
92
+ options = self.serialize_options_global.merge(options)
97
93
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::Int32}), &block
98
94
  end
99
95
 
@@ -102,16 +98,16 @@ module ActiveModel
102
98
  end
103
99
 
104
100
  def int32be( attr_name, options = {}, &block )
105
- int16( attr_name, options.merge({endianess: :big}), &block )
101
+ int32( attr_name, options.merge({endianess: :big}), &block )
106
102
  end
107
103
 
108
104
  def uint8( attr_name, options = {}, &block )
109
- options = serialize_options_global.merge(options)
105
+ options = self.serialize_options_global.merge(options)
110
106
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::UInt8}), &block
111
107
  end
112
108
 
113
109
  def uint16( attr_name, options = {}, &block )
114
- options = serialize_options_global.merge(options)
110
+ options = self.serialize_options_global.merge(options)
115
111
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::UInt16}), &block
116
112
  end
117
113
 
@@ -124,7 +120,7 @@ module ActiveModel
124
120
  end
125
121
 
126
122
  def uint32( attr_name, options = {}, &block )
127
- options = serialize_options_global.merge(options)
123
+ options = self.serialize_options_global.merge(options)
128
124
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::UInt32}), &block
129
125
  end
130
126
 
@@ -137,55 +133,133 @@ module ActiveModel
137
133
  end
138
134
 
139
135
  def bitfield( attr_name, options = {}, &block )
140
- options = serialize_options_global.merge(options)
141
- serialize_attribute_options attr_name, options.merge({coder: DataTypes::BitField}), &block
136
+ options = self.serialize_options_global.merge(options)
137
+ serialize_attribute_options attr_name, options.merge({coder: DataTypes::BitField, type: :bitfield}), &block
142
138
  end
143
139
 
144
140
  def float32( attr_name, options = {}, &block )
145
- options = serialize_options_global.merge(options)
141
+ options = self.serialize_options_global.merge(options)
146
142
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::Float32}), &block
147
143
  end
148
144
 
145
+ def float32le( attr_name, options = {}, &block )
146
+ float32( attr_name, options.merge({endianess: :little}), &block )
147
+ end
148
+
149
+ def float32be( attr_name, options = {}, &block )
150
+ float32( attr_name, options.merge({endianess: :big}), &block )
151
+ end
152
+
149
153
  def float64( attr_name, options = {}, &block )
150
- options = serialize_options_global.merge(options)
151
- serialize_attribute_options attr_name, options.merge({coder: DataTypes::Float32}), &block
154
+ options = self.serialize_options_global.merge(options)
155
+ serialize_attribute_options attr_name, options.merge({coder: DataTypes::Float64}), &block
152
156
  end
153
157
 
158
+ def float64le( attr_name, options = {}, &block )
159
+ float64( attr_name, options.merge({endianess: :little}), &block )
160
+ end
161
+
162
+ def float64be( attr_name, options = {}, &block )
163
+ float64( attr_name, options.merge({endianess: :big}), &block )
164
+ end
165
+
154
166
  def char( attr_name, options = {}, &block )
155
- options = serialize_options_global.merge(options)
167
+ options = self.serialize_options_global.merge(options)
156
168
  serialize_attribute_options attr_name, options.merge({coder: DataTypes::Char}), &block
157
169
  end
158
170
 
159
171
  def bool( attr_name, options = {}, &block )
160
- options = serialize_options_global.merge(options)
161
- serialize_attribute_options attr_name, options.merge({coder: DataTypes::Bool}), &block
172
+ options = self.serialize_options_global.merge(options)
173
+ serialize_attribute_options attr_name, options.merge({coder: DataTypes::Bool, type: :bool}), &block
162
174
  end
163
175
 
164
176
  def nest( attr_name, options={}, &block )
165
- options = serialize_options_global.merge(options)
177
+ options = self.serialize_options_global.merge(options)
166
178
  serialize_attribute_options attr_name, options.merge({type: :nest}), &block
167
179
  end
168
180
 
169
181
  def endianess( type = :little )
170
- serialize_options_global.merge!({endianess: type})
182
+ self.serialize_options_global.merge!({endianess: type})
183
+ end
184
+
185
+ def align( boundary = false )
186
+ self.serialize_options_global.merge!({align: boundary})
171
187
  end
172
188
  end
173
189
 
174
190
  class Serializer #:nodoc:
175
- #attr_reader :options
191
+ attr_accessor :start_address
176
192
 
177
193
  def initialize(serializable, options = nil)
178
194
  @serializable = serializable
179
195
  @options = options ? options.dup : {}
196
+ @current_address = 0
197
+ @start_address = 0
198
+ @current_byte = 0
199
+ @current_bit = 0
200
+ end
201
+
202
+ def current_address= (value)
203
+ @current_address = value
204
+ @current_byte = value.floor
205
+ @current_bit = (value.modulo(1)*8).round
206
+ end
207
+
208
+ def current_address
209
+ @current_address
210
+ end
211
+
212
+ def current_byte= (value)
213
+ @current_byte = value
214
+ @current_address = (@current_byte+@current_bit/8.0)
215
+ end
216
+
217
+ def current_byte
218
+ @current_byte
219
+ end
220
+
221
+ def current_bit= (value)
222
+ @current_bit = value
223
+ @current_address = (@current_byte+@current_bit/8.0)
224
+ end
225
+
226
+ def current_bit
227
+ @current_bit
228
+ end
229
+
230
+ def align_data( attr_options, var )
231
+ # XXX: hay que sacar nest de acá
232
+ if !attr_options[:type].in? [:bitfield, :bool, :nest]
233
+ # Se posiciona al principio de un byte
234
+ if self.current_bit != 0
235
+ self.current_address = self.current_address.ceil
236
+ end
237
+ if @options[:align]==:dword
238
+ # Si el dato es una palabra simple, alinea los datos en el siguiente byte par
239
+ if var.bit_length > 8 and (self.current_address + self.start_address).modulo(2) != 0
240
+ self.current_byte += 1
241
+ end
242
+ # Si el dato es una palabra doble, alinea los datos en la siguiente palabra par
243
+ if var.bit_length > 16 and (self.current_address + self.start_address).modulo(4) != 0
244
+ self.current_byte += 4-self.current_byte%4
245
+ end
246
+ elsif @options[:align]==:word
247
+ # Si el dato es una palabra simple, alinea los datos en el siguiente byte par
248
+ if var.bit_length > 8 and (self.current_address + self.start_address).modulo(2) != 0
249
+ self.current_byte += 1
250
+ end
251
+ end
252
+ end
180
253
  end
181
254
 
182
255
  def dump
183
- serializable_values = @serializable.serializable_hash(@options)
184
- start_address = @options[:start_address] || 0
256
+ serializable_values = @serializable.attributes
257
+ self.start_address = @options[:start_address] || 0
185
258
 
186
259
  buffer = [] # Data Buffer
187
260
  tmp_buffer = [] # Aux Data Buffer
188
- current_address = start_address*2 + 0.0 # Address in bytes
261
+
262
+ self.current_address = self.start_address # Address in bytes
189
263
 
190
264
  @serializable.attr_config.each do |attr_options|
191
265
  attr_name = attr_options[:name]
@@ -197,46 +271,24 @@ module ActiveModel
197
271
  var = attr_options[:coder].new(var_value)
198
272
  end
199
273
 
200
- byte = current_address.floor
201
- bit = (current_address.modulo(1)*8).round
202
-
203
274
  tmp_buffer = var.dump
204
275
 
205
- if @options[:align]
206
- if !attr_options[:type].in? [:bitfield, :bool, :nest]
207
- # Se posiciona al principio de un byte
208
- if bit != 0
209
- byte += 1
210
- bit = 0
211
- current_address = (byte+bit/8.0)
212
- end
213
- # Si el dato es una palabra simple, alinea los datos en el siguiente byte par
214
- if var.bit_length > 8 and current_address.modulo(2) != 0
215
- byte += 1
216
- bit = 0
217
- end
218
- # Si el dato es una palabra doble, alinea los datos en la siguiente palabra par
219
- if var.bit_length > 16 and (current_address + start_address*2).modulo(4) != 0
220
- byte += 4-byte%4
221
- bit = 0
222
- end
223
- end
224
- end
276
+ align_data(attr_options, var) if @options[:align]
225
277
 
226
278
  # Si los datos ocupan mas de un byte concatena los arrays
227
279
  if !attr_options[:type].in? [:bitfield, :bool] and @options[:align]
228
- buffer.insert(byte, tmp_buffer).flatten!
280
+ buffer.insert(self.current_byte, tmp_buffer).flatten!
229
281
  else # En caso de ser bits
230
282
  tmp_buffer.flatten!
231
283
  tmp_bits=tmp_buffer.pack('C*').unpack('b*').first.slice(0,var.size*8)
232
- tmp_buffer=[tmp_bits.rjust(tmp_bits.length+bit,'0')].pack('b*').unpack('C*')
284
+ tmp_buffer=[tmp_bits.rjust(tmp_bits.length+self.current_bit,'0')].pack('b*').unpack('C*')
233
285
 
234
286
  tmp_buffer.each_with_index do |v,i|
235
- buffer[byte+i] = (buffer[byte+i] || 0) | v
287
+ buffer[self.current_byte+i] = (buffer[self.current_byte+i] || 0) | v
236
288
  end
237
289
  end
238
290
 
239
- current_address = (byte+bit/8.0)+var.size
291
+ self.current_address += var.size
240
292
  end
241
293
  buffer.map!{|el| el || 0}
242
294
  end
@@ -244,47 +296,26 @@ module ActiveModel
244
296
  #deserializado
245
297
  def load (buffer=[])
246
298
  serialized_values = {}
247
- start_address = @options[:start_address] || 0
299
+ self.start_address = @options[:start_address] || 0
248
300
 
249
301
  buffer ||= [] # Buffer en bytes
250
302
  tmp_buffer = [] # Buffer temporal en bytes
251
303
 
252
- current_address = start_address*2 + 0.0 # Dirección en bytes
304
+ self.current_address = self.start_address # Dirección en bytes
253
305
 
254
306
  @serializable.attr_config.each do |attr_options|
255
307
  attr_name = attr_options[:name]
256
- byte = current_address.floor
257
- bit = (current_address.modulo(1)*8).round
258
308
 
259
309
  var = attr_options[:coder].new(attr_options.merge(parent: @serializable)) #creo objeto del tipo de dato pasado
260
310
 
261
- if @options[:align]
262
- if !attr_options[:type].in? [:bitfield, :bool, :nest]
263
- # Se posiciona al principio de un byte
264
- if bit != 0
265
- byte += 1
266
- bit = 0
267
- current_address = (byte+bit/8.0)
268
- end
269
- # Si el dato es una palabra simple, alinea los datos en el siguiente byte par
270
- if var.bit_length > 8 and current_address.modulo(2) != 0
271
- byte += 1
272
- bit = 0
273
- end
274
- # Si el dato es una palabra doble, alinea los datos en la siguiente palabra par
275
- if var.bit_length > 16 and (current_address + start_address*2).modulo(4) != 0
276
- byte += 4-byte%4
277
- bit = 0
278
- end
279
- end
280
- end
311
+ align_data(attr_options, var) if @options[:align]
281
312
 
282
313
  # Si los datos ocupan mas de un byte, obtiene los bytes completos del buffer original
283
314
  if !attr_options[:type].in? [:bitfield, :bool] and @options[:align]
284
- result_deserialized=var.load(buffer.slice(byte, var.size))
315
+ result_deserialized=var.load(buffer.slice(self.current_byte, var.size))
285
316
  else # En caso de ser bits
286
- tmp_buffer = buffer.slice(byte, (var.size+bit/8.0).ceil)
287
- result_deserialized=var.load([tmp_buffer.pack('C*').unpack('b*').first.slice(bit,var.size*8)].pack('b*').unpack('C*'))
317
+ tmp_buffer = buffer.slice(self.current_byte, (var.size+self.current_bit/8.0).ceil)
318
+ result_deserialized=var.load([tmp_buffer.pack('C*').unpack('b*').first.slice(self.current_bit,var.size*8)].pack('b*').unpack('C*'))
288
319
  end
289
320
 
290
321
  if attr_options[:type] == :nest
@@ -292,7 +323,7 @@ module ActiveModel
292
323
  else
293
324
  serialized_values["#{attr_name}"] = result_deserialized.count>1 ? result_deserialized : result_deserialized.first
294
325
  end
295
- current_address = (byte+bit/8.0)+var.size
326
+ self.current_address += var.size
296
327
  end
297
328
 
298
329
  # Asigno los datos leidos
@@ -304,40 +335,19 @@ module ActiveModel
304
335
 
305
336
  # Return size of object in bytes
306
337
  def size
307
- serializable_values = @serializable.serializable_hash(@options)
308
- start_address = @options[:start_address] || 0
338
+ serializable_values = @serializable.attributes
339
+ self.start_address = @options[:start_address] || 0
309
340
 
310
341
  current_address = 0.0 # Dirección en bytes
311
342
 
312
343
  @serializable.attr_config.each do |attr_options|
313
344
  var = attr_options[:coder].new(attr_options.merge(parent: @serializable))
314
345
 
315
- byte = current_address.floor
316
- bit = (current_address.modulo(1)*8).round
317
-
318
- if @options[:align]
319
- if !attr_options[:type].in? [:bitfield, :bool, :nest]
320
- # Se posiciona al principio de un byte
321
- if bit != 0
322
- byte += 1
323
- bit = 0
324
- current_address = (byte+bit/8.0)
325
- end
326
- # Si el dato es una palabra simple, alinea los datos en el siguiente byte par
327
- if var.bit_length > 8 and current_address.modulo(2) != 0
328
- byte += 1
329
- bit = 0
330
- end
331
- # Si el dato es una palabra doble, alinea los datos en la siguiente palabra par
332
- if var.bit_length > 16 and (current_address + start_address*2).modulo(4) != 0
333
- byte += 4-byte%4
334
- bit = 0
335
- end
336
- end
337
- end
338
- current_address = (byte+bit/8.0)+var.size
346
+ align_data(attr_options, var) if @options[:align]
347
+
348
+ self.current_address += var.size
339
349
  end
340
- current_address-start_address
350
+ self.current_address-self.start_address
341
351
  end
342
352
 
343
353
  end #close class serializer
@@ -351,9 +361,9 @@ module ActiveModel
351
361
  # => [98, 111, 98, 0, 0, 0, 0, 0, 0, 0, 22, 0, 1]
352
362
  #
353
363
  def to_bytes(options = {}, &block)
354
- options = self.serialize_options_global.deep_merge(options)
364
+ options = self.serialize_options_global.merge(options)
355
365
  if block_given?
356
- yield self
366
+ yield self, options
357
367
  end
358
368
  Serializer.new(self, options).dump
359
369
  end
@@ -362,8 +372,12 @@ module ActiveModel
362
372
 
363
373
  def to_words(options = {}, &block)
364
374
  data = to_bytes(options, &block)
365
- byte_count = (data.count/2.0).ceil*2
366
- data.fill(0, data.count...byte_count).pack('C*').unpack('v*')
375
+ data.push(0) if data.count.odd?
376
+ if self.serialize_options_global[:endianess]==:big
377
+ data.pack('C*').unpack('S>*')
378
+ else
379
+ data.pack('C*').unpack('S<*')
380
+ end
367
381
  end
368
382
 
369
383
  # Sets the model +attributes+ from an Binary string. Returns +self+.
@@ -403,11 +417,11 @@ module ActiveModel
403
417
  # @yield code block to execute after deserialization
404
418
  #
405
419
  def from_bytes(buffer, options = {}, &block)
406
- options = self.serialize_options_global.deep_merge(options)
420
+ options = self.serialize_options_global.merge(options)
407
421
  retVal = Serializer.new(self, options).load buffer
408
422
 
409
423
  if block_given?
410
- yield self
424
+ yield self, options
411
425
  end
412
426
  retVal
413
427
  end
@@ -415,12 +429,16 @@ module ActiveModel
415
429
  alias_method :load, :from_bytes
416
430
 
417
431
  def from_words(buffer = [], options = {}, &block)
418
- data = buffer.pack('v*').unpack('C*')
432
+ if self.serialize_options_global[:endianess]==:big
433
+ data = buffer.pack('S>*').unpack('C*')
434
+ else
435
+ data = buffer.pack('S<*').unpack('C*')
436
+ end
419
437
  from_bytes(data, options, &block)
420
438
  end
421
439
 
422
440
  def size(options = {})
423
- options = self.serialize_options_global.deep_merge(options)
441
+ options = self.serialize_options_global.merge(options)
424
442
  Serializer.new(self, options).size
425
443
  end
426
444
 
@@ -48,7 +48,7 @@ module DataTypes
48
48
  default_value
49
49
  else
50
50
  case type
51
- when :float32
51
+ when :float32, :float64
52
52
  v.to_f
53
53
  when :char
54
54
  v.to_s[0...length]