field_mapper 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c5deabe6cc8ef7c58243cb797ebea59ad4affcb
4
+ data.tar.gz: 536688f17cc3afd08428f772cd49ecbdabd06dab
5
+ SHA512:
6
+ metadata.gz: 9e1599b63efea5c5e21366ddcb9797f00eee43118305e6bddb44cf93720abec31bf805780c0ce76a3ddd34f64f5a04a1f0148d9a462b7c17e19ad56409bfe210
7
+ data.tar.gz: 7ee6d66aa959a5e5e084820b0e64e2e28c23b8c30198e053f4b10b926c93aa7a45dfdfa627ee0d7c357b67ca3ffae20b1aad3d6cd61d75c8e59f0f93e352d8a9
@@ -0,0 +1,18 @@
1
+ require "active_support/all"
2
+ require "american_date"
3
+ require "money"
4
+ require "monetize"
5
+ require_relative "field_mapper/version"
6
+ require_relative "field_mapper/types/boolean"
7
+ require_relative "field_mapper/types/plat"
8
+ require_relative "field_mapper/types/list"
9
+ require_relative "field_mapper/standard/plat"
10
+ require_relative "field_mapper/custom/plat"
11
+ require_relative "field_mapper/standard/converter"
12
+ require_relative "field_mapper/custom/converter"
13
+
14
+ module FieldMapper
15
+ class << self
16
+ attr_accessor :logger
17
+ end
18
+ end
@@ -0,0 +1,118 @@
1
+ module FieldMapper
2
+ module Custom
3
+ class Converter
4
+
5
+ attr_reader(
6
+ :custom_plat,
7
+ :standard_plat,
8
+ :custom_instance
9
+ )
10
+
11
+ def initialize(custom_instance)
12
+ @custom_plat = custom_instance.class
13
+ @custom_instance = custom_instance
14
+ @standard_plat = custom_plat.standard_plat
15
+ end
16
+
17
+ def convert_to_standard
18
+ standard_instance = standard_plat.new
19
+
20
+ custom_plat.fields.each do |custom_field_name, custom_field|
21
+ if custom_field.standard_field.present?
22
+ raw_standard_value = get_raw_standard_value(
23
+ custom_field,
24
+ custom_instance[custom_field_name],
25
+ standard_instance
26
+ )
27
+ raw_standard_value = custom_field.standard_field.cast(raw_standard_value)
28
+ standard_instance[custom_field.standard_field.name] = raw_standard_value
29
+ end
30
+ end
31
+
32
+ [custom_instance, standard_instance].each do |instance|
33
+ instance.send(:after_convert, from: custom_instance, to: standard_instance)
34
+ end
35
+
36
+ standard_instance
37
+ end
38
+
39
+ def convert_to(custom_plat)
40
+ converter = FieldMapper::Standard::Converter.new(convert_to_standard)
41
+ converter.convert_to(custom_plat)
42
+ end
43
+
44
+ protected
45
+
46
+ def get_raw_standard_value(custom_field, raw_custom_value, standard_instance)
47
+ return nil if raw_custom_value.nil?
48
+
49
+ strategy = custom_field.flip_strategy(:custom_to_standard)
50
+ custom_flipper = custom_field.custom_flipper?(:custom_to_standard)
51
+
52
+ if !custom_flipper
53
+ if custom_field.plat?
54
+ return compute_raw_standard_value_for_plat(custom_field, raw_custom_value)
55
+ end
56
+
57
+ if custom_field.plat_list?
58
+ if custom_field.plat_list?
59
+ return compute_raw_standard_value_for_plat_list(custom_field, raw_custom_value)
60
+ end
61
+ end
62
+ end
63
+
64
+ if strategy == :find
65
+ if custom_field.type.is_a?(FieldMapper::Types::List)
66
+ return raw_custom_value.map do |single_raw_custom_value|
67
+ find_raw_standard_value(custom_field, single_raw_custom_value)
68
+ end
69
+ else
70
+ return find_raw_standard_value(custom_field, raw_custom_value)
71
+ end
72
+ end
73
+
74
+ compute_raw_standard_value(custom_field, raw_custom_value, standard_instance)
75
+ end
76
+
77
+ def find_raw_standard_value(custom_field, raw_custom_value)
78
+ return raw_custom_value unless custom_field.standard_field.has_values?
79
+
80
+ if custom_field.has_values?
81
+ custom_value = custom_field.find_value(raw_custom_value)
82
+ if custom_value.present?
83
+ custom_value.standard_value.value if custom_value.standard_value.present?
84
+ end
85
+ else
86
+ standard_value = custom_field.standard_field.find_value(raw_custom_value)
87
+ standard_value.value if standard_value.present?
88
+ end
89
+ end
90
+
91
+ def compute_raw_standard_value(custom_field, raw_custom_value, standard_instance)
92
+ raw_standard_value = custom_instance.instance_exec(
93
+ raw_custom_value,
94
+ standard_instance: standard_instance,
95
+ &custom_field.custom_to_standard
96
+ )
97
+
98
+ if !raw_standard_value.nil?
99
+ raw_standard_value = custom_field.standard_field.cast(raw_standard_value)
100
+ end
101
+
102
+ raw_standard_value
103
+ end
104
+
105
+ def compute_raw_standard_value_for_plat_list(custom_field, raw_custom_values)
106
+ raw_custom_values.map do |raw_custom_value|
107
+ compute_raw_standard_value_for_plat(custom_field, raw_custom_value)
108
+ end
109
+ end
110
+
111
+ def compute_raw_standard_value_for_plat(custom_field, raw_custom_value)
112
+ converter = FieldMapper::Custom::Converter.new(raw_custom_value)
113
+ converter.convert_to_standard
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,110 @@
1
+ require_relative "../standard/field"
2
+ require_relative "value"
3
+
4
+ module FieldMapper
5
+ module Custom
6
+ class Field < FieldMapper::Standard::Field
7
+
8
+ DefaultFlipper = -> (value, standard_instance: nil) { value }
9
+
10
+ attr_reader(
11
+ :name,
12
+ :type,
13
+ :desc,
14
+ :default,
15
+ :values,
16
+ :standard_field,
17
+ :custom_to_standard,
18
+ :standard_to_custom
19
+ )
20
+
21
+ def initialize(
22
+ name,
23
+ type: nil,
24
+ desc: nil,
25
+ default: nil,
26
+ standard_field: nil,
27
+ custom_to_standard: DefaultFlipper,
28
+ standard_to_custom: DefaultFlipper,
29
+ &block
30
+ )
31
+ type ||= standard_field.type unless standard_field.nil?
32
+ super name, type: type, desc: desc, default: default
33
+
34
+ @standard_field = standard_field
35
+ @custom_to_standard = custom_to_standard
36
+ @standard_to_custom = standard_to_custom
37
+
38
+ eigen = class << self; self; end
39
+ eigen.instance_eval do
40
+ define_method :custom_to_standard1, &custom_to_standard
41
+ define_method :standard_to_custom2, &standard_to_custom
42
+ end
43
+ end
44
+
45
+ def value(value, standard: nil, priority: nil)
46
+ @values ||= []
47
+ @values << FieldMapper::Custom::Value.new(
48
+ value,
49
+ field: self,
50
+ standard_value: standard,
51
+ priority: priority
52
+ )
53
+ @values.last
54
+ end
55
+
56
+ # Adds values to a Field instance that are defined in a CSV file.
57
+ #
58
+ # Intended use is from within a Plat class declaration.
59
+ # @example
60
+ # class ExamplePlat < FieldMapper::Standard::Plat
61
+ # field :example do
62
+ # load_values "/path/to/file.csv"
63
+ # end
64
+ # end
65
+ #
66
+ # The format of the CSV file should contain a two columns with a header row.
67
+ # NOTE: An optional priority column can also be included.
68
+ # @example
69
+ # custom_value,standard_value,priority
70
+ # "A",1,
71
+ # "B",2,true
72
+ # "C",2,
73
+ #
74
+ def load_values(path_to_csv)
75
+ CSV.foreach(path_to_csv, :headers => true) do |row|
76
+ value(
77
+ row["custom_value"],
78
+ standard: row["standard_value"],
79
+ priority: FieldMapper::Types::Boolean.parse(row["priority"])
80
+ )
81
+ end
82
+ end
83
+
84
+ def find_values_mapped_to_standard(standard_value)
85
+ values.select do |val|
86
+ val.standard_value == standard_value ||
87
+ val.standard_value.value == standard_value
88
+ end
89
+ end
90
+
91
+ def find_priority_value_mapped_to_standard(standard_value)
92
+ matches = find_values_mapped_to_standard(standard_value)
93
+ match = matches.find { |val| val.priority }
94
+ match ||= matches.first
95
+ end
96
+
97
+ def flip_strategy(direction)
98
+ return :compute if custom_flipper?(direction)
99
+ return :find if has_values?
100
+ return :find if standard_field.present? && standard_field.has_values?
101
+ :compute
102
+ end
103
+
104
+ def custom_flipper?(direction)
105
+ instance_variable_get("@#{direction}") != DefaultFlipper
106
+ end
107
+
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,60 @@
1
+ require_relative "../standard/plat"
2
+ require_relative "../errors"
3
+ require_relative "../name_helper"
4
+ require_relative "field"
5
+
6
+ module FieldMapper
7
+ module Custom
8
+ class Plat < FieldMapper::Standard::Plat
9
+
10
+ class << self
11
+ attr_reader :standard_plat
12
+
13
+ def set_standard(standard_plat)
14
+ @standard_plat = standard_plat
15
+ end
16
+
17
+ def field(
18
+ name,
19
+ type: nil,
20
+ desc: nil,
21
+ default: nil,
22
+ standard: nil,
23
+ custom_to_standard: FieldMapper::Custom::Field::DefaultFlipper,
24
+ standard_to_custom: FieldMapper::Custom::Field::DefaultFlipper,
25
+ &block
26
+ )
27
+ field_names[attr_name(name)] = name
28
+
29
+ field = fields[name] = FieldMapper::Custom::Field.new(
30
+ name,
31
+ type: type,
32
+ desc: desc,
33
+ default: default,
34
+ standard_field: standard_plat.fields[standard],
35
+ custom_to_standard: custom_to_standard,
36
+ standard_to_custom: standard_to_custom
37
+ )
38
+
39
+ field.instance_exec(&block) if block_given?
40
+
41
+ define_method(attr_name name) do
42
+ self[name]
43
+ end
44
+
45
+ define_method("#{attr_name name}=") do |value|
46
+ self[name] = value
47
+ end
48
+
49
+ define_method("custom_to_standard_#{name}", &custom_to_standard)
50
+ define_method("standard_to_custom_#{name}", &standard_to_custom)
51
+ end
52
+
53
+ def find_mapped_fields(standard_field)
54
+ fields.values.select { |field| field.standard_field == standard_field }
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ require_relative "../errors"
2
+
3
+ module FieldMapper
4
+ module Custom
5
+ class Value < FieldMapper::Standard::Value
6
+ attr_reader(
7
+ :value,
8
+ :field,
9
+ :priority,
10
+ :standard_value
11
+ )
12
+
13
+ def initialize(
14
+ value,
15
+ field: nil,
16
+ priority: nil,
17
+ standard_value: nil
18
+ )
19
+ super value, field: field
20
+
21
+ if !standard_value.nil?
22
+ if field.standard_field.nil?
23
+ message = "[#{field.name}] [#{value}] is mapped to a standard but [#{field.name}] is not"
24
+ raise StandardFieldNotFound.new(message)
25
+ end
26
+
27
+ raw_standard_value = standard_value
28
+ standard_value = field.standard_field.find_value(standard_value)
29
+
30
+ if standard_value.nil?
31
+ message = "[#{field.name}] [#{value}] is mapped, but the standard [#{field.standard_field.name}] doesn't define the value [#{raw_standard_value}]"
32
+ raise StandardValueNotFound.new(message)
33
+ end
34
+ end
35
+
36
+ @priority = priority
37
+ @standard_value = standard_value
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ class StandardNotSet < StandardError; end
2
+ class StandardFieldNotFound < StandardError; end
3
+ class StandardValueNotFound < StandardError; end
4
+ class StandardMismatch < StandardError; end
5
+ class TypeNotSpecified < StandardError; end
6
+ class TypeNotSupported < StandardError; end
7
+ class FieldNotDefined < StandardError; end
8
+ class InvalidPlatType < StandardError; end
9
+ class InvalidListType < StandardError; end
10
+ class InvalidListValue < StandardError; end
11
+ class InvalidMarshal < StandardError; end
@@ -0,0 +1,24 @@
1
+ require "oj"
2
+
3
+ module FieldMapper
4
+ module Marshaller
5
+
6
+ OPTIONS = {
7
+ indent: 0,
8
+ circular: false,
9
+ class_cache: true,
10
+ escape: :json,
11
+ time: :unix,
12
+ create_id: "natefoo"
13
+ }
14
+
15
+ def marshal(value)
16
+ Oj.dump value, OPTIONS
17
+ end
18
+
19
+ def unmarshal(value)
20
+ Oj.load value, OPTIONS
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module FieldMapper
2
+ module NameHelper
3
+
4
+ def attr_name(value)
5
+ value = value.to_s
6
+ @attr_names ||= {}
7
+ @attr_names[value] ||= begin
8
+ value.
9
+ gsub(/\W/, "_").
10
+ gsub(/[A-Z][A-Z]+/) { |match| "_#{match.downcase}_" }.
11
+ gsub(/[A-Z]/) { |match| "_#{match.downcase}" }.
12
+ gsub(/\A_|_\z/, "")
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,111 @@
1
+ module FieldMapper
2
+ module Standard
3
+ class Converter
4
+
5
+ attr_reader :standard_plat, :standard_instance
6
+
7
+ def initialize(standard_instance)
8
+ @standard_plat = standard_instance.class
9
+ @standard_instance = standard_instance
10
+ end
11
+
12
+ def convert_to(custom_plat)
13
+ raise StandardMismatch if custom_plat.standard_plat != standard_plat
14
+
15
+ custom_instance = custom_plat.new
16
+
17
+ standard_plat.fields.each do |standard_field_name, standard_field|
18
+ custom_fields = custom_plat.find_mapped_fields(standard_field)
19
+ custom_fields.each do |custom_field|
20
+ raw_custom_value = get_raw_custom_value(
21
+ custom_field,
22
+ standard_instance[standard_field_name],
23
+ custom_instance
24
+ )
25
+ raw_custom_value = custom_field.cast(raw_custom_value)
26
+ custom_instance[custom_field.name] = raw_custom_value
27
+ end
28
+ end
29
+
30
+ [standard_instance, custom_instance].each do |instance|
31
+ instance.send(:after_convert, from: standard_instance, to: custom_instance)
32
+ end
33
+
34
+ custom_instance
35
+ end
36
+
37
+ protected
38
+
39
+ def get_raw_custom_value(custom_field, raw_standard_value, custom_instance)
40
+ return nil if raw_standard_value.nil?
41
+
42
+ strategy = custom_field.flip_strategy(:standard_to_custom)
43
+ custom_flipper = custom_field.custom_flipper?(:standard_to_custom)
44
+
45
+ if !custom_flipper
46
+ if custom_field.plat?
47
+ return compute_raw_custom_value_for_plat(custom_field, raw_standard_value)
48
+ end
49
+
50
+ if custom_field.plat_list?
51
+ if custom_field.plat_list?
52
+ return compute_raw_custom_value_for_plat_list(custom_field, raw_standard_value)
53
+ end
54
+ end
55
+ end
56
+
57
+ if strategy == :find
58
+ if custom_field.type.is_a?(FieldMapper::Types::List)
59
+ return raw_standard_value.map do |single_raw_standard_value|
60
+ find_raw_custom_value(custom_field, single_raw_standard_value)
61
+ end
62
+ else
63
+ return find_raw_custom_value(custom_field, raw_standard_value)
64
+ end
65
+ end
66
+
67
+ compute_raw_custom_value(custom_field, raw_standard_value, custom_instance)
68
+ end
69
+
70
+ def find_raw_custom_value(custom_field, raw_standard_value)
71
+ return raw_standard_value unless custom_field.has_values?
72
+
73
+ if !custom_field.standard_field.has_values?
74
+ custom_value = custom_field.find_value(raw_standard_value)
75
+ return nil if custom_value.nil?
76
+ return custom_value.value
77
+ end
78
+
79
+ custom_value = custom_field.find_priority_value_mapped_to_standard(raw_standard_value)
80
+ return nil if custom_value.nil?
81
+ custom_value.value
82
+ end
83
+
84
+ def compute_raw_custom_value(custom_field, raw_standard_value, custom_instance)
85
+ raw_custom_value = custom_instance.instance_exec(
86
+ raw_standard_value,
87
+ standard_instance: standard_instance,
88
+ &custom_field.standard_to_custom
89
+ )
90
+
91
+ if !raw_custom_value.nil?
92
+ raw_custom_value = custom_field.cast(raw_custom_value)
93
+ end
94
+ raw_custom_value
95
+ end
96
+
97
+ def compute_raw_custom_value_for_plat_list(custom_field, raw_standard_values)
98
+ raw_standard_values.map do |raw_standard_value|
99
+ compute_raw_custom_value_for_plat(custom_field, raw_standard_value)
100
+ end
101
+ end
102
+
103
+ def compute_raw_custom_value_for_plat(custom_field, raw_standard_value)
104
+ converter = FieldMapper::Standard::Converter.new(raw_standard_value)
105
+ converter.convert_to(custom_field.type.type)
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+