delineate 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/delineate.gemspec +4 -3
- data/lib/delineate/attribute_map.rb +40 -98
- data/lib/delineate/map_attributes.rb +3 -1
- data/lib/delineate/resolve.rb +97 -0
- data/lib/delineate/serializers/csv_serializer.rb +7 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6242128797a0959401231046cd088cf6c4d3cd5
|
4
|
+
data.tar.gz: 3c04565b00c65ee61c96c1f87b9154722060ef9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5412696a28e4d9c40b97496ab6d5182943adbfb3b3b01e59bd4203d4ee47b4feef056f3348300cba91ba3990424ec83e932742e24b81b7635c2a848b21e07b5
|
7
|
+
data.tar.gz: 87587a174725ecd3d79a68abd1a38163ae1d3fc69a9b6e11d2dfa17e348340a5cbf9a713f0dc8000bddfd65f43a3f6e62cecc0584cd2adc359fdd7457836cc69
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.2
|
data/delineate.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: delineate 0.6.
|
5
|
+
# stub: delineate 0.6.2 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "delineate"
|
9
|
-
s.version = "0.6.
|
9
|
+
s.version = "0.6.2"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tom Smith"]
|
14
|
-
s.date = "2014-02-
|
14
|
+
s.date = "2014-02-28"
|
15
15
|
s.description = "ActiveRecord serializer DSL for mapping model attributes and associations. Similar to ActiveModel Serializers with many enhancements including bi-directional support, i.e. deserialization."
|
16
16
|
s.email = "tsmith@landfall.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/delineate/attribute_map.rb",
|
35
35
|
"lib/delineate/map_attributes.rb",
|
36
36
|
"lib/delineate/map_serializer.rb",
|
37
|
+
"lib/delineate/resolve.rb",
|
37
38
|
"lib/delineate/schema.rb",
|
38
39
|
"lib/delineate/serialization.rb",
|
39
40
|
"lib/delineate/serializers/csv_serializer.rb",
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/hash/deep_dup.rb'
|
2
2
|
|
3
|
+
require 'delineate/resolve'
|
3
4
|
require 'delineate/serialization'
|
4
5
|
require 'delineate/schema'
|
5
6
|
|
@@ -222,12 +223,10 @@ module Delineate
|
|
222
223
|
@attributes[name] = options
|
223
224
|
|
224
225
|
model_attr = (options[:model_attr] || name).to_sym
|
225
|
-
model_attr = define_attr_methods(name, model_attr, options)
|
226
|
+
model_attr = define_attr_methods(name, model_attr, options)
|
226
227
|
|
227
228
|
if options[:access] != :ro
|
228
|
-
|
229
|
-
raise "Expected 'attr_accessible :#{model_attr}' in #{@klass_name}"
|
230
|
-
end
|
229
|
+
validate_attr_writable(model_attr)
|
231
230
|
@write_attributes[name] = model_attr
|
232
231
|
end
|
233
232
|
end
|
@@ -246,12 +245,7 @@ module Delineate
|
|
246
245
|
|
247
246
|
attr_map.instance_eval(&blk) if block_given?
|
248
247
|
|
249
|
-
|
250
|
-
raise ArgumentError, "Map association '#{name}' in class #{@klass_name} specifies :replace but has empty block"
|
251
|
-
end
|
252
|
-
if options[:access] != :ro and !klass.accessible_attributes.include?(model_attr.to_s+'_attributes')
|
253
|
-
raise "Expected attr_accessible and/or accepts_nested_attributes_for :#{model_attr} in #{@klass_name} model"
|
254
|
-
end
|
248
|
+
validate_assoc_map(name, model_attr, attr_map, options)
|
255
249
|
|
256
250
|
@associations[name] = {:klass_name => reflection.class_name, :options => options,
|
257
251
|
:attr_map => attr_map.empty? ? nil : attr_map,
|
@@ -263,7 +257,7 @@ module Delineate
|
|
263
257
|
return if other_attr_map.nil?
|
264
258
|
|
265
259
|
@attributes = @attributes.deep_merge(other_attr_map.attributes)
|
266
|
-
@associations.deep_merge
|
260
|
+
@associations = @associations.deep_merge(other_attr_map.associations)
|
267
261
|
|
268
262
|
@write_attributes = {:_destroy => :_destroy}
|
269
263
|
@attributes.each {|k, v| @write_attributes[k] = (v[:model_attr] || k) unless v[:access] == :ro}
|
@@ -297,31 +291,6 @@ module Delineate
|
|
297
291
|
self
|
298
292
|
end
|
299
293
|
|
300
|
-
def resolved?
|
301
|
-
@resolved
|
302
|
-
end
|
303
|
-
|
304
|
-
# Will raise an exception of the map cannot be fully resolved
|
305
|
-
def resolve!
|
306
|
-
resolve(:must_resolve)
|
307
|
-
self
|
308
|
-
end
|
309
|
-
|
310
|
-
# Attempts to resolve the map and the maps it depends on. If must_resolve is truthy, will
|
311
|
-
# raise an exception if map cannot be resolved.
|
312
|
-
def resolve(must_resolve = false, resolving = [])
|
313
|
-
return true if @resolved
|
314
|
-
return true if resolving.include?(@klass_name) # prevent infinite recursion
|
315
|
-
|
316
|
-
resolving.push(@klass_name)
|
317
|
-
|
318
|
-
result = resolve_associations(must_resolve, resolving)
|
319
|
-
result = false unless resolve_sti_baseclass(must_resolve, resolving)
|
320
|
-
|
321
|
-
resolving.pop
|
322
|
-
@resolved = result
|
323
|
-
end
|
324
|
-
|
325
294
|
|
326
295
|
protected
|
327
296
|
|
@@ -354,8 +323,10 @@ module Delineate
|
|
354
323
|
raise ArgumentError, "Association '#{association}' in model #{@klass_name} is not defined" if reflection.nil?
|
355
324
|
begin
|
356
325
|
reflection.klass
|
357
|
-
rescue
|
358
|
-
|
326
|
+
rescue => e
|
327
|
+
msg = "Cannot resolve association class '#{reflection.class_name}' from model '#{@klass_name}'"
|
328
|
+
msg += "\n#{e.message}"
|
329
|
+
raise NameError, msg
|
359
330
|
end
|
360
331
|
end
|
361
332
|
end
|
@@ -384,7 +355,7 @@ module Delineate
|
|
384
355
|
validate_access_option(options[:access])
|
385
356
|
options[:model_attr] = options.delete(:using) if options.key?(:using)
|
386
357
|
|
387
|
-
raise ArgumentError, 'Cannot specify :override or provide block with :polymorphic' if options[:polymorphic]
|
358
|
+
raise ArgumentError, 'Cannot specify :override or provide block with :polymorphic' if options[:polymorphic] && (blk or options[:override])
|
388
359
|
raise ArgumentError, 'Option :override must = :replace or :merge' unless !options.key?(:override) || [:merge, :replace].include?(options[:override])
|
389
360
|
end
|
390
361
|
|
@@ -411,70 +382,37 @@ module Delineate
|
|
411
382
|
raise ArgumentError, 'Invalid value for :access option' if opt and !VALID_ACCESS_OPTIONS.include?(opt)
|
412
383
|
end
|
413
384
|
|
414
|
-
def
|
415
|
-
|
416
|
-
|
417
|
-
map
|
418
|
-
end
|
419
|
-
|
420
|
-
def resolve_associations(must_resolve, resolving)
|
421
|
-
result = true
|
422
|
-
|
423
|
-
@associations.each do |assoc_name, assoc|
|
424
|
-
if detect_circular_merge(assoc)
|
425
|
-
raise "Detected attribute map circular merge references: class=#{@klass_name}, association=#{assoc_name}"
|
426
|
-
end
|
427
|
-
|
428
|
-
assoc_map = assoc[:attr_map] || assoc[:klass_name].constantize.attribute_maps.try(:fetch, @name, nil)
|
429
|
-
if assoc_map && !assoc_map.resolved?
|
430
|
-
if assoc_map.resolve(must_resolve, resolving) && merge_option?(assoc[:options]) && assoc[:attr_map]
|
431
|
-
merge_map = assoc[:klass_name].constantize.attribute_maps[@name]
|
432
|
-
assoc_map = merge_map.dup.merge!(assoc_map, :with_options => true, :with_state => true)
|
433
|
-
end
|
434
|
-
end
|
435
|
-
assoc[:attr_map] = assoc_map
|
436
|
-
|
437
|
-
if assoc_map.nil? or !assoc_map.resolve(false, resolving)
|
438
|
-
result = false
|
439
|
-
raise "Cannot resolve map for association :#{assoc_name} in #{@klass_name}:#{@name} map" if must_resolve
|
440
|
-
end
|
385
|
+
def validate_attr_writable(attr_name)
|
386
|
+
if attr_name.to_s != klass.primary_key && !klass.accessible_attributes.detect { |a| a == attr_name.to_s }
|
387
|
+
raise "Expected 'attr_accessible :#{attr_name}' in #{@klass_name}"
|
441
388
|
end
|
442
|
-
|
443
|
-
result
|
444
389
|
end
|
445
390
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
if
|
451
|
-
|
452
|
-
@resolved = @sti_baseclass_merged = true
|
453
|
-
self.copy(klass.superclass.attribute_maps[@name].dup.merge!(self))
|
454
|
-
else
|
455
|
-
result = false
|
456
|
-
raise "Can't resolve base class map for #{@klass_name}:#{@name} map" if must_resolve
|
457
|
-
end
|
391
|
+
def validate_assoc_map(name, model_attr, attr_map, options)
|
392
|
+
if !merge_option?(options) && attr_map.empty?
|
393
|
+
raise ArgumentError, "Map association '#{name}' in class #{@klass_name} specifies :replace but has empty block"
|
394
|
+
end
|
395
|
+
if options[:access] != :ro and !klass.accessible_attributes.include?(model_attr.to_s+'_attributes')
|
396
|
+
raise "Expected attr_accessible and/or accepts_nested_attributes_for :#{model_attr} in #{@klass_name} model"
|
458
397
|
end
|
459
|
-
|
460
|
-
result
|
461
398
|
end
|
462
399
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
map.associations.each_value do |a|
|
470
|
-
return true if a[:klass_name] == @klass_name && merge_option?(a[:options]) && a[:attr_map]
|
471
|
-
end
|
400
|
+
def validate_attr_accessor(read_or_write, options)
|
401
|
+
return unless (accessor = options[read_or_write])
|
402
|
+
raise ArgumentError, "Invalid parameter for #{read_or_write}" unless (accessor.is_a?(Symbol) || accessor.is_a?(Proc))
|
403
|
+
accessor
|
404
|
+
end
|
472
405
|
|
473
|
-
|
406
|
+
def validate(map, class_name)
|
407
|
+
raise(NameError, "Expected attribute map :#{@name} to be defined for class '#{class_name}'") if map.nil?
|
408
|
+
map.resolve! unless map.resolved?
|
409
|
+
map
|
474
410
|
end
|
475
411
|
|
476
412
|
# Defines custom read/write attribute methods
|
477
413
|
def define_attr_methods(name, model_attr, options)
|
414
|
+
return model_attr if is_model_attr?(model_attr)
|
415
|
+
|
478
416
|
read_model_attr = define_attr_reader_method(name, model_attr, options)
|
479
417
|
write_model_attr = define_attr_writer_method(name, model_attr, options)
|
480
418
|
|
@@ -486,10 +424,9 @@ module Delineate
|
|
486
424
|
end
|
487
425
|
|
488
426
|
def define_attr_reader_method(name, model_attr, options)
|
489
|
-
return unless (reader =
|
490
|
-
raise ArgumentError, 'Invalid parameter for :read' unless (reader.is_a?(Symbol) || reader.is_a?(Proc))
|
427
|
+
return unless (reader = validate_attr_accessor(:read, options))
|
491
428
|
|
492
|
-
returning(
|
429
|
+
returning accessor_method_name(name, model_attr) do |method_name|
|
493
430
|
if reader.is_a?(Symbol)
|
494
431
|
klass.class_eval %(
|
495
432
|
def #{method_name}
|
@@ -511,10 +448,9 @@ module Delineate
|
|
511
448
|
end
|
512
449
|
|
513
450
|
def define_attr_writer_method(name, model_attr, options)
|
514
|
-
return unless (writer =
|
515
|
-
raise ArgumentError, 'Invalid parameter for :write' unless (writer.is_a?(Symbol) || writer.is_a?(Proc))
|
451
|
+
return unless (writer = validate_attr_accessor(:write, options))
|
516
452
|
|
517
|
-
returning(
|
453
|
+
returning accessor_method_name(name, model_attr) do |method_name|
|
518
454
|
if writer.is_a?(Symbol)
|
519
455
|
klass.class_eval %(
|
520
456
|
def #{method_name}=(value)
|
@@ -537,6 +473,12 @@ module Delineate
|
|
537
473
|
end
|
538
474
|
end
|
539
475
|
|
476
|
+
def accessor_method_name(name, model_attr)
|
477
|
+
model_attr == name ? "#{name}_#{@name}" : model_attr
|
478
|
+
end
|
479
|
+
|
480
|
+
|
481
|
+
include Resolve
|
540
482
|
include Serialization
|
541
483
|
include Schema
|
542
484
|
|
@@ -8,6 +8,7 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
# Collection of declared attribute maps for the model class
|
10
10
|
class_inheritable_accessor :attribute_maps
|
11
|
+
#class_attribute :attribute_maps
|
11
12
|
self.attribute_maps = {}
|
12
13
|
|
13
14
|
# The map_attributes method lets an ActiveRecord model class define a set
|
@@ -112,7 +113,7 @@ module ActiveRecord
|
|
112
113
|
end
|
113
114
|
|
114
115
|
def attribute_map(map_name)
|
115
|
-
self.class.
|
116
|
+
self.class.attribute_map(map_name)
|
116
117
|
end
|
117
118
|
|
118
119
|
# Returns the attributes as specified in the attribut map. The +format+ paramater
|
@@ -179,6 +180,7 @@ module ActiveRecord
|
|
179
180
|
raise "Base class for CTI subclass #{self.name} must specify attribute map #{map.name}" unless base_class_map
|
180
181
|
|
181
182
|
map.copy(base_class_map)
|
183
|
+
map.associations.delete_if {|k, a| a[:klass_name] == self.name}
|
182
184
|
map.instance_variable_set(:@resolved, false)
|
183
185
|
end
|
184
186
|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Delineate
|
2
|
+
module Resolve
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# Returns true if this map is fully resolved
|
6
|
+
def resolved?
|
7
|
+
@resolved
|
8
|
+
end
|
9
|
+
|
10
|
+
# Attempts to resolve this map and the maps it depends on, including declared associations.
|
11
|
+
# Will raise an exception if the map cannot be fully resolved.
|
12
|
+
def resolve!
|
13
|
+
resolve(:must_resolve)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
# Attempts to resolve this map and the maps it depends on, including declared associations,
|
18
|
+
# and returns success status as a boolean. If the +must_resolve+ parameter is truthy, raises
|
19
|
+
# raise an exception if the map cannot be fully resolved.
|
20
|
+
def resolve(must_resolve = false, resolving = [])
|
21
|
+
return true if @resolved
|
22
|
+
return true if resolving.include?(@klass_name) # prevent infinite recursion
|
23
|
+
|
24
|
+
resolving.push(@klass_name)
|
25
|
+
|
26
|
+
result = resolve_associations(must_resolve, resolving)
|
27
|
+
result = false unless resolve_sti_baseclass(must_resolve, resolving)
|
28
|
+
|
29
|
+
resolving.pop
|
30
|
+
@resolved = result
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Resolves association maps, and handles map merges as necessary
|
36
|
+
def resolve_associations(must_resolve, resolving)
|
37
|
+
result = true
|
38
|
+
|
39
|
+
@associations.each do |assoc_name, assoc|
|
40
|
+
detect_circular_merge!(assoc_name, assoc)
|
41
|
+
|
42
|
+
assoc_map = assoc[:attr_map] || assoc[:klass_name].constantize.attribute_maps.try(:fetch, @name, nil)
|
43
|
+
if assoc_map && assoc_map.resolve(must_resolve, resolving)
|
44
|
+
assoc_map = merge_assoc_map(assoc, assoc_map) if merge_option?(assoc[:options])
|
45
|
+
end
|
46
|
+
|
47
|
+
assoc[:attr_map] = assoc_map
|
48
|
+
|
49
|
+
if assoc_map.nil? or !assoc_map.resolve(false, resolving)
|
50
|
+
result = false
|
51
|
+
raise "Cannot resolve map for association :#{assoc_name} in #{@klass_name}:#{@name} map" if must_resolve
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
# If this is the map of an STI subclass, inherit/merge the map from the base class
|
59
|
+
def resolve_sti_baseclass(must_resolve, resolving)
|
60
|
+
result = true
|
61
|
+
|
62
|
+
if merge_option?(@options) && klass_sti_subclass? && !@sti_baseclass_merged
|
63
|
+
if klass.superclass.attribute_maps.try(:fetch, @name, nil).try(:resolve, must_resolve, resolving)
|
64
|
+
@resolved = @sti_baseclass_merged = true
|
65
|
+
self.copy(klass.superclass.attribute_maps[@name].dup.merge!(self))
|
66
|
+
else
|
67
|
+
result = false
|
68
|
+
raise "Can't resolve base class map for #{@klass_name}:#{@name} map" if must_resolve
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
# Raises exception if an association specifies a merge, and the association class's
|
76
|
+
# attribute map attempts to merge the association parent attribute map.
|
77
|
+
def detect_circular_merge!(assoc_name, assoc)
|
78
|
+
return if assoc.nil? || assoc[:attr_map].nil? || !merge_option?(assoc[:options])
|
79
|
+
return unless (map = assoc[:klass_name].constantize.attribute_maps.try(:fetch, @name, nil))
|
80
|
+
|
81
|
+
map.associations.each_value do |a|
|
82
|
+
if a[:klass_name] == @klass_name && merge_option?(a[:options]) && a[:attr_map]
|
83
|
+
raise "Detected attribute map circular merge references: class=#{@klass_name}, association=#{assoc_name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def merge_assoc_map(assoc, assoc_map)
|
89
|
+
if merge_option?(assoc[:options]) && assoc[:attr_map]
|
90
|
+
merge_map = assoc[:klass_name].constantize.attribute_maps[@name]
|
91
|
+
assoc_map = merge_map.dup.merge!(assoc_map, :with_options => true, :with_state => true)
|
92
|
+
end
|
93
|
+
assoc_map
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -14,8 +14,8 @@ module Delineate
|
|
14
14
|
# of the attributes.
|
15
15
|
def serialize(options = {})
|
16
16
|
opts = options[:include_header] ?
|
17
|
-
{:write_headers => true, :headers => serializable_header, :encoding =>
|
18
|
-
{:encoding =>
|
17
|
+
{:write_headers => true, :headers => serializable_header, :encoding => 'UTF-8'} :
|
18
|
+
{:encoding => 'UTF-8'}
|
19
19
|
|
20
20
|
opts = remove_serializer_class_options(options).merge(opts)
|
21
21
|
opts.delete(:include_header)
|
@@ -27,18 +27,18 @@ module Delineate
|
|
27
27
|
|
28
28
|
# Returns the header row as a CSV string.
|
29
29
|
def serialize_header(options = {})
|
30
|
-
opts = {:encoding =>
|
30
|
+
opts = {:encoding => 'UTF-8'}.merge(remove_serializer_class_options(options))
|
31
31
|
CSV.generate_line(serializable_header, opts)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Not implemented yet.
|
35
35
|
def serialize_in(csv_string, options = {})
|
36
|
-
raise
|
36
|
+
raise 'Serializing from CSV is not supported. You can inherit a class from CsvSerializer to write a custom importer.'
|
37
37
|
end
|
38
38
|
|
39
39
|
# Returns the record's mapped attributes in the serializer's "internal"
|
40
40
|
# format. For this class the representation is an array of one or more
|
41
|
-
# rows, one row for each item in
|
41
|
+
# rows, one row for each item in the record's has_many collections. Each
|
42
42
|
# row is an array of values ordered as follows:
|
43
43
|
#
|
44
44
|
# 1. All the record's mapped attributes in map order.
|
@@ -70,7 +70,7 @@ module Delineate
|
|
70
70
|
end
|
71
71
|
|
72
72
|
# Returns the header row as an array of strings, one for each
|
73
|
-
# mapped attribute, including nested
|
73
|
+
# mapped attribute, including nested associations. The items
|
74
74
|
# appear in the array in the same order as their corresponding
|
75
75
|
# attribute values.
|
76
76
|
def serializable_header(prefix = '')
|
@@ -93,7 +93,7 @@ module Delineate
|
|
93
93
|
|
94
94
|
private
|
95
95
|
|
96
|
-
# The diff here is that if the
|
96
|
+
# The diff here is that if the association record(s) is empty, we have to generate
|
97
97
|
# a new empty record: @record.class.new.build_xxx or @record.class.new.xxx.build
|
98
98
|
def add_includes(assoc_type)
|
99
99
|
includes = @options.delete(:include)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delineate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- lib/delineate/attribute_map.rb
|
148
148
|
- lib/delineate/map_attributes.rb
|
149
149
|
- lib/delineate/map_serializer.rb
|
150
|
+
- lib/delineate/resolve.rb
|
150
151
|
- lib/delineate/schema.rb
|
151
152
|
- lib/delineate/serialization.rb
|
152
153
|
- lib/delineate/serializers/csv_serializer.rb
|