hashcraft 1.0.0.pre.alpha.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca45f5a2350cfc2b0bd127f015d675ee511546eea1d370740ee57ceb9d986411
4
- data.tar.gz: 2c937e11d5cd7f58cd84045b8ef538eb65e9949b347473d7178cbcc0f6c4260d
3
+ metadata.gz: 38c94cd212c01aa72103257b3d053e3dd9beeb190641fcb82df4ac1896db27ba
4
+ data.tar.gz: 21de2c24f3a98e759b68e4783b29d9c8e11f45cc3e71b71bd779e0319ba52986
5
5
  SHA512:
6
- metadata.gz: 80a214e03fa73c8db28b5bef364324966fe66ba43c6b00b80e5709aae8c5357add7f7f44c9395a3f2593f25fc0bdd07bbf1bb40aa01c531d837283fbedba558b
7
- data.tar.gz: 418ab8958ea1f0c5b704fc6669ff9ed3f7a24cbe642c1a667063e77201c8ff4e8b470072a75edd078e20086778d4b411a45393c1aa38a0079fc32244c3b4e7c9
6
+ metadata.gz: 5ab0f421744e572638f75ccf04508471f7df8131f105b8e55ae806b90255340f8b45193f72c94dfb84ee1b6037e836e8d29050c8963417d41484c2f83efe97b5
7
+ data.tar.gz: 3c2e2f61c4eeea3af9fdc95d493461bd727e9b49134c60a8577b5a7c149ae3a4da1796c05f3db6257afed23b2abd29bf1ef146359dc3d5726185ec73b7c6a221
data/.gitignore CHANGED
@@ -4,5 +4,4 @@
4
4
  /coverage
5
5
  Gemfile.lock
6
6
  /pkg
7
-
8
-
7
+ /doc
@@ -1,3 +1,7 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+ NewCops: enable
4
+
1
5
  Layout/LineLength:
2
6
  Max: 100
3
7
  Exclude:
@@ -15,9 +19,6 @@ Metrics/BlockLength:
15
19
  Metrics/MethodLength:
16
20
  Max: 30
17
21
 
18
- AllCops:
19
- TargetRubyVersion: 2.5
20
-
21
22
  Metrics/AbcSize:
22
23
  Max: 16
23
24
 
@@ -1,4 +1,4 @@
1
- # 1.0.0 TBD
1
+ # 1.0.0 July 15th, 2020
2
2
 
3
3
  Initial, feature-complete implementation.
4
4
 
data/README.md CHANGED
@@ -147,7 +147,7 @@ This means there is an available method called api_url that can be called to set
147
147
  * **eager**: always assign a value. When true it will always assign the key a value.
148
148
  * **key**: allows for aliasing keys. If omitted, the key will be the option's method name (api_url as noted above).
149
149
  * **meta**: used to store any arbitrary data that can be accessed with transformers.
150
- * **mutator**: defines the type of the data backing the method, defaulting to property. When the default property is used then it will simply assign the passed in value. Other values are: `hash` and `array`. When hash is used then the passed in value will be merged onto the key's value. When array is used then the passed in value will be pushed onto the key's value.
150
+ * **mutator**: defines the *type of update* to be made to the underlying value, defaulting to `property`. When the default, `property`, is used then it will simply assign the passed in value. Some other options are: `hash` and `array`. When hash is used then the passed in value will be merged onto the key's value. When array is used then the passed in value will be pushed onto the key's value. For other types see the `Hashcraft::MutatorRegistry` file.
151
151
 
152
152
  ### Internationalization Support
153
153
 
@@ -155,7 +155,7 @@ There is currently no first-class support for internationalization, but you can
155
155
 
156
156
  ### Transformers
157
157
 
158
- Transformers are optional but come into play when you need any additional/special processing of keys and values. By default, keys and values use the pass-thru transformer (Hashable::Transformers::PassThru) but can be explicitly passed any object that responds to `#transform(value, option)`.
158
+ Transformers are optional but come into play when you need any additional/special processing of keys and values. By default, keys and values use the pass-thru transformer, `Hashcraft::Transformers::PassThru`, but can be explicitly passed any object that responds to `#transform(value, option)`.
159
159
 
160
160
  #### Key Transformer Example
161
161
 
