monolens 0.5.3 → 0.6.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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -85
  3. data/lib/monolens/command.rb +111 -14
  4. data/lib/monolens/error.rb +2 -0
  5. data/lib/monolens/file.rb +6 -0
  6. data/lib/monolens/jsonpath.rb +76 -0
  7. data/lib/monolens/lens/options.rb +26 -12
  8. data/lib/monolens/lens/signature/missing.rb +11 -0
  9. data/lib/monolens/lens/signature.rb +60 -0
  10. data/lib/monolens/lens.rb +25 -4
  11. data/lib/monolens/macros.rb +28 -0
  12. data/lib/monolens/namespace.rb +11 -0
  13. data/lib/monolens/registry.rb +77 -0
  14. data/lib/monolens/{array → stdlib/array}/compact.rb +2 -0
  15. data/lib/monolens/{array → stdlib/array}/join.rb +4 -0
  16. data/lib/monolens/{array → stdlib/array}/map.rb +13 -19
  17. data/lib/monolens/{array.rb → stdlib/array.rb} +8 -6
  18. data/lib/monolens/{check → stdlib/check}/not_empty.rb +4 -0
  19. data/lib/monolens/{check.rb → stdlib/check.rb} +4 -2
  20. data/lib/monolens/{coerce → stdlib/coerce}/date.rb +5 -0
  21. data/lib/monolens/{coerce → stdlib/coerce}/date_time.rb +6 -1
  22. data/lib/monolens/{coerce → stdlib/coerce}/integer.rb +4 -0
  23. data/lib/monolens/{coerce → stdlib/coerce}/string.rb +2 -0
  24. data/lib/monolens/{coerce.rb → stdlib/coerce.rb} +10 -8
  25. data/lib/monolens/{core → stdlib/core}/chain.rb +5 -3
  26. data/lib/monolens/{core → stdlib/core}/dig.rb +5 -0
  27. data/lib/monolens/stdlib/core/literal.rb +68 -0
  28. data/lib/monolens/{core → stdlib/core}/mapping.rb +15 -5
  29. data/lib/monolens/{core.rb → stdlib/core.rb} +10 -8
  30. data/lib/monolens/stdlib/object/allbut.rb +22 -0
  31. data/lib/monolens/{object → stdlib/object}/extend.rb +10 -5
  32. data/lib/monolens/{object → stdlib/object}/keys.rb +4 -0
  33. data/lib/monolens/stdlib/object/merge.rb +56 -0
  34. data/lib/monolens/{object → stdlib/object}/rename.rb +5 -1
  35. data/lib/monolens/{object → stdlib/object}/select.rb +9 -0
  36. data/lib/monolens/{object → stdlib/object}/transform.rb +8 -3
  37. data/lib/monolens/{object → stdlib/object}/values.rb +9 -4
  38. data/lib/monolens/stdlib/object.rb +55 -0
  39. data/lib/monolens/{skip → stdlib/skip}/null.rb +2 -0
  40. data/lib/monolens/{skip.rb → stdlib/skip.rb} +4 -2
  41. data/lib/monolens/{str → stdlib/str}/downcase.rb +2 -0
  42. data/lib/monolens/{str → stdlib/str}/split.rb +5 -1
  43. data/lib/monolens/{str → stdlib/str}/strip.rb +2 -0
  44. data/lib/monolens/{str → stdlib/str}/upcase.rb +2 -0
  45. data/lib/monolens/{str.rb → stdlib/str.rb} +10 -8
  46. data/lib/monolens/stdlib.rb +7 -0
  47. data/lib/monolens/type/any.rb +39 -0
  48. data/lib/monolens/type/array.rb +27 -0
  49. data/lib/monolens/type/boolean.rb +17 -0
  50. data/lib/monolens/type/callback.rb +17 -0
  51. data/lib/monolens/type/coercible.rb +10 -0
  52. data/lib/monolens/type/diggable.rb +9 -0
  53. data/lib/monolens/type/emptyable.rb +9 -0
  54. data/lib/monolens/type/integer.rb +18 -0
  55. data/lib/monolens/type/lenses.rb +17 -0
  56. data/lib/monolens/type/map.rb +30 -0
  57. data/lib/monolens/type/object.rb +17 -0
  58. data/lib/monolens/type/responding.rb +25 -0
  59. data/lib/monolens/type/strategy.rb +56 -0
  60. data/lib/monolens/type/string.rb +18 -0
  61. data/lib/monolens/type/symbol.rb +20 -0
  62. data/lib/monolens/type.rb +33 -0
  63. data/lib/monolens/version.rb +2 -2
  64. data/lib/monolens.rb +22 -66
  65. data/spec/fixtures/macro.yml +13 -0
  66. data/spec/fixtures/recursive.yml +15 -0
  67. data/spec/monolens/command/literal.yml +2 -0
  68. data/spec/monolens/command/literal2.yml +2 -0
  69. data/spec/monolens/command/upcase.lens.yml +4 -0
  70. data/spec/monolens/lens/test_options.rb +2 -14
  71. data/spec/monolens/lens/test_signature.rb +38 -0
  72. data/spec/monolens/{array → stdlib/array}/test_compact.rb +8 -0
  73. data/spec/monolens/{array → stdlib/array}/test_join.rb +0 -0
  74. data/spec/monolens/{array → stdlib/array}/test_map.rb +15 -0
  75. data/spec/monolens/{check → stdlib/check}/test_not_empty.rb +0 -0
  76. data/spec/monolens/{coerce → stdlib/coerce}/test_date.rb +0 -0
  77. data/spec/monolens/{coerce → stdlib/coerce}/test_datetime.rb +1 -1
  78. data/spec/monolens/{coerce → stdlib/coerce}/test_integer.rb +0 -0
  79. data/spec/monolens/{coerce → stdlib/coerce}/test_string.rb +0 -0
  80. data/spec/monolens/{core → stdlib/core}/test_dig.rb +0 -0
  81. data/spec/monolens/stdlib/core/test_literal.rb +73 -0
  82. data/spec/monolens/{core → stdlib/core}/test_mapping.rb +37 -1
  83. data/spec/monolens/stdlib/object/test_allbut.rb +31 -0
  84. data/spec/monolens/{object → stdlib/object}/test_extend.rb +0 -0
  85. data/spec/monolens/{object → stdlib/object}/test_keys.rb +0 -0
  86. data/spec/monolens/stdlib/object/test_merge.rb +133 -0
  87. data/spec/monolens/{object → stdlib/object}/test_rename.rb +0 -0
  88. data/spec/monolens/{object → stdlib/object}/test_select.rb +0 -0
  89. data/spec/monolens/{object → stdlib/object}/test_transform.rb +0 -0
  90. data/spec/monolens/{object → stdlib/object}/test_values.rb +0 -0
  91. data/spec/monolens/{skip → stdlib/skip}/test_null.rb +0 -0
  92. data/spec/monolens/{str → stdlib/str}/test_downcase.rb +0 -0
  93. data/spec/monolens/{str → stdlib/str}/test_split.rb +0 -0
  94. data/spec/monolens/{str → stdlib/str}/test_strip.rb +0 -0
  95. data/spec/monolens/{str → stdlib/str}/test_upcase.rb +0 -0
  96. data/spec/monolens/test_command.rb +145 -0
  97. data/spec/monolens/test_error_traceability.rb +1 -1
  98. data/spec/monolens/test_jsonpath.rb +88 -0
  99. data/spec/monolens/test_lens.rb +1 -1
  100. data/spec/test_documentation.rb +52 -0
  101. data/spec/test_monolens.rb +20 -0
  102. data/tasks/test.rake +1 -1
  103. metadata +91 -55
  104. data/lib/monolens/core/literal.rb +0 -11
  105. data/lib/monolens/object.rb +0 -41
  106. data/spec/monolens/core/test_literal.rb +0 -13
