csverizer 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5409fdd6a87c19c96e4665e6b3cd1c59b1d5daf0aa00299d54af55120d1dfbbc
4
- data.tar.gz: fd8be4d1bdf9049e4471c0e5c442e130ce05eeb2c82ec3e5c8c61740ea18908d
3
+ metadata.gz: 55eee804461744dce24ad41fccb06203f0eb9788849cedfd22ddbf3188398394
4
+ data.tar.gz: 2468a5c68cec4beca7fbe0d8c71f52dc81e2b129fb4c59b54653f37816fca595
5
5
  SHA512:
6
- metadata.gz: 6db78ad668319a078b82044292f3167b61859c3164ab4a7f9dba0407013e0a84eb520371399d0b85c403b148a84cd940c3ca47f5ee7077d51d9e21872441390e
7
- data.tar.gz: 803c802c2e6e2fc5fc3987b7d28db56c4eac56098844bde9afb436a373153c021cb4286d8dd26a976722f1c22c4b25239f55d05ad481366464cca51b0d3ef4ff
6
+ metadata.gz: 59abeb2c9a6a7eeb91d1d15bfddeb69914fc67c2eed7d6647ed4c76f4186b39f3d1834e60bdef3d21e626cdbb3b658ce1985a02afd0d3db4b76c847a1a71d094
7
+ data.tar.gz: d4e4531e84adc53d16fda163fc83743ad97d7f5de0f9f820862233b9f326671591832d2c32d84779bbc852e00868acbbadee873ad3a3544e654771a29754114a
@@ -9,25 +9,19 @@ module ActiveModel
9
9
  @options = options
10
10
  end
11
11
 
12
- def to_a
13
- return ActiveModel::Csverizer.new(nil).to_a if @objects.nil?
14
- @objects.collect do |object|
15
- serializer = @each_serializer || ActiveModel::CsverizerFactory
16
- serializer.new(object, @options).to_a
17
- end
18
- end
19
-
20
12
  def to_csv
13
+ serializer = @each_serializer || ActiveModel::CsverizerFactory
14
+ serializer = serializer.new(@objects.first, @options)
15
+ options_hash = @options.slice(:serializer)
16
+
21
17
  CSV.generate do |csv|
22
- csv << attribute_names
23
- to_a.each { |record| csv << record.first }
24
- end
25
- end
18
+ csv << serializer.attribute_names
19
+ serializer = @each_serializer || ActiveModel::CsverizerFactory
26
20
 
27
- def attribute_names
28
- return [] unless @objects
29
- serializer = @each_serializer || ActiveModel::CsverizerFactory
30
- serializer.new(@objects.first, @options).attribute_names
21
+ @objects.each do |record|
22
+ serializer.new(record, options_hash).to_a.each { |record| csv << record }
23
+ end
24
+ end
31
25
  end
32
26
  end
33
27
  end
@@ -2,44 +2,316 @@ require 'csv'
2
2
 
3
3
  module ActiveModel
4
4
  class Csverizer
5
- @_attributes = []
6
- @associations = []
7
- @root = true
5
+ @mutex = Mutex.new
8
6
 
9
7
  class << self
