eco-helpers 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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/README.md +20 -0
  5. data/eco-helpers.gemspec +34 -0
  6. data/lib/eco-helpers.rb +15 -0
  7. data/lib/eco/api.rb +13 -0
  8. data/lib/eco/api/common.rb +10 -0
  9. data/lib/eco/api/common/people.rb +17 -0
  10. data/lib/eco/api/common/people/base_parser.rb +16 -0
  11. data/lib/eco/api/common/people/default_parsers.rb +40 -0
  12. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +28 -0
  13. data/lib/eco/api/common/people/default_parsers/date_parser.rb +33 -0
  14. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +33 -0
  15. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +23 -0
  16. data/lib/eco/api/common/people/default_parsers/select_parser.rb +29 -0
  17. data/lib/eco/api/common/people/entries.rb +120 -0
  18. data/lib/eco/api/common/people/person_entry.rb +380 -0
  19. data/lib/eco/api/common/people/person_factory.rb +114 -0
  20. data/lib/eco/api/common/people/person_modifier.rb +62 -0
  21. data/lib/eco/api/common/people/person_parser.rb +140 -0
  22. data/lib/eco/api/common/people/types.rb +47 -0
  23. data/lib/eco/api/common/session.rb +15 -0
  24. data/lib/eco/api/common/session/base_session.rb +46 -0
  25. data/lib/eco/api/common/session/environment.rb +47 -0
  26. data/lib/eco/api/common/session/file_manager.rb +90 -0
  27. data/lib/eco/api/common/session/logger.rb +105 -0
  28. data/lib/eco/api/common/session/mailer.rb +92 -0
  29. data/lib/eco/api/common/session/s3_uploader.rb +110 -0
  30. data/lib/eco/api/common/version_patches.rb +11 -0
  31. data/lib/eco/api/common/version_patches/external_person.rb +11 -0
  32. data/lib/eco/api/eco_faker.rb +59 -0
  33. data/lib/eco/api/organization.rb +13 -0
  34. data/lib/eco/api/organization/account.rb +23 -0
  35. data/lib/eco/api/organization/people.rb +118 -0
  36. data/lib/eco/api/organization/policy_groups.rb +51 -0
  37. data/lib/eco/api/organization/preferences.rb +28 -0
  38. data/lib/eco/api/organization/preferences_reference.json +23 -0
  39. data/lib/eco/api/organization/presets.rb +138 -0
  40. data/lib/eco/api/organization/presets_backup.rb +220 -0
  41. data/lib/eco/api/organization/presets_values.json +10 -0
  42. data/lib/eco/api/organization/tag_tree.rb +134 -0
  43. data/lib/eco/api/organization_old.rb +73 -0
  44. data/lib/eco/api/session.rb +180 -0
  45. data/lib/eco/api/session/batch.rb +132 -0
  46. data/lib/eco/api/session/batch_job.rb +152 -0
  47. data/lib/eco/api/session/batch_jobs.rb +131 -0
  48. data/lib/eco/api/session/batch_status.rb +138 -0
  49. data/lib/eco/api/session/task.rb +92 -0
  50. data/lib/eco/api/session_config.rb +179 -0
  51. data/lib/eco/api/session_config/api.rb +47 -0
  52. data/lib/eco/api/session_config/apis.rb +78 -0
  53. data/lib/eco/api/session_config/files.rb +30 -0
  54. data/lib/eco/api/session_config/logger.rb +54 -0
  55. data/lib/eco/api/session_config/mailer.rb +65 -0
  56. data/lib/eco/api/session_config/people.rb +89 -0
  57. data/lib/eco/api/session_config/s3_bucket.rb +62 -0
  58. data/lib/eco/api/session_config/use_cases.rb +30 -0
  59. data/lib/eco/api/usecases.rb +12 -0
  60. data/lib/eco/api/usecases/base_case.rb +14 -0
  61. data/lib/eco/api/usecases/case_data.rb +13 -0
  62. data/lib/eco/api/usecases/default_cases.rb +53 -0
  63. data/lib/eco/api/usecases/default_cases/change_email_case.rb +47 -0
  64. data/lib/eco/api/usecases/default_cases/create_details_case.rb +29 -0
  65. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +49 -0
  66. data/lib/eco/api/usecases/default_cases/delete_case.rb +20 -0
  67. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +24 -0
  68. data/lib/eco/api/usecases/default_cases/hris_case.rb +67 -0
  69. data/lib/eco/api/usecases/default_cases/new_email_case.rb +26 -0
  70. data/lib/eco/api/usecases/default_cases/new_id_case.rb +26 -0
  71. data/lib/eco/api/usecases/default_cases/refresh_presets.rb +25 -0
  72. data/lib/eco/api/usecases/default_cases/reinvite_case.rb +22 -0
  73. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +36 -0
  74. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +24 -0
  75. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +44 -0
  76. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +39 -0
  77. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -0
  78. data/lib/eco/api/usecases/default_cases/update_details_case.rb +30 -0
  79. data/lib/eco/api/usecases/default_cases/upsert_account_case.rb +35 -0
  80. data/lib/eco/api/usecases/use_case.rb +177 -0
  81. data/lib/eco/api/usecases/use_group.rb +104 -0
  82. data/lib/eco/cli.rb +9 -0
  83. data/lib/eco/cli/input.rb +109 -0
  84. data/lib/eco/cli/input_multi.rb +137 -0
  85. data/lib/eco/cli/root.rb +8 -0
  86. data/lib/eco/cli/session.rb +9 -0
  87. data/lib/eco/cli/session/batch.rb +9 -0
  88. data/lib/eco/common.rb +7 -0
  89. data/lib/eco/common/base_cli.rb +116 -0
  90. data/lib/eco/common/language.rb +9 -0
  91. data/lib/eco/data.rb +9 -0
  92. data/lib/eco/data/crypto.rb +7 -0
  93. data/lib/eco/data/crypto/encryption.rb +318 -0
  94. data/lib/eco/data/files.rb +10 -0
  95. data/lib/eco/data/files/directory.rb +93 -0
  96. data/lib/eco/data/files/file_pattern.rb +32 -0
  97. data/lib/eco/data/files/helpers.rb +90 -0
  98. data/lib/eco/data/mapper.rb +54 -0
  99. data/lib/eco/data/random.rb +10 -0
  100. data/lib/eco/data/random/distribution.rb +133 -0
  101. data/lib/eco/data/random/fake.rb +320 -0
  102. data/lib/eco/data/random/values.rb +80 -0
  103. data/lib/eco/language.rb +12 -0
  104. data/lib/eco/language/curry.rb +28 -0
  105. data/lib/eco/language/hash_transform.rb +68 -0
  106. data/lib/eco/language/hash_transform_modifier.rb +114 -0
  107. data/lib/eco/language/match.rb +30 -0
  108. data/lib/eco/language/match_modifier.rb +190 -0
  109. data/lib/eco/language/models.rb +11 -0
  110. data/lib/eco/language/models/attribute_parser.rb +38 -0
  111. data/lib/eco/language/models/collection.rb +181 -0
  112. data/lib/eco/language/models/modifier.rb +68 -0
  113. data/lib/eco/language/models/wrap.rb +114 -0
  114. data/lib/eco/language/values_at.rb +159 -0
  115. data/lib/eco/lexic/dictionary.rb +33 -0
  116. data/lib/eco/lexic/dictionary/dictionary.txt +355484 -0
  117. data/lib/eco/lexic/dictionary/tags.json +38 -0
  118. data/lib/eco/scripting.rb +30 -0
  119. data/lib/eco/scripting/README.md +11 -0
  120. data/lib/eco/scripting/arguments.rb +40 -0
  121. data/lib/eco/tester.rb +97 -0
  122. data/lib/eco/version.rb +3 -0
  123. metadata +325 -0