@@ -0,0 +1,77 @@
1
+ module Monolens
2
+ class Registry
3
+ LENS_NAME_RX = /^[a-z]+\.[a-z][a-zA-Z\-_]+$/
4
+
5
+ def initialize(registry = {}, default_namespace = 'core')
6
+ @registry = registry
7
+ @default_namespace = default_namespace
8
+ end
9
+
10
+ def define_namespace(name, impl_module)
11
+ @registry[name] = impl_module
12
+ end
13
+
14
+ def load_file(file, registry = self)
15
+ load_yaml(::File.read(file), registry)
16
+ end
17
+
18
+ def load_yaml(yaml, registry = self)
19
+ Monolens::File.new(YAML.safe_load(yaml), registry)
20
+ end
21
+
22
+ def lens(arg, registry = self)
23
+ case arg
24
+ when Lens then arg
25
+ when ::Array then chain(arg, registry)
26
+ when ::String, ::Symbol then leaf_lens(arg, registry)
27
+ when ::Hash then hash_lens(arg, registry)
28
+ else
29
+ raise Error, "No such lens #{arg} (#{arg.class})"
30
+ end
31
+ end
32
+
33
+ def fork(default_namespace = 'self')
34
+ Registry.new(@registry.dup, default_namespace)
35
+ end
36
+
37
+ private
38
+
39
+ def chain(lenses, registry)
40
+ Core::Chain.new(lenses.map{|l| lens(l) }, registry)
41
+ end
42
+
43
+ def file_lens(arg, registry)
44
+ File.new(arg, registry)
45
+ end
46
+
47
+ def leaf_lens(arg, registry)
48
+ namespace_name, lens_name = split_lens_name(arg)
49
+ factor_lens(namespace_name, lens_name, {}, registry)
50
+ end
51
+
52
+ def hash_lens(arg, registry)
53
+ return file_lens(arg, registry) if arg['version'] || arg[:version]
54
+ raise Error, "Invalid lens #{arg}" unless arg.size == 1
55
+
56
+ name, options = arg.to_a.first
57
+ namespace_name, lens_name = split_lens_name(name)
58
+ factor_lens(namespace_name, lens_name, options, registry)
59
+ end
60
+
61
+ def factor_lens(namespace_name, lens_name, options, registry)
62
+ if namespace = @registry[namespace_name]
63
+ namespace.factor_lens(namespace_name, lens_name, options, registry)
64
+ else
65
+ raise Error, "No such namespace #{namespace_name}"
66
+ end
67
+ end
68
+
69
+ def split_lens_name(name)
70
+ if name =~ LENS_NAME_RX
71
+ name.to_s.split('.')
72
+ else
73
+ [@default_namespace, name]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -3,6 +3,8 @@ module Monolens
3
3
  class Compact