10
- attr_accessor :_attributes, :associations, :root
8
+ def inherited(base)
9
+ base._root = _root
10
+ base._attributes = (_attributes || []).dup
11
+ base._associations = (_associations || {}).dup
12
+ end
13
+
14
+ def setup
15
+ @mutex.synchronize do
16
+ yield CONFIG
17
+ end
18
+ end
19
+
20
+ EMBED_IN_ROOT_OPTIONS = [
21
+ :include,
22
+ :embed_in_root,
23
+ :embed_in_root_key,
24
+ :embed_namespace
25
+ ].freeze
26
+
27
+ def embed(type, options={})
28
+ CONFIG.embed = type
29
+ if EMBED_IN_ROOT_OPTIONS.any? { |opt| options[opt].present? }
30
+ CONFIG.embed_in_root = true
31
+ end
32
+ if options[:embed_in_root_key].present?
33
+ CONFIG.embed_in_root_key = options[:embed_in_root_key]
34
+ end
35
+ ActiveSupport::Deprecation.warn <<-WARN
36
+ ** Notice: embed is deprecated. **
37
+ The use of .embed method on a Serializer will be soon removed, as this should have a global scope and not a class scope.
38
+ Please use the global .setup method instead:
39
+ ActiveModel::Serializer.setup do |config|
40
+ config.embed = :#{type}
41
+ config.embed_in_root = #{CONFIG.embed_in_root || false}
42
+ end
43
+ WARN
44
+ end
45
+
46
+ def format_keys(format)
47
+ @key_format = format
48
+ end
49
+ attr_reader :key_format
50
+
51
+ def serializer_for(resource, options = {})
52
+ if resource.respond_to?(:serializer_class)
53
+ resource.serializer_class
54
+ elsif resource.respond_to?(:to_ary)
55
+ if Object.constants.include?(:ArraySerializer)
56
+ ::ArraySerializer
57
+ else
58
+ ArraySerializer
59
+ end
60
+ else
61
+ klass_name = build_serializer_class(resource, options)
62
+ Serializer.serializers_cache.fetch_or_store(klass_name) do
63
+ _const_get(klass_name)
64
+ end
65
+ end
66
+ end
67
+
68
+ attr_accessor :_root, :_attributes, :_associations
69
+ alias root _root=
70
+ alias root= _root=
71
+
72
+ def root_name
73
+ if name
74
+ root_name = name.demodulize.underscore.sub(/_serializer$/, '')
75
+ CONFIG.plural_default_root ? root_name.pluralize : root_name
76
+ end
77
+ end
78
+
79
+ def attributes(*attrs)
80
+ attrs.each do |attr|
81
+ striped_attr = strip_attribute attr
82
+
83
+ @_attributes << striped_attr
84
+
85
+ define_method striped_attr do
86
+ object.read_attribute_for_serialization attr
87
+ end unless method_defined?(attr)
88
+ end
89
+ end
90
+
91
+ def has_one(*attrs)
92
+ associate(Association::HasOne, *attrs)
93
+ end
94
+
95
+ def has_many(*attrs)
96
+ associate(Association::HasMany, *attrs)
97
+ end
98
+
99
+ def serializers_cache
100
+ @serializers_cache ||= Concurrent::Map.new
101
+ end
102
+
103
+ private
104
+
105
+ def strip_attribute(attr)
106
+ symbolized = attr.is_a?(Symbol)
107
+
108
+ attr = attr.to_s.gsub(/\?\Z/, '')
109
+ attr = attr.to_sym if symbolized
110
+ attr
111
+ end
112
+
113
+ def build_serializer_class(resource, options)
114
+ "".tap do |klass_name|
115
+ klass_name << "#{options[:namespace]}::" if options[:namespace]
116
+ klass_name << options[:prefix].to_s.classify if options[:prefix]
117
+ klass_name << "#{resource.class.name}Serializer"
118
+ end
119
+ end
120
+
121
+ def associate(klass, *attrs)
122
+ options = attrs.extract_options!
123
+
124
+ attrs.each do |attr|
125
+ define_method attr do
126
+ object.send attr
127
+ end unless method_defined?(attr)
128
+
129
+ @_associations[attr] = klass.new(attr, options)
130
+ end
131
+ end
11
132
  end
12
133
 