@@ -171,12 +171,35 @@ Say, for example, we wanted to transform all keys to camel case. We could creat
171
171
  end
172
172
  ````
173
173
 
174
- We can then use this when deriving hashes (using the above Grid example):
174
+ We can then use this when deriving hashes (building on the Grid example above):
175
175
 
176
176
  ````ruby
177
+ class Content < Hashcraft::Base
178
+ option :property
179
+ end
180
+
181
+ class Column < Hashcraft::Base
182
+ option :header
183
+
184
+ option :content, craft: Content,
185
+ mutator: :array,
186
+ key: :contents
187
+ end
188
+
189
+ class Grid < Hashcraft::Base
190
+ key_transformer CamelCase.new
191
+
192
+ option :api_url,
193
+ :name
194
+
195
+ option :column, craft: Column,
196
+ mutator: :array,
197
+ key: :columns
198
+ end
199
+
177
200
  config = Grid.new do
178
201
  api_url '/patients'
179
- end.to_h(key_transformer: CamelCase.new)
202
+ end.to_h
180
203
  ````
181
204
 
182
205
  The resulting `config` value will now be:
@@ -190,12 +213,23 @@ The resulting `config` value will now be:
190
213
  Note that this library ships with some basic transformers like the one mentioned above. If you want to use this then you can simply do this:
191
214
 
192
215
  ````ruby
216
+ class Grid < Hashcraft::Base
217
+ key_transformer :camel_case
218
+
219
+ option :api_url,
220
+ :name
221
+
222
+ option :column, craft: Column,
223
+ mutator: :array,
224
+ key: :columns
225
+ end
226
+
193
227
  config = Grid.new do
194
228
  api_url '/patients'
195
- end.to_h(key_transformer: :camel_case)
229
+ end.to_h
196
230
  ````
197
231
 
198
- See Hashcraft::TransformerRegistry for a full list of provided transformers.
232
+ See the `Hashcraft::TransformerRegistry` file for a full list of provided transformers.
199
233
 
200
234
  #### Value Transformer Example
201
235
 
@@ -213,10 +247,12 @@ class Localizer
213
247
  end
214
248
  ````
215
249
 
216
- Building on our Grid example, we could enhance the Column object as such:
250
+ Building on our Grid example, we could enhance the Column object:
217
251
 
218
252
  ````ruby
219
253
  class Column < Hashcraft::Base
254
+ value_transformer Localizer.new
255
+
220
256
  option :header, meta: { localize: true }
221
257
 
222
258
  option :content, craft: Content,
@@ -225,7 +261,7 @@ class Column < Hashcraft::Base
225
261
  end
226
262
  ````
227
263
 
228
- We can then use the new value transformer (Localizer) when deriving hashes (using the above Grid and updated Column example):
264
+ We can then use the new value transformer, `Localizer`, when deriving hashes (building on the above Grid and updated Column example):
229
265
 
230
266
  ````yaml
231
267
  en:
@@ -237,10 +273,10 @@ en:
237
273
  config = Grid.new do
238
274
  column header: :id
239
275
  column header: :first
240
- end.to_h(value_transformer: Localizer.new)
276
+ end.to_h
241
277
  ````
242
278
 
243
- Assuming our en.yml looks like the above example and our locale is set to:en then the resulting `config` value will now be:
279
+ Assuming our en.yml looks like the above example and our locale is set to :en then the resulting `config` value will now be:
244
280
 
245
281
  ````ruby
246
282
  {
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
  s.add_development_dependency('pry', '~>0')
33
33
  s.add_development_dependency('rake', '~> 13')
34
34
  s.add_development_dependency('rspec')
35
- s.add_development_dependency('rubocop', '~>0.79.0')
36
- s.add_development_dependency('simplecov', '~>0.17.0')
35
+ s.add_development_dependency('rubocop', '~>0.88.0')
36
+ s.add_development_dependency('simplecov', '~>0.18.5')
37
37
  s.add_development_dependency('simplecov-console', '~>0.6.0')
38
38
  end
@@ -10,13 +10,8 @@
10
10
  require 'forwardable'
11
11
  require 'singleton'
12
12
 
13
- # Monkey-patching core libraries
14
- require_relative 'hashcraft/core_ext/hash'
15
- Hash.include Hashcraft::CoreExt::Hash
16
-
17
13
  # General tooling
18
14
  require_relative 'hashcraft/generic'
19
15
 
16
+ # Main Entrypoint(s)
20
17
  require_relative 'hashcraft/base'
21
- require_relative 'hashcraft/mutator_registry'
22
- require_relative 'hashcraft/transformer_registry'
@@ -7,8 +7,8 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
- require_relative 'compiler'
11
10
  require_relative 'dsl'
11
+ require_relative 'transformers'
12
12
 
13
13
  module Hashcraft
14
14
  # Super-class for craftable objects.
@@ -17,13 +17,15 @@ module Hashcraft
17
17
  extend Forwardable
18
18
 
19
19
  def_delegators :'self.class',
20
- :option?,
21
20
  :option_set,
22
- :find_option
21
+ :find_option,
22
+ :key_transformer_to_use,
23
+ :value_transformer_to_use
23
24
 
24
25
  def initialize(opts = {}, &block)
25
- @data = make_default_data
26
+ @data = {}
26
27
 
28
+ load_default_data
27
29
  load_opts(opts)
28
30
 
29
31
  return unless block_given?
@@ -35,36 +37,63 @@ module Hashcraft
35
37
  end
36
38
  end
37
39
 
38
- def to_h(key_transformer: nil, value_transformer: nil)
39
- Compiler.new(
40
- option_set,
41
- key_transformer: key_transformer,
42
- value_transformer: value_transformer
43
- ).evaluate!(data)
40
+ # Main compilation method. Once an object is hydrated, you can call this method to get the
41
+ # materialized hash.
42
+ def to_h
43
+ data.each_with_object({}) do |(key, value), memo|
44
+ method = value.is_a?(Array) ? :evaluate_values! : :evaluate_value!
45
+
46
+ send(method, memo, key, value)
47
+ end
44
48
  end
45
49
 
46
50
  private
47
51
 
48
52
  attr_reader :data
49
53
 
50
- def make_default_data
51
- option_set.values.each_with_object({}) { |o, memo| o.default!(memo) }
54
+ def load_default_data
55
+ option_set.each { |option| default!(option) }
52
56
  end
53
57
 
54
58
  def load_opts(opts)
55
59
  (opts || {}).each { |k, v| send(k, v) }
56
60
  end
57
61
 
58
- def respond_to_missing?(method_name, include_private = false)
59
- option?(method_name) || super
62
+ def evaluate_values!(data, key, values)
63
+ data[key] = values.map { |value| value.is_a?(Hashcraft::Base) ? value.to_h : value }
64
+
65
+ self
60
66
  end
61
67
 
62
- def method_missing(method_name, *arguments, &block)
63
- if option?(method_name)
64
- find_option(method_name).value!(data, arguments.first, &block)
65
- else
66
- super
67
- end
68
+ def evaluate_value!(data, key, value)
69
+ data[key] = (value.is_a?(Hashcraft::Base) ? value.to_h : value)
70
+
71
+ self
72
+ end
73
+
74
+ def default!(option)
75
+ return self unless option.eager?
76
+
77
+ key = hash_key(option)
78
+ value = Transformers.instance.transform(value_transformer_to_use, option.default.dup, option)
79
+
80
+ data[key] = value
81
+
82
+ self
83
+ end
84
+
85
+ def value!(option, value, &block)
86
+ key = hash_key(option)
87
+ value = option.craft_value(value, &block)
88
+ value = Transformers.instance.transform(value_transformer_to_use, value, option)
89
+
90
+ option.value!(data, key, value)
91
+
92
+ self
93
+ end
94
+
95
+ def hash_key(option)
96
+ Transformers.instance.transform(key_transformer_to_use, option.hash_key, option)
68
97
  end
69
98
  end
70
99
  end
@@ -14,14 +14,47 @@ module Hashcraft
14
14
  # OptionSet instance along with materializing one for its
15
15
  # inheritance chain (child has precedence.)
16
16
  module Dsl
17
- def option?(name)
18
- option_set.exist?(name)
17
+ attr_reader :local_key_transformer,
18
+ :local_value_transformer
19
+
20
+ # DSL Method used to declare what the sub-class should use as a transformer for all keys.
21
+ # It will follow the typical inheritance chain and find the closest
22
+ # transformer to use (child-first).
23
+ def key_transformer(name)
24
+ tap { @local_key_transformer = name }
25
+ end
26
+
27
+ # DSL Method used to declare what the sub-class should use as a transformer for all values.
28
+ # It will follow the typical inheritance chain and find the closest
29
+ # transformer to use (child-first).
30
+ def value_transformer(name)
31
+ tap { @local_value_transformer = name }
32
+ end
33
+
34
+ def key_transformer_to_use # :nodoc:
35
+ return @key_transformer_to_use if @key_transformer_to_use
36
+
37
+ @key_transformer_to_use =
38
+ ancestors.select { |a| a < Base }
39
+ .find(&:local_key_transformer)
40
+ &.local_key_transformer
19
41
  end
20
42
 
21
- def find_option(name)
43
+ def value_transformer_to_use # :nodoc:
44
+ return @value_transformer_to_use if @value_transformer_to_use
45
+
46
+ @value_transformer_to_use =
47
+ ancestors.select { |a| a < Base }
48
+ .find(&:local_value_transformer)
49
+ &.local_value_transformer
50
+ end
51
+
52
+ def find_option(name) # :nodoc:
22
53
  option_set.find(name)
23
54
  end
24
55
 
56
+ # The main class-level DSL method consumed by sub-classes. This is the entry-point for the
57
+ # declaration of available options.
25
58
  def option(*args)
26
59
  opts = args.last.is_a?(Hash) ? args.pop : {}
27
60
 
@@ -29,12 +62,22 @@ module Hashcraft
29
62
  option = Option.new(key, opts)
30
63
 
31
64
  local_option_set.add(option)
65
+
66
+ method_name = option.name
67
+
68
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
69
+ def #{method_name}(opts = {}, &block)
70
+ option = find_option('#{method_name}')
71
+
72
+ value!(option, opts, &block)
73
+ end
74
+ RUBY
32
75
  end
33
76
 
34
77
  self
35
78
  end
36
79
 
37
- def option_set
80
+ def option_set # :nodoc:
38
81
  @option_set ||=
39
82
  ancestors
40
83
  .reverse
@@ -42,7 +85,7 @@ module Hashcraft
42
85
  .each_with_object(Generic::Dictionary.new) { |a, memo| memo.merge!(a.local_option_set) }
43
86
  end
44
87
 
45
- def local_option_set
88
+ def local_option_set # :nodoc:
46
89
  @local_option_set ||= Generic::Dictionary.new(key: :name)
47
90
  end
48
91
  end
@@ -8,7 +8,7 @@
8
8
  #
9
9
 
10
10
  module Hashcraft
11
- module Generic
11
+ module Generic # :nodoc: all
12
12
  # Dictionary structure defining how we want to organize objects. Basically a type-insensitive
13
13
  # hash where each key is the object's value for the specified key.
14
14
  # All keys are #to_s evaluated in order to achieve the type-insensitivity.
@@ -28,8 +28,10 @@ module Hashcraft
28
28
  freeze
29
29
  end
30
30
 
31
- def exist?(key)
32
- !find(key).nil?
31
+ def each(&block)
32
+ return enum_for(:each) unless block_given?
33
+
34
+ values.each(&block)
33
35
  end
34
36
 
35
37
  def find(key)
@@ -8,7 +8,7 @@
8
8
  #
9
9
 
10
10
  module Hashcraft
11
- module Generic
11
+ module Generic # :nodoc: all
12
12
  # A general data structure that can register and resolve objects by name.
13
13
  # It also will act as a pass-thru if a non-string or non-symbol is passed through.
14
14
  class Registry
@@ -46,11 +46,7 @@ module Hashcraft
46
46
  def resolve(value)
47
47
  return value unless lookup?(value)
48
48
 
49
- mutator = map[value.to_s]
50
-
51
- raise ArgumentError, "registration: #{value} not found" unless mutator
52
-
53
- mutator
49
+ map[value.to_s] || raise(ArgumentError, "registration: #{value} not found")
54
50
  end
55
51
 
56
52
  private
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Hashcraft
11
+ # Singleton that knows how to register and retrieve mutator instances.
12
+ # Entries can either be instances that respond to value! or procs that accept three arguments.
13
+ class Mutators < Generic::Registry # :nodoc:
14
+ FUNCTIONS = {
15
+ always_false: ->(data, key, _value) { data[key] = false },
16
+ always_true: ->(data, key, _value) { data[key] = true },
17
+ array: ->(data, key, value) { (data[key] ||= []) << value },
18
+ flat_array: lambda do |data, key, value|
19
+ data[key] ||= []
20
+
21
+ if value.is_a?(::Array)
22
+ data[key] += value
23
+ else
24
+ data[key] << value
25
+ end
26
+ end,
27
+ hash: ->(data, key, value) { (data[key] ||= {}).merge!(value || {}) },
28
+ property: ->(data, key, value) { data[key] = value },
29
+ }.freeze
30
+
31
+ private_constant :FUNCTIONS
32
+
33
+ def initialize
34
+ # append on the default case
35
+ super(FUNCTIONS.merge('': FUNCTIONS[:property]))
36
+ end
37
+
38
+ def value!(name, data, key, value)
39
+ object = resolve(name)
40
+ method = object.is_a?(Proc) ? :call : :value!
41
+
42
+ object.send(method, data, key, value)
43
+ end
44
+ end
45
+ end
@@ -7,6 +7,8 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
+ require_relative 'mutators'
11
+
10
12
  module Hashcraft
11
13
  # Defines a method and corresponding attribute for a craftable class.
12
14
  class Option
@@ -17,46 +19,47 @@ module Hashcraft
17
19
  :mutator,
18
20
  :name
19
21
 
20
- def initialize(name, opts = {})
22
+ alias eager? eager
23
+
24
+ def initialize(name, opts = {}) # :nodoc:
21
25
  raise ArgumentError, 'name is required' if name.to_s.empty?
22
26
 
23
- @craft = opts[:craft]
24
- @default = opts[:default]
25
- @eager = opts[:eager] || false
26
- @internal_meta = (opts[:meta] || {}).symbolize_keys
27
- @key = opts[:key].to_s
28
- @mutator = MutatorRegistry.resolve(opts[:mutator])
29
- @name = name.to_s
27
+ @craft = opts[:craft]
28
+ @default = opts[:default]
29
+ @eager = opts[:eager] || false
30
+ @internal_meta = symbolize_keys(opts[:meta] || {})
31
+ @key = opts[:key].to_s
32
+ @mutator = opts[:mutator]
33
+ @name = name.to_s
30
34
 
31
35
  freeze
32
36
  end
33
37
 
38
+ def value!(data, key, value) # :nodoc:
39
+ Mutators.instance.value!(mutator, data, key, value)
40
+ end
41
+
42
+ # Options are sent into transformers as arguments. Leverage the meta key for an option
43
+ # to store any additional data that you may need in transformers. This method provides a
44
+ # quick message-based entry point into inspecting the meta key's value.
34
45
  def meta(key)
35
46
  internal_meta[key.to_s.to_sym]
36
47
  end
37
48
 
38
- def default!(data)
39
- return self unless eager
40
-
41
- data[name] = default.dup
42
-
43
- self
49
+ def hash_key # :nodoc:
50
+ key.empty? ? name : key
44
51
  end
45
52
 
46
- def value!(data, value, &block)
47
- value = craft_value(value, &block)
48
-
49
- mutator.value!(data, name, value)
50
-
51
- self
53
+ def craft_value(value, &block) # :nodoc:
54
+ craft ? craft.new(value, &block) : value
52
55
  end
53
56
 
54
57
  private
55
58
 
56
59
  attr_reader :internal_meta
57
60
 
58
- def craft_value(value, &block)
59
- craft ? craft.new(value, &block) : value
61
+ def symbolize_keys(hash)
62
+ hash.transform_keys(&:to_sym)
60
63
  end
61
64
  end
62
65
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Hashcraft
11
+ # Singleton that knows how to register and retrieve transformer instances.
12
+ # Entries can either be instances that respond to transform or procs that accept two arguments.
13
+ class Transformers < Generic::Registry # :nodoc:
14
+ FUNCTIONS = {
15
+ camel_case: lambda do |value, _option|
16
+ return value.to_s if value.to_s.empty?
17
+
18
+ name = value.to_s.split('_').collect(&:capitalize).join
19
+
20
+ name[0, 1].downcase + name[1..-1]
21
+ end,
22
+ pascal_case: ->(value, _option) { value.to_s.split('_').collect(&:capitalize).join },
23
+ pass_thru: ->(value, _option) { value }
24
+ }.freeze
25
+
26
+ private_constant :FUNCTIONS
27
+
28
+ def initialize
29
+ # append on the default case
30
+ super(FUNCTIONS.merge('': FUNCTIONS[:pass_thru]))
31
+ end
32
+
33
+ def transform(name, value, option)
34
+ object = resolve(name)
35
+ method = object.is_a?(Proc) ? :call : :transform
36
+
37
+ object.send(method, value, option)
38
+ end
39
+ end
40
+ end
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Hashcraft
11
- VERSION = '1.0.0-alpha.2'
11
+ VERSION = '1.0.0'
12
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashcraft
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-07 00:00:00.000000000 Z
11
+ date: 2020-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: guard-rspec
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.79.0
75
+ version: 0.88.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.79.0
82
+ version: 0.88.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.17.0
89
+ version: 0.18.5
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.17.0
96
+ version: 0.18.5
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: simplecov-console
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -132,21 +132,13 @@ files:
132
132
  - hashcraft.gemspec
133
133
  - lib/hashcraft.rb
134
134
  - lib/hashcraft/base.rb
135
- - lib/hashcraft/compiler.rb
136
- - lib/hashcraft/core_ext/hash.rb
137
135
  - lib/hashcraft/dsl.rb
138
136
  - lib/hashcraft/generic.rb
139
137
  - lib/hashcraft/generic/dictionary.rb
140
138
  - lib/hashcraft/generic/registry.rb
141
- - lib/hashcraft/mutator_registry.rb
142
- - lib/hashcraft/mutators/array.rb
143
- - lib/hashcraft/mutators/hash.rb
144
- - lib/hashcraft/mutators/property.rb
139
+ - lib/hashcraft/mutators.rb
145
140
  - lib/hashcraft/option.rb
146
- - lib/hashcraft/transformer_registry.rb
147
- - lib/hashcraft/transformers/camel_case.rb
148
- - lib/hashcraft/transformers/pascal_case.rb
149
- - lib/hashcraft/transformers/pass_thru.rb
141
+ - lib/hashcraft/transformers.rb
150
142
  - lib/hashcraft/version.rb
151
143
  homepage: https://github.com/bluemarblepayroll/hashcraft
152
144
  licenses:
@@ -168,9 +160,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
168
160
  version: '2.5'
169
161
  required_rubygems_version: !ruby/object:Gem::Requirement
170
162
  requirements:
171
- - - ">"
163
+ - - ">="
172
164
  - !ruby/object:Gem::Version
173
- version: 1.3.1
165
+ version: '0'
174
166
  requirements: []
175
167
  rubygems_version: 3.0.3
176
168
  signing_key:
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- # This class understands how to traverse an option chain and output a hash.
12
- class Compiler
13
- attr_reader :key_transformer, :option_set, :value_transformer
14
-
15
- def initialize(option_set, key_transformer: nil, value_transformer: nil)
16
- raise ArgumentError, 'option_set is required' unless option_set
17
-
18
- @option_set = option_set
19
- @key_transformer = TransformerRegistry.resolve(key_transformer)
20
- @value_transformer = TransformerRegistry.resolve(value_transformer)
21
-
22
- freeze
23
- end
24
-
25
- def evaluate!(data)
26
- data.each_with_object({}) do |(key, value), memo|
27
- option = option_set.find(key)
28
-
29
- evaluate_single!(memo, option, value)
30
- end
31
- end
32
-
33
- private
34
-
35
- attr_reader :data
36
-
37
- def evaluate_single!(data, option, value)
38
- key = option.key.empty? ? option.name : option.key
39
- transformed_key = key_transformer.transform(key, option)
40
-
41
- method = value.is_a?(Array) ? :evaluate_values! : :evaluate_value!
42
-
43
- send(method, option, data, transformed_key, value)
44
-
45
- self
46
- end
47
-
48
- def evaluate_values!(option, data, key, values)
49
- data[key] ||= []
50
-
51
- values.each do |value|
52
- data[key] <<
53
- if value.is_a?(Hashcraft::Base)
54
- value.to_h(
55
- key_transformer: key_transformer,
56
- value_transformer: value_transformer
57
- )
58
- else
59
- value_transformer.transform(value, option)
60
- end
61
- end
62
-
63
- self
64
- end
65
-
66
- def evaluate_value!(option, data, key, value)
67
- data[key] =
68
- if value.is_a?(Hashcraft::Base)
69
- value.to_h(
70
- key_transformer: key_transformer,
71
- value_transformer: value_transformer
72
- )
73
- else
74
- value_transformer.transform(value, option)
75
- end
76
-
77
- self
78
- end
79
- end
80
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module CoreExt
12
- # Monkey-patches for the core Hash class. These will be manually mixed in separately.
13
- module Hash
14
- unless method_defined?(:symbolize_keys)
15
- def symbolize_keys
16
- map { |k, v| [k.to_sym, v] }.to_h
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- require_relative 'mutators/array'
11
- require_relative 'mutators/hash'
12
- require_relative 'mutators/property'
13
-
14
- module Hashcraft
15
- # Singleton that knows how to register and retrieve mutator instances.
16
- class MutatorRegistry < Generic::Registry
17
- def initialize
18
- super(
19
- 'array' => Mutators::Array.instance,
20
- 'hash' => Mutators::Hash.instance,
21
- 'property' => Mutators::Property.instance,
22
- '' => Mutators::Property.instance
23
- )
24
- end
25
- end
26
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Mutators
12
- # When a hash's key is an arry then this mutator can be used to push a new value on the
13
- # respective array.
14
- class Array
15
- include Singleton
16
-
17
- def value!(data, key, value)
18
- data[key] ||= []
19
- data[key] << value
20
-
21
- self
22
- end
23
- end
24
- end
25
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Mutators
12
- # When a hash's key a Hash then this mutator can be used to merge a new value on the
13
- # respective hash.
14
- class Hash
15
- include Singleton
16
-
17
- def value!(data, key, value)
18
- data[key] ||= {}
19
- data[key].merge!(value || {})
20
-
21
- self
22
- end
23
- end
24
- end
25
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Mutators
12
- # When a hash key can be simply assigned to (like a property) then this mutator can be used to
13
- # simply do the assignment.
14
- class Property
15
- include Singleton
16
-
17
- def value!(data, key, value)
18
- data[key] = value
19
-
20
- self
21
- end
22
- end
23
- end
24
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- require_relative 'transformers/camel_case'
11
- require_relative 'transformers/pascal_case'
12
- require_relative 'transformers/pass_thru'
13
-
14
- module Hashcraft
15
- # Singleton that knows how to register and retrieve transformer instances.
16
- class TransformerRegistry < Generic::Registry
17
- def initialize
18
- super(
19
- 'camel_case' => Transformers::CamelCase.instance,
20
- 'pascal_case' => Transformers::PascalCase.instance,
21
- 'pass_thru' => Transformers::PassThru.instance,
22
- '' => Transformers::PassThru.instance
23
- )
24
- end
25
- end
26
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Transformers
12
- # Transform snake-cased to camel-cased string. Example:
13
- # date_of_birth => dateOfBirth
14
- # DATE_OF_BIRTH => dateOfBirth
15
- class CamelCase
16
- include Singleton
17
-
18
- def transform(value, _option)
19
- return '' if value.to_s.empty?
20
-
21
- name = value.to_s.split('_').collect(&:capitalize).join
22
-
23
- name[0, 1].downcase + name[1..-1]
24
- end
25
- end
26
- end
27
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Transformers
12
- # Transform snake-cased to pascal-cased string. Example:
13
- # date_of_birth => DateOfBirth
14
- # DATE_OF_BIRTH => DateOfBirth
15
- class PascalCase
16
- include Singleton
17
-
18
- def transform(value, _option)
19
- return '' if value.to_s.empty?
20
-
21
- value.to_s.split('_').collect(&:capitalize).join
22
- end
23
- end
24
- end
25
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Hashcraft
11
- module Transformers
12
- # Default transformer, simply returns the value passed in.
13
- class PassThru
14
- include Singleton
15
-
16
- def transform(value, _option)
17
- value
18
- end
19
- end
20
- end
21
- end