4
4
  include Lens
5
5
 
6
+ signature(Type::Array, Type::String)
7
+
6
8
  def call(arg, world = {})
7
9
  is_array!(arg, world)
8
10
 
@@ -3,6 +3,10 @@ module Monolens
3
3
  class Join
4
4
  include Lens
5
5
 
6
+ signature(Type::Array, Type::String, {
7
+ separator: [Type::String, false]
8
+ })
9
+
6
10
  def call(arg, world = {})
7
11
  is_array!(arg, world)
8
12
 
@@ -3,44 +3,38 @@ module Monolens
3
3
  class Map
4
4
  include Lens
5
5
 
6
- def initialize(arg)
7
- options, lenses = case arg
8
- when ::Hash
9
- opts = arg.dup; opts.delete(:lenses)
10
- _, ls = fetch_on(:lenses, arg)
11
- raise ArgumentError, 'Lenses are required' if ls.nil?
12
- [ opts, ls ]
13
- else
14
- [{}, arg]
15
- end
16
- super(options)
17
- @lenses = Monolens.lens(lenses)
18
- end
6
+ signature(Type::Array, Type::Array, {
7
+ on_error: [Type::Strategy.error(%w{handler keep fail null skip}), false],
8
+ lenses: [Type::Lenses, true]
9
+ })
19
10
 
20
11
  def call(arg, world = {})
21
- is_enumerable!(arg, world)
12
+ is_array!(arg, world)
22
13
 
14
+ lenses = option(:lenses)
23
15
  result = []
24
- arg.each_with_index do |a, i|
16
+ arg.each_with_index do |member, i|
25
17
  deeper(world, i) do |w|
26
18
  begin
27
- result << @lenses.call(a, w)
19
+ result << lenses.call(member, w)
28
20
  rescue Monolens::LensError => ex