13
- def self.inherited(base)
14
- base._attributes = []
15
- base.associations = []
16
- base.root = true
134
+ def initialize(object, options={})
135
+ @object = object
136
+ @scope = options[:scope]
137
+ @root = options.fetch(:root, self.class._root)
138
+ @polymorphic = options.fetch(:polymorphic, false)
139
+ @meta_key = options[:meta_key] || :meta
140
+ @meta = options[@meta_key]
141
+ @wrap_in_array = options[:_wrap_in_array]
142
+ @only = options[:only] ? Array(options[:only]) : nil
143
+ @except = options[:except] ? Array(options[:except]) : nil
144
+ @key_format = options[:key_format]
145
+ @context = options[:context]
146
+ @namespace = options[:namespace]
147
+ @prefix = options.fetch(:prefix, '')
148
+ end
149
+ attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context, :polymorphic
150
+
151
+ def json_key
152
+ key = if root == true || root.nil?
153
+ self.class.root_name
154
+ else
155
+ root
156
+ end
157
+
158
+ key_format == :lower_camel && key.present? ? key.camelize(:lower) : key
17
159
  end
18
160
 
19
- def self.attributes(*attributes)
20
- @_attributes = @_attributes.concat(attributes)
161
+ def attributes
162
+ filter(self.class._attributes.dup).each_with_object({}) do |name, hash|
163
+ hash[name] = send(name)
164
+ end
21
165
  end
22
166
 
23
- def self.has_one(associated)
24
- @associations << {
25
- associated: associated,
26
- serializer: ActiveModel::CsverizerFactory
27
- }
167
+ def associations(options={})
168
+ associations = self.class._associations
169
+ included_associations = filter(associations.keys)
170
+ associations.each_with_object({}) do |(name, association), hash|
171
+ if included_associations.include? name
172
+ if association.embed_ids?
173
+ ids = serialize_ids association
174
+ if association.embed_namespace?
175
+ hash = hash[association.embed_namespace] ||= {}
176
+ hash[association.key] = ids
177
+ else
178
+ hash[association.key] = ids
179
+ end
180
+ elsif association.embed_objects?
181
+ if association.embed_namespace?
182
+ hash = hash[association.embed_namespace] ||= {}
183
+ end
184
+ hash[association.embedded_key] = serialize association, options
185
+ end
186
+ end
187
+ end
28
188
  end
29
189
 
30
- def self.has_many(associated)
31
- @associations << {
32
- associated: associated,
33
- serializer: ActiveModel::CsvArraySerializer
34
- }
190
+ def filter(keys)
191
+ if @only
192
+ keys & @only
193
+ elsif @except
194
+ keys - @except
195
+ else
196
+ keys
197
+ end
35
198
  end
36
199
 
37
- attr_reader :object
200
+ def embedded_in_root_associations
201
+ associations = self.class._associations
202
+ included_associations = filter(associations.keys)
203
+ associations.each_with_object({}) do |(name, association), hash|
204
+ if included_associations.include? name
205
+ association_serializer = build_serializer(association)
206
+ # we must do this always because even if the current association is not
207
+ # embedded in root, it might have its own associations that are embedded in root
208
+ hash.merge!(association_serializer.embedded_in_root_associations) do |key, oldval, newval|
209
+ if oldval.respond_to?(:to_ary)
210
+ [oldval, newval].flatten.uniq
211
+ else
212
+ oldval.merge(newval) { |_, oldval, newval| [oldval, newval].flatten.uniq }
213
+ end
214
+ end
215
+
216
+ if association.embed_in_root?
217
+ if association.embed_in_root_key?
218
+ hash = hash[association.embed_in_root_key] ||= {}
219
+ end
38
220
 