@@ -0,0 +1,11 @@
1
+ module Eco
2
+ module Language
3
+ module Models
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'models/modifier'
9
+ require_relative 'models/collection'
10
+ require_relative 'models/attribute_parser'
11
+ require_relative 'models/wrap'
@@ -0,0 +1,38 @@
1
+ module Eco
2
+ module Language
3
+ module Models
4
+
5
+ class AttributeParser
6
+
7
+ attr_reader :attr
8
+
9
+ def initialize(attr, dependencies: {})
10
+ @attr = attr
11
+ @dependencies = dependencies
12
+ end
13
+
14
+ def def_parser(&block)
15
+ @parser = block
16
+ self
17
+ end
18
+
19
+ def def_serializer(&block)
20
+ @serializer = block
21
+ self
22
+ end
23
+
24
+ def parse(source, dependencies: {})
25
+ raise "There is no parser for this attribue '#{attr}'" if !@parser
26
+ @parser.call(source, @dependencies.merge(dependencies), attr)
27
+ end
28
+
29
+ def serialize(object, dependencies: {})
30
+ raise "There is no serializer for this attribue '#{attr}'" if !@serializer
31
+ @serializer.call(object, @dependencies.merge(dependencies), attr)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,181 @@
1
+ module Eco
2
+ module Language
3
+ module Models
4
+ class Collection < Array
5
+
6
+ ATTR_PRESENCE_METHODS = ["present", "empty", "present_all?", "present_some?"]
7
+ ATTR_COLLECTION_METHODS = ["exclude", "remove", "attr", "attr?", "attrs", "unique_attrs", "contains"] + ATTR_PRESENCE_METHODS
8
+
9
+ #attr_reader :kclass
10
+
11
+ def initialize(data = [], klass:, factory: nil, handy: Eco::Common::Language.new)
12
+ raise "Raise klass required, given: #{klass}" if !klass
13
+ @klass = klass
14
+ @factory = factory
15
+ @handy = handy
16
+ super(to_klass(data))
17
+ end
18
+
19
+ def newFrom(data)
20
+ self.class.new(data, klass: @klass, factory: @factory)
21
+ end
22
+
23
+ def to_c
24
+ Collection.new(self, klass: @klass, factory: @factory)
25
+ end
26
+
27
+ def new
28
+ self.class.new(self.to_a, klass: @klass, factory: @factory)
29
+ end
30
+
31
+ def <(value)
32
+ self.clear << value
33
+ on_change
34
+ self
35
+ end
36
+
37
+ def <<(value)
38
+ self.concat(into_a(value))
39
+ on_change
40
+ self
41
+ end
42
+
43
+ def update(&block)
44
+ newFrom self.map(&block)
45
+ end
46
+
47
+ def delete(value)
48
+ self < self - value
49
+ on_change
50
+ self
51
+ end
52
+
53
+ # attr dependant methods
54
+ def exclude(attr, value, modifier = Language::MatchModifier.new)
55
+ newFrom self - self.attr(attr, value, modifier)
56
+ end
57
+
58
+ def remove(attr, value, modifier = Language::MatchModifier.new)
59
+ self < self.exclude(attr, value, modifier)
60
+ on_change
61
+ self
62
+ end
63
+
64
+ def attr(attr, value = true, modifier = Language::MatchModifier.new)
65
+ if !!value == value # boolean?
66
+ self.present(attr, value)
67
+ else
68
+ return newFrom self.select { |object|
69
+ attr_val = fetch_attr(object, attr)
70
+ match?(attr_val, value, modifier)
71
+ }
72
+ end
73
+ end
74
+
75
+ def attr?(attr, value = true, modifier = Language::MatchModifier.new)
76
+ modifier = modifier.new.reverse
77
+ if !!value == value # boolean?
78
+ self.present(attr, value).length == self.length
79
+ else
80
+ obj_vals = self.attrs(attr)
81
+ return match?(obj_vals, value, modifier)
82
+ end
83
+ end
84
+
85
+ def attrs(attr)
86
+ self.map { |object| fetch_attr(object, attr) }
87
+ end
88
+
89
+ def unique_attrs(attr)
90
+ self.to_h(attr).keys
91
+ end
92
+
93
+ def present(attr, flag = true)
94
+ block = ->(o) { !!fetch_attr(o, attr) == !!flag }
95
+ newFrom self.select(&block)
96
+ end
97
+
98
+ def empty(attr, flag = true)
99
+ self.present(attr, !flag)
100
+ end
101
+
102
+ def present_all?(attr, flag = true)
103
+ self.present(attr, flag).length == self.length
104
+ end
105
+
106
+ def present_some?(attr, flag = true)
107
+ self.present(attr, flag).length > 0
108
+ end
109
+
110
+ def contains(attr, value, modifier = Language::MatchModifier.new)
111
+ modifier = modifier.new.pattern
112
+ self.attr(attr, value, modifier)
113
+ end
114
+
115
+ def group_by(attr)
116
+ #self.to_h(attr)
117
+ return {} if !attr
118
+ self.to_a.group_by { |object| object.method(attr).call }
119
+ end
120
+
121
+ def to_h(attr)
122
+ return {} if !attr
123
+ self.to_a.group_by { |object| object.method(attr).call }
124
+ end
125
+
126
+ # class level methods
127
+ def self.attr_collection (*attrs)
128
+ block = ->(method) { attrs_create_method(attrs, method) }
129
+ ATTR_COLLECTION_METHODS.each(&block)
130
+ end
131
+
132
+ def self.attr_presence (*attrs)
133
+ block = ->(method) { attrs_create_method(attrs, method) }
134
+ ATTR_PRESENCE_METHODS.each(&block)
135
+ end
136
+
137
+ def self.attrs_create_method(attrs, method)
138
+ attrs.each do |attr|
139
+ attr = attr.to_s
140
+ if method.include?("attr")
141
+ attr_method = method.sub("attr", attr)
142
+ else
143
+ attr_method = "#{attr}_#{method}"
144
+ end
145
+ define_method attr_method do |*args|
146
+ send(method, attr, *args)
147
+ end
148
+ end
149
+ end
150
+
151
+ protected
152
+
153
+ def on_change
154
+ # function to be overriden by children classes
155
+ end
156
+
157
+ def into_a(value)
158
+ value = [].push(value) unless value.is_a?(Array)
159
+ value
160
+ end
161
+
162
+ private
163
+
164
+ def match?(*args)
165
+ @handy.match?(*args)
166
+ end
167
+
168
+ def to_klass(list)
169
+ into_a(list).map do |v|
170
+ v.is_a?(@klass) ? v : @factory&.new(v) || @klass.new(v)
171
+ end
172
+ end
173
+
174
+ def fetch_attr(object,attr)
175
+ object.method(attr).call
176
+ end
177
+
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,68 @@
1
+ module Eco
2
+ module Language
3
+ module Models
4
+ class Modifier < Array
5
+
6
+ DEFAULT_MODE = [:default, :none]
7
+
8
+ def initialize(value = [])
9
+ super(into_a(value))
10
+ end
11
+
12
+ def new
13
+ self.class.new(self.resolve_mode.to_a)
14
+ end
15
+
16
+ # MODE MODIFIERS
17
+ # resolve mode
18
+ def mode
19
+ resolve_mode
20
+ end
21
+
22
+ def <(value)
23
+ self.clear << value
24
+ end
25
+
26
+ def <<(value)
27
+ self.concat(into_a(value))
28
+ end
29
+
30
+ def flush_mode
31
+ self < self.mode
32
+ end
33
+
34
+ def default(value)
35
+ #self.unshift(value)
36
+ reset
37
+ end
38
+
39
+ def reset
40
+ self < DEFAULT_MODE.first
41
+ end
42
+
43
+ def default?
44
+ (mode - DEFAULT_MODE).length == 0
45
+ end
46
+
47
+ protected
48
+
49
+ def resolve_mode
50
+ self.to_a
51
+ end
52
+
53
+ def resolve (ms, flags)
54
+ # later modifiers have precedence
55
+ flag = (ms & flags).last
56
+ ms = (ms - flags).push(flag) unless !flag
57
+ return ms
58
+ end
59
+
60
+ def into_a(value)
61
+ value = [].push(value) unless value.is_a?(Array)
62
+ value
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,114 @@
1
+ module Eco
2
+ module Language
3
+ module Models
4
+ class Wrap
5
+
6
+ attr_reader :setters, :getters
7
+ attr_reader :object
8
+
9
+ def initialize(object = nil, setters: [], getters: [])
10
+ @object = object
11
+ self.setters = setters
12
+ self.getters = getters
13
+ end
14
+
15
+ def getters=(value)
16
+ @getters = into_a(value)
17
+ @wgetters = wrap_object(@object, @getters)
18
+ end
19
+
20
+ def setters=(value)
21
+ @setters = into_a(value)
22
+ @wsetters = wrap_object(@object, @setters)
23
+ end
24
+
25
+ def object=(value)
26
+ @object = value
27
+ @wgetters = wrap_object(@object, @getters)
28
+ @wsetters = wrap_object(@object, @setters)
29
+ end
30
+
31
+ def <<(value = [])
32
+ value = into_a(value)
33
+ @wsetters.each_with_index.map do |set, i|
34
+ args = into_a(value[i])
35
+ set.call(*args)
36
+ end
37
+ end
38
+
39
+ def row(value = (miss = true; []))
40
+ value = into_a(value)
41
+ @wgetters.each_with_index.map do |get, i|
42
+ args = into_a(value[i])
43
+ get.call(*args) unless miss
44
+ get.call
45
+ end
46
+ end
47
+
48
+ def wrap_object(object = nil, ops = [])
49
+ object = object || @object
50
+ return [] unless !!object
51
+ into_a(ops).map do |op|
52
+ cop = required_parameters?(op) ? curry(op) : op
53
+ ->(*args) {
54
+ args.unshift(object);
55
+ #puts "calling wop: #{cop}, with args: #{args}, object: #{object}"
56
+ cop[*args] #rescue nil
57
+ }
58
+ end
59
+ end
60
+
61
+ module Test
62
+ class A
63
+ attr_accessor :a, :e
64
+ def initialize(val)
65
+ self.a = val
66
+ @e = 2
67
+ end
68
+ def exp
69
+ @a**@e
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.test()
75
+ # setters
76
+ soa = ->(o,v) { o.a = v }
77
+ soe = ->(o,v) { o.e = v }
78
+ # getters
79
+ goa = ->(o) { o.a }
80
+ goe = ->(o) { o.e }
81
+ goex = ->(o) { o.exp }
82
+
83
+ # arrays
84
+ setters = [soa, soe ]
85
+ getters = [goa, goe, goex]
86
+
87
+ a = Test::A.new(3)
88
+ b = Test::A.new(5)
89
+ w = self.new(a, setters: setters, getters: getters)
90
+
91
+ puts "before check -> a: #{a.a}; e: #{a.e}; exp: #{a.exp}"
92
+ # launch setters
93
+ w << [4, 2]
94
+ puts "after setters -> a: #{a.a}; e: #{a.e}; exp: #{a.exp}"
95
+ # launch getters
96
+ a, e, exp = w.row
97
+ puts "from getters(a) -> a: #{a}; e: #{e}; exp: #{exp}"
98
+ # switch object
99
+ w.object = b
100
+ # launch getters
101
+ a, e, exp = w.row
102
+ puts "from getters(b) -> a: #{a}; e: #{e}; exp: #{exp}"
103
+ end
104
+
105
+ private
106
+
107
+ def into_a(value)
108
+ value = [].push(value) unless value.is_a?(Array)
109
+ value
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,159 @@
1
+ module Eco
2
+ module Language
3
+
4
+ def to_key(pr)
5
+ pr.is_a?(Hash) ? pr.keys.first : pr
6
+ end
7
+
8
+ def to_value(pr)
9
+ pr.is_a?(Hash) ? pr.values.first : pr
10
+ end
11
+
12
+ def values_at(object, *attrs, **kattrs)
13
+ all_params = attrs + kattrs.map {|k,v| {k => v} }
14
+ return object.values_at(*all_params) if object.is_a?(Hash)
15
+ all_keys = all_params.map { |pr| to_key(pr) }
16
+ arguments = all_params.each_with_index.map { |pr, i| pr.values[0] if pr.is_a?(Hash)}
17
+ #puts "all_keys: #{all_keys}; arguments: #{arguments}"
18
+ values = all_keys.each_with_index.map do |k, i|
19
+ a = k; v = []
20
+ v = arguments[i] if all_params[i].is_a?(Hash) # with params
21
+ attr_method = get_accessor(object, a)[object, a] rescue nil
22
+ #puts "attr: #{a} - method: #{attr_method}"
23
+ next if !attr_method
24
+ args = v.is_a?(Array) ? v : [].push(v)
25
+ attr_method = curry(attr_method) if required_parameters?(attr_method)
26
+ # final call to the method
27
+ #puts "args: #{args}"
28
+ value = attr_method[*args] # rescue nil
29
+ value = nil if value.is_a?(Proc)
30
+ value
31
+ end
32
+ end
33
+
34
+ # adapted from: https://stackoverflow.com/a/16908153/4352306
35
+ def get_accessor(object, attr, accessors: [])
36
+ accessors.push( ->(obj, att) { object.method(att) } )
37
+ #accessors.push( ->(obj, att) { object[att] } )
38
+ accessors.reduce(nil) { |chosen, acc| chosen ? chosen :
39
+ (acc[object, attr] && acc rescue nil) || chosen }
40
+ #accessors.reduce(nil) { |chosen, acc|
41
+ # puts "attr: #{attr} => current: #{acc}"
42
+ # chosen ? chosen : (acc[object, attr] && acc rescue nil) || chosen
43
+ # puts "attr: #{attr} => after check - chosen: #{chosen}"
44
+ # chosen
45
+ # }
46
+ end
47
+
48
+ # enables dot notation to do object nesting access
49
+ def values_at_dot (object, *attrs, curry: false, debug: false, **kattrs)
50
+ all_params = attrs + kattrs.map {|k,v| {k => v} }
51
+ return object.values_at(*all_params) if object.is_a?(Hash)
52
+
53
+ to_s = ->(k) { ((k.to_s rescue k) || key) }
54
+ to_sym = ->(k) { ((k.to_sym rescue k) || key) }
55
+ dotted = ->(k) { to_s[k].include?('.') }
56
+ pdotted = ->(pr) { dotted[to_key(pr)] }
57
+ haccess = ->(dp) { # prepare call
58
+ k = dp
59
+ k, v = [dp.keys.first, dp.values.first] if (hash = dp.is_a?(Hash))
60
+ kf, kr = [ (ss = to_s[k].split('.')).first , ss.slice(1)]
61
+ nxt_cll = hash ? {kr => v} : kr
62
+ {k => {'a' => kf, 'v' => nxt_cll }}
63
+ }
64
+ all_keys = all_params.map { |pr| to_key(pr) }
65
+ hash_params = all_params.reduce({}) { |h,pr| h.merge!(pr) if pr.is_a?(Hash); h }
66
+ dot_params = all_params.select { |pr| pdotted[pr] }
67
+ dot_keys = dot_params.map { |dp| to_key(dp) }
68
+ dot_wrappers = dot_params.reduce({}) { |h,dp| h.merge(haccess[dp]) }
69
+
70
+ if debug
71
+ pp "object : #{object}"
72
+ pp "all_params: #{all_params}"
73
+ pp "dot_keys : #{dot_keys}"
74
+ end
75
+
76
+ values = all_keys.map do |k|
77
+ a = k; v = []
78
+ puts "key: #{k}" if debug
79
+ case true
80
+ when dot_keys.include?(k) # dotted (recursive)
81
+ dot = dot_wrappers[k]
82
+ a, v = dot.values_at('a', 'v')
83
+ when hash_params.key?(k) # no dotted, with params
84
+ v = hash_params[k]
85
+ end
86
+
87
+ attr_method = get_accessor(object, a)[object, a] rescue nil
88
+ next if !attr_method
89
+
90
+ args = v.is_a?(Array) ? v : [].push(v)
91
+ attr_method = curry(attr_method) if required_parameters?(attr_method)
92
+ if dot_keys.include?(k) # recurse (dotted)
93
+ args.push({ curry: curry, debug: debug })
94
+ value = values_at(attr_method.call, *args).first
95
+ else # final call to the method
96
+ value = attr_method[*args] # rescue nil
97
+ puts "value: #{value}" if debug
98
+ end
99
+ value = nil if !curry && value.is_a?(Proc)
100
+ value
101
+ end
102
+
103
+ end
104
+
105
+ module Test
106
+ class A
107
+ attr_reader :ops, :cops
108
+ def initialize(val)
109
+ @ops = val
110
+ end
111
+ def mops
112
+ 'm-' + @ops
113
+ end
114
+ def cops=(value)
115
+ @ops = value
116
+ end
117
+ def rops(val)
118
+ @ops + " there we go with '#{val}' !!"
119
+ end
120
+ def xops(val, xop:)
121
+ @ops + " #{val}!! Xops do the trick with '#{xop}'."
122
+ end
123
+ private
124
+ def gops
125
+ 'g+' + @ops
126
+ end
127
+ end
128
+ class B
129
+ attr_accessor :a, :c
130
+ def initialize(a)
131
+ @a = A.new(a)
132
+ end
133
+ def c=(value)
134
+ @c = value
135
+ end
136
+ def bofs(v1, v2:)
137
+ "#{v1} #{v2} with #{@c}"
138
+ end
139
+ end
140
+ end
141
+
142
+ def self.test_values_at
143
+ h = {'pops' => 'aghr!', pops: 5, 'nops' => 'nope!'}
144
+ a = Test::A.new('hello!')
145
+ b = Test::B.new('bye')
146
+ b.c = 'cheese'
147
+
148
+ pp Handy.values_at(h, 'nops', :pops)
149
+ pp Handy.values_at(a, 'rops', 'mops', rops: 'EVERY', xops: ['Dear', xop: 'SOMETHING BETTER'])
150
+ pp Handy.values_at(a, 'mops', 'gops', rops: 'EVERY THING')
151
+
152
+ pp Handy.values_at_dot(b, 'c', 'bofs' => ['stranberry', v2: 'combines'], 'a.rops': 'DOTTED CALL')
153
+ pp "+" * 60
154
+ pp Handy.values_at_dot(b, 'c', 'bofs' => ['stranberry', v2: 'combines'], debug: true, 'a.rops': 'DOTTED CALL')
155
+ pp "+" * 60
156
+ end
157
+
158
+ end
159
+ end