29
21
  strategy = option(:on_error, :fail)
30
- handle_error(strategy, ex, result, world)
22
+ handle_error(strategy, member, ex, result, world)
31
23
  end
32
24
  end
33
25
  end
34
26
  result
35
27
  end
36
28
 
37
- def handle_error(strategy, ex, result, world)
29
+ def handle_error(strategy, member, ex, result, world)
38
30
  strategy = strategy.to_sym unless strategy.is_a?(::Array)
39
31
  case strategy
40
32
  when ::Array
41
- strategy.each{|s| handle_error(s, ex, result, world) }
33
+ strategy.each{|s| handle_error(s, member, ex, result, world) }
42
34
  when :handler
43
35
  error_handler!(world).call(ex)
36
+ when :keep
37
+ result << member
44
38
  when :fail
45
39
  raise
46
40
  when :null
@@ -1,17 +1,19 @@
1
1
  module Monolens
2
2
  module Array
3
- def compact(options = {})
4
- Compact.new(options)
3
+ extend Namespace
4
+
5
+ def compact(options, registry)
6
+ Compact.new(options, registry)
5
7
  end
6
8
  module_function :compact
7
9
 
8
- def join(options = {})
9
- Join.new(options)
10
+ def join(options, registry)
11
+ Join.new(options, registry)
10
12
  end
11
13
  module_function :join
12
14
 
13
- def map(options)
14
- Map.new(options)
15
+ def map(options, registry)
16
+ Map.new(options, registry)
15
17
  end
16
18
  module_function :map
17
19
 
@@ -5,6 +5,10 @@ module Monolens
5
5
  class NotEmpty
6
6
  include Lens
7
7
 
8
+ signature(Type::Emptyable, Type::Emptyable, {
9
+ message: [Type::String, false]
10
+ })
11
+
8
12
  def call(arg, world = {})
9
13
  if arg.nil?
10
14
  do_fail!(arg, world)
@@ -1,7 +1,9 @@
1
1
  module Monolens
2
2
  module Check
3
- def notEmpty(options)
4
- NotEmpty.new(options)
3
+ extend Namespace
4
+
5
+ def notEmpty(options, registry)
6
+ NotEmpty.new(options, registry)
5
7
  end
6
8
  module_function :notEmpty
7
9
 
@@ -9,6 +9,11 @@ module Monolens
9
9
  nil
10
10
  ]
11
11
 
12
+ signature(Type::Coercible.to(Type::String), Type::Date, {
13
+ parser: [Type::DateTimeParser, false],
14
+ formats: [Type::Array.of(Type::String), false]
15
+ })
16
+
12
17
  def call(arg, world = {})
13
18
  return arg if arg.is_a?(::Date)
14
19
 
@@ -9,6 +9,11 @@ module Monolens
9
9
  nil
10
10
  ]
11
11
 
12
+ signature(Type::Coercible.to(Type::String), Type::DateTime, {
13
+ parser: [Type::DateTimeParser, false],
14
+ formats: [Type::Array.of(Type::String), false]
15
+ })
16
+
12
17
  def call(arg, world = {})
13
18
  return arg if arg.is_a?(::DateTime)
14
19
 
@@ -16,7 +21,7 @@ module Monolens
16
21
 
17
22
  date = nil
18
23
  first_error = nil
19
- formats = @options.fetch(:formats, DEFAULT_FORMATS)
24
+ formats = option(:formats, DEFAULT_FORMATS)
20
25
  formats.each do |format|
21
26
  begin
22
27
  return date = strptime(arg, format)
@@ -5,10 +5,14 @@ module Monolens
5
5
  class Integer
6
6
  include Lens
7
7
 
8
+ signature(Type::Coercible.to(Type::Integer), Type::Integer)
9
+
8
10
  def call(arg, world = {})
9
11
  Integer(arg)
10
12
  rescue => ex
11
13
  fail!(ex.message, world)
14
+ rescue ArgumentError => ex
15
+ fail!(ex.message, world)
12
16
  end
13
17
  end
14
18
  end
@@ -5,6 +5,8 @@ module Monolens
5
5
  class String
6
6
  include Lens
7
7
 
8
+ signature(Type::Any, Type::String)
9
+
8
10
  def call(arg, world = {})
9
11
  arg.to_s
10
12
  end
@@ -1,22 +1,24 @@
1
1
  module Monolens
2
2
  module Coerce
3
- def date(options = {})
4
- Date.new(options)
3
+ extend Namespace
4
+
5
+ def date(options, registry)
6
+ Date.new(options, registry)
5
7
  end
6
8
  module_function :date
7
9
 
8
- def integer(options = {})
9
- Integer.new(options)
10
+ def integer(options, registry)
11
+ Integer.new(options, registry)
10
12
  end
11
13
  module_function :integer
12
14
 
13
- def datetime(options = {})
14
- DateTime.new(options)
15
+ def datetime(options, registry)
16
+ DateTime.new(options, registry)
15
17
  end
16
18
  module_function :datetime
17
19
 
18
- def string(options = {})
19
- String.new(options)
20
+ def string(options, registry)
21
+ String.new(options, registry)
20
22
  end
21
23
  module_function :string
22
24
 
@@ -3,9 +3,11 @@ module Monolens
3
3
  class Chain
4
4
  include Lens
5
5
 
6
- def initialize(lenses)
7
- super({})
8
- @lenses = lenses
6
+ signature(Type::Any, Type::Any)
7
+
8
+ def initialize(options, registry)
9
+ super({}, registry)
10
+ @lenses = options.map{|l| lens(l) }
9
11
  end
10
12
 
11
13
  def call(arg, world = {})
@@ -3,6 +3,11 @@ module Monolens
3
3
  class Dig
4
4
  include Lens
5
5
 
6
+ signature(Type::Diggable, Type::Any, {
7
+ defn: [Type::Array.of(Type::Any.of(Type::Integer, Type::String)), true],
8
+ on_missing: [Type::Strategy.missing(%w{fail null}), false]
9
+ })
10
+
6
11
  def call(arg, world = {})
7
12
  option(:defn, []).inject(arg) do |memo, part|
8
13
  dig_on(part, memo, world)
@@ -0,0 +1,68 @@
1
+ module Monolens
2
+ module Core
3
+ class Literal
4
+ include Lens
5
+
6
+ signature(Type::Any, Type::Any, {
7
+ defn: [Type::Any, true],
8
+ jsonpath: [Type::Object, false]
9
+ })
10
+
11
+ def initialize(options, registry)
12
+ super(options, registry)
13
+ @root_symbol = extract_jsonpath_root_symbol
14
+ @one_rx = Jsonpath.one_detect_rx(@root_symbol)
15
+ @interpolate_rx = Jsonpath.interpolate_detect_rx(@root_symbol)
16
+ end
17
+
18
+ def call(arg, world = {})
19
+ instantiate(option(:defn), arg, world)
20
+ end
21
+
22
+ private
23
+
24
+ def instantiate(obj, input, world)
25
+ case obj
26
+ when ::Array
27
+ obj.map {|item|
28
+ instantiate(item, input, world)
29
+ }
30
+ when ::Hash
31
+ obj.each_with_object({}){|(k,v),memo|
32
+ memo[k] = instantiate(v, input, world)
33
+ }
34
+ when @one_rx
35
+ Jsonpath.one(obj, input, jsonpath_options(input))
36
+ when @interpolate_rx
37
+ Jsonpath.interpolate(obj, input, jsonpath_options(input))
38
+ else
39
+ obj
40
+ end
41
+ end
42
+
43
+ def jsonpath_options(input)
44
+ {
45
+ use_symbols: use_symbols?(input),
46
+ root_symbol: @root_symbol
47
+ }
48
+ end
49
+
50
+ def extract_jsonpath_root_symbol
51
+ opts = option(:jsonpath, {})
52
+ _, symbol = fetch_on(:root_symbol, opts, '$')
53
+ symbol
54
+ end
55
+
56
+ def use_symbols?(input)
57
+ case input
58
+ when ::Hash
59
+ input.keys.any?{|s| s.is_a?(Symbol) }
60
+ when ::Array
61
+ input.any?{|x| use_symbols?(x) }
62
+ else
63
+ false
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,8 +3,16 @@ module Monolens
3
3
  class Mapping
4
4
  include Lens
5
5
 
6
+ signature(Type::Any, Type::Any, {
7
+ defn: [Type::Object, false],
8
+ values: [Type::Object, false], # deprecated
9
+ default: [Type::Any, false],
10
+ fallback: [Type::Callback, false],
11
+ on_missing: [Type::Strategy.missing(%w{default fail fallback keep null}), false]
12
+ })
13
+
6
14
  def call(arg, world = {})
7
- option(:values, {}).fetch(arg) do
15
+ option(:defn, option(:values, {})).fetch(arg) do
8
16
  on_missing(arg, world)
9
17
  end
10
18
  end
@@ -14,17 +22,19 @@ module Monolens
14
22
  def on_missing(arg, world)
15
23
  strategy = option(:on_missing, :fail)
16
24
  case strategy.to_sym
17
- when :fail
18
- fail!("Unrecognized value `#{arg}`", world)
19
25
  when :default
20
26
  option(:default, nil)
21
- when :null
22
- nil
27
+ when :fail
28
+ fail!("Unrecognized value `#{arg}`", world)
23
29
  when :fallback
24
30
  missing_fallback = ->(arg, world) do
25
31
  raise Monolens::Error, "Unexpected missing fallback handler"
26
32
  end
27
33
  option(:fallback, missing_fallback).call(self, arg, world)
34
+ when :keep
35
+ arg
36
+ when :null
37
+ nil
28
38
  else
29
39
  raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
30
40
  end
@@ -1,22 +1,24 @@
1
1
  module Monolens
2
2
  module Core
3
- def chain(parts)
4
- Chain.new(parts)
3
+ extend Namespace
4
+
5
+ def chain(options, registry)
6
+ Chain.new(options, registry)
5
7
  end
6
8
  module_function :chain
7
9
 
8
- def dig(options)
9
- Dig.new(options)
10
+ def dig(options, registry)
11
+ Dig.new(options, registry)
10
12
  end
11
13
  module_function :dig
12
14
 
13
- def literal(options)
14
- Literal.new(options)
15
+ def literal(options, registry)
16
+ Literal.new(options, registry)
15
17
  end
16
18
  module_function :literal
17
19
 
18
- def mapping(options)
19
- Mapping.new(options)
20
+ def mapping(options, registry)
21
+ Mapping.new(options, registry)
20
22
  end
21
23
  module_function :mapping
22
24
 
@@ -0,0 +1,22 @@
1
+ module Monolens
2
+ module Object
3
+ class Allbut
4
+ include Lens
5
+
6
+ signature(Type::Object, Type::Object, {
7
+ defn: [Type::Array.of(Type::Name), false]
8
+ })
9
+
10
+ def call(arg, world = {})
11
+ is_hash!(arg, world)
12
+
13
+ allbut = option(:defn, [])
14
+ arg.delete_if{|k|
15
+ allbut.include?(k) || \
16
+ allbut.include?(k.to_s) || \
17
+ allbut.include?(k.to_sym)
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,11 +3,16 @@ module Monolens
3
3
  class Extend
4
4
  include Lens
5
5
 
6
- def initialize(options)
7
- super(options)
6
+ signature(Type::Object, Type::Object, {
7
+ defn: [Type::Map.of(Type::Name, Type::Lenses), false],
8
+ on_error: [Type::Strategy.error(%w{fail handler null skip}), false],
9
+ })
10
+
11
+ def initialize(options, registry)
12
+ super(options, registry)
8
13
  ts = option(:defn, {})
9
14
  ts.each_pair do |k,v|
10
- ts[k] = Monolens.lens(v)
15
+ ts[k] = lens(v)
11
16
  end
12
17
  end
13
18
 
@@ -35,10 +40,10 @@ module Monolens
35
40
  case strategy
36
41
  when ::Array
37
42
  strategy.each{|s| handle_error(s, ex, result, attr, world) }
38
- when :handler
39
- error_handler!(world).call(ex)
40
43
  when :fail
41
44
  raise
45
+ when :handler
46
+ error_handler!(world).call(ex)
42
47
  when :null
43
48
  result[attr] = nil
44
49
  when :skip
@@ -3,6 +3,10 @@ module Monolens
3
3
  class Keys
4
4
  include Lens
5
5
 
6
+ signature(Type::Object, Type::Object, {
7
+ lenses: [Type::Lenses, false],
8
+ })
9
+
6
10
  def call(arg, world = {})
7
11
  is_hash!(arg, world)
8
12
 
@@ -0,0 +1,56 @@
1
+ module Monolens
2
+ module Object
3
+ class Merge
4
+ include Lens
5
+
6
+ signature(Type::Object, Type::Object, {
7
+ priority: [Type::Strategy.priority(%w{input defn}), false],
8
+ deep: [Type::Boolean, false],
9
+ defn: [Type::Object, true]
10
+ })
11
+
12
+ def call(input, world = {})
13
+ is_hash!(input, world)
14
+
15
+ v1, v2 = input, option(:defn, {})
16
+ if deep?
17
+ deep_merge(v1, v2, world, priority_at_input?)
18
+ else
19
+ normal_merge(v1, v2, world, priority_at_input?)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def normal_merge(v1, v2, world, priority_at_input)
26
+ is_hash!(v1, world)
27
+ is_hash!(v2, world)
28
+
29
+ v1.merge(v2) do |k, v11, v22|
30
+ priority_at_input? ? v11 : v22
31
+ end
32
+ end
33
+
34
+ def deep_merge(v1, v2, world, priority_at_input)
35
+ case v1
36
+ when ::Hash
37
+ is_hash!(v2, world)
38
+
39
+ v1.merge(v2) do |k, v11, v22|
40
+ deep_merge(v11, v22, world, priority_at_input)
41
+ end
42
+ else
43
+ priority_at_input? ? v1 : v2
44
+ end
45
+ end
46
+
47
+ def deep?
48
+ option(:deep, false) == true
49
+ end
50
+
51
+ def priority_at_input?
52
+ @pati ||= (option(:priority, 'defn').to_s == 'input')
53
+ end
54
+ end
55
+ end
56
+ end
@@ -3,11 +3,15 @@ module Monolens
3
3
  class Rename
4
4
  include Lens
5
5
 
6
+ signature(Type::Object, Type::Object, {
7
+ defn: [Type::Map.of(Type::Name, Type::Name), false],
8
+ })
9
+
6
10
  def call(arg, world = {})
7
11
  is_hash!(arg, world)
8
12
 
9
13
  dup = arg.dup
10
- option(:defn).each_pair do |oldname, newname|
14
+ option(:defn, {}).each_pair do |oldname, newname|
11
15
  actual_name, value = fetch_on(oldname, arg)
12
16
  newname = actual_name.is_a?(Symbol) ? newname.to_sym : newname.to_s
13
17
  dup.delete(actual_name)
@@ -3,6 +3,15 @@ module Monolens
3
3
  class Select
4
4
  include Lens
5
5
 
6
+ signature(Type::Object, Type::Object, {
7
+ strategy: [Type::Strategy.selection(%w{all first}), false],
8
+ defn: [Type::Any.of(
9
+ Type::Array.of(Type::Name),
10
+ Type::Map.of(Type::Name, Type::Any.of(Type::Array.of(Type::Name), Type::Name))
11
+ ), true],
12
+ on_missing: [Type::Strategy.missing(%w{fail null skip}), false]
13
+ })
14
+
6
15
  def call(arg, world = {})
7
16
  is_hash!(arg, world)
8
17
 
@@ -3,11 +3,16 @@ module Monolens
3
3
  class Transform
4
4
  include Lens
5
5
 
6
- def initialize(options)
7
- super(options)
6
+ signature(Type::Object, Type::Object, {
7
+ defn: [Type::Map.of(Type::Name, Type::Lenses), true],
8
+ on_missing: [Type::Strategy.missing(%w{fail null skip}), false]
9
+ })
10
+
11
+ def initialize(options, registry)
12
+ super(options, registry)
8
13
  ts = option(:defn, {})
9
14
  ts.each_pair do |k,v|
10
- ts[k] = Monolens.lens(v)
15
+ ts[k] = lens(v)
11
16
  end
12
17
  end
13
18