39
- def initialize(object, options = {})
40
- @object = object
41
- @root = options.fetch(:root, self.class.root)
42
- @prefix = options.fetch(:prefix, '')
221
+ serialized_data = association_serializer.serializable_object
222
+ key = association.root_key
223
+ if hash.has_key?(key)
224
+ hash[key].concat(serialized_data).uniq!
225
+ else
226
+ hash[key] = serialized_data
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ def build_serializer(association)
234
+ object = send(association.name)
235
+ association.build_serializer(object, association_options_for_serializer(association))
236
+ end
237
+
238
+ def association_options_for_serializer(association)
239
+ prefix = association.options[:prefix]
240
+ namespace = association.options[:namespace] || @namespace || self.namespace
241
+
242
+ { scope: scope }.tap do |opts|
243
+ opts[:namespace] = namespace if namespace
244
+ opts[:prefix] = prefix if prefix
245
+ end
246
+ end
247
+
248
+ def serialize(association,options={})
249
+ build_serializer(association).serializable_object(options)
250
+ end
251
+
252
+ def serialize_ids(association)
253
+ associated_data = send(association.name)
254
+ if associated_data.respond_to?(:to_ary)
255
+ associated_data.map { |elem| serialize_id(elem, association) }
256
+ else
257
+ serialize_id(associated_data, association) if associated_data
258
+ end
259
+ end
260
+
261
+ def key_format
262
+ @key_format || self.class.key_format || CONFIG.key_format
263
+ end
264
+
265
+ def format_key(key)
266
+ if key_format == :lower_camel
267
+ key.to_s.camelize(:lower)
268
+ else
269
+ key
270
+ end
271
+ end
272
+
273
+ def convert_keys(hash)
274
+ Hash[hash.map do |k,v|
275
+ key = if k.is_a?(Symbol)
276
+ format_key(k).to_sym
277
+ else
278
+ format_key(k)
279
+ end
280
+
281
+ [key ,v]
282
+ end]
283
+ end
284
+
285
+ attr_writer :serialization_options
286
+ def serialization_options
287
+ @serialization_options || {}
288
+ end
289
+
290
+ def serializable_object(options={})
291
+ self.serialization_options = options
292
+ return @wrap_in_array ? [] : nil if @object.nil?
293
+ hash = attributes
294
+ hash.merge! associations(options)
295
+ hash = convert_keys(hash) if key_format.present?
296
+ hash = { :type => type_name(@object), type_name(@object) => hash } if @polymorphic
297
+ @wrap_in_array ? [hash] : hash
298
+ end
299
+ alias_method :serializable_hash, :serializable_object
300
+
301
+ def serialize_id(elem, association)
302
+ id = elem.read_attribute_for_serialization(association.embed_key)
303
+ association.polymorphic? ? { id: id, type: type_name(elem) } : id
304
+ end
305
+
306
+ def type_name(elem)
307
+ elem.class.to_s.demodulize.underscore.to_sym
308
+ end
309
+
310
+ def to_csv
311
+ CSV.generate do |csv|
312
+ csv << attribute_names
313
+ to_a.each { |record| csv << record }
314
+ end
43
315
  end
44
316
 
45
317
  def to_a
@@ -55,13 +327,6 @@ module ActiveModel
55
327
  values
56
328
  end
57
329
 
58
- def to_csv
59
- CSV.generate do |csv|
60
- csv << attribute_names if @root
61
- to_a.each { |record| csv << record }
62
- end
63
- end
64
-
65
330
  def attribute_names
66
331
  names = self.class._attributes.collect do |attribute|
67
332
  @prefix + attribute.to_s
@@ -71,8 +336,6 @@ module ActiveModel
71
336
  end
72
337
  end
73
338
 
74
- private
75
-
76
339
  def read_attribute(name)
77
340
  return send(name) if respond_to?(name)
78
341
  object.read_attribute_for_serialization(name)
@@ -83,7 +346,7 @@ module ActiveModel
83
346
  end
84
347
 
85
348
  def associated_serializers
86
- @associated_serializers ||= self.class.associations.collect do |hash|
349
+ @associated_serializers ||= self.class._associations.collect do |hash|
87
350
  object = read_attribute(hash[:associated])
88
351
  hash[:serializer].new(object, prefix: hash[:associated].to_s + '_')
89
352
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  class Csverizer
3
- VERSION = '0.0.6'
3
+ VERSION = '0.0.7'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csverizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Cunningham
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-02-04 00:00:00.000000000 Z
12
+ date: 2020-05-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel