hollerith 0.0.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e3353a0f5cf501d7eddbc88d38e1f2e96167a6eb5b862f888a4ca8842e5a301f
4
+ data.tar.gz: 0991414ad5bc0a26004f6418594d4d0e1e659d87ba4a0f34e3c0e7a37063da64
5
+ SHA512:
6
+ metadata.gz: 5f31d9d1b552c06a62663d5cad8b99f21d18ea68aa90f234f9d8a17fef7db1b1b64f9664dde621f584fa42ecba85123c111c49a57b429c37c0b544eddcf21b03
7
+ data.tar.gz: 862b4222af98f74c3895bf832e215ed8e778589b1303a50f20aa14d590c400f5d2d6afa92fa47b5801a7aff516547a4f68de225afa2739600c843eb386ce9589
@@ -0,0 +1,60 @@
1
+ class Hollerith::ArgumentDecoder
2
+ # "result,%%_make_external_request($$_oi,true)"
3
+ # becomes
4
+ # ['result', '%%_make_external_request($$_oi,true)']
5
+ #
6
+ # harder than it looks :)
7
+ def self.decode_argument declaration
8
+ arguments = declaration.match(/\((?>[^)(]+|\g<0>)*\)/)[0][1..-2]
9
+
10
+ arguments_array = []
11
+ each_argument = ''
12
+ unclosed_parens = 0
13
+ unclosed_square_brackets = 0
14
+ unclosed_single_quotes = 0
15
+ unclosed_double_quotes = 0
16
+
17
+ arguments.split('').each do |char|
18
+ everything_is_closed = (
19
+ unclosed_parens == 0 &&
20
+ unclosed_square_brackets == 0 &&
21
+ unclosed_single_quotes == 0 &&
22
+ unclosed_double_quotes == 0
23
+ )
24
+
25
+ if everything_is_closed && char == ','
26
+ arguments_array << each_argument
27
+ each_argument = ''
28
+ elsif char == '('
29
+ unclosed_parens += 1
30
+ each_argument += char
31
+ elsif char == ')'
32
+ unclosed_parens -= 1
33
+ each_argument+= char
34
+ elsif char == "["
35
+ unclosed_square_brackets += 1
36
+ each_argument += char
37
+ elsif char == ']'
38
+ unclosed_square_brackets -= 1
39
+ each_argument+= char
40
+ elsif char == "'" && unclosed_single_quotes == 0
41
+ unclosed_single_quotes += 1
42
+ each_argument += char
43
+ elsif char == "'"
44
+ unclosed_single_quotes -= 1
45
+ each_argument+= char
46
+ elsif char == '"' && unclosed_double_quotes == 0
47
+ unclosed_double_quotes += 1
48
+ each_argument += char
49
+ elsif char == '"'
50
+ unclosed_double_quotes -= 1
51
+ each_argument+= char
52
+ else
53
+ each_argument += char
54
+ end
55
+ end
56
+ arguments_array << each_argument
57
+
58
+ arguments_array
59
+ end
60
+ end
@@ -0,0 +1,231 @@
1
+ class Hollerith::BaseFunctions
2
+
3
+ def valid_functions
4
+ %w(
5
+ for_each
6
+ set
7
+ if
8
+ or
9
+ and
10
+ negate
11
+ count
12
+ make_external_request
13
+ custom_function
14
+ puts
15
+ concat
16
+ blank_array
17
+ array_push
18
+ array_value
19
+ eql
20
+ add
21
+ subtract
22
+ multiply
23
+ divide
24
+ split
25
+ )
26
+ end
27
+
28
+ # Example usage: `%%_for_each($$_planets,%%_custom_function(get_distance_from_sun),each_planet)`
29
+ # In the `get_distance_from_sun` custom function, `each_planet` will be the variable to reference.
30
+ # The for each loop will assign a local variable instead of passing as an argument so your function
31
+ # does not need to define `functions`.
32
+ #
33
+ # @param function_array [Array] Expects three arguments,
34
+ # the array to iterate over, the callback function and the variable to set each element to.
35
+ def __for_each function_array
36
+ object_to_iterate = get_value(function_array.shift)
37
+
38
+ if !object_to_iterate.respond_to?(:each)
39
+ raise ArgumentError.new('Not an iteratable object')
40
+ end
41
+
42
+ object_to_iterate.each do |value|
43
+ local_context = {
44
+ function_array[1] => value
45
+ }
46
+
47
+ get_value(function_array[0], local_context)
48
+ end
49
+ end
50
+
51
+ # Example: `%%_set(my_favourite_planet,'Saturn')`
52
+ # Calling $$_my_favourite_planet will now return "Saturn".
53
+ # @param function_array [Array] Expects two arguments, the variable name and the value.
54
+ def __set function_array
55
+ variable_to_set = function_array.shift
56
+
57
+ @user_context_change.merge!({
58
+ variable_to_set => get_value(function_array.shift)
59
+ })
60
+
61
+ return true
62
+ end
63
+
64
+ def __make_external_request function_array
65
+ return true
66
+ end
67
+
68
+ # Example: `%%_set(my_favourite_planet,'Saturn')`
69
+ # Calling $$_my_favourite_planet will now return "Saturn".
70
+ # @param function_array [Array] Expects one to three arguments, the condition, a callback
71
+ # for when the condition is true, and a callback if the condition is false.
72
+ def __if function_array
73
+ if get_value(function_array.shift)
74
+ if function_array[0]
75
+ get_value(function_array[0])
76
+ else
77
+ return true
78
+ end
79
+ else
80
+ if function_array[1]
81
+ get_value(function_array[1])
82
+ else
83
+ return false
84
+ end
85
+ end
86
+ end
87
+
88
+ def __or function_array
89
+ function_array.each do |each_condition|
90
+ if get_value(each_condition)
91
+ return true
92
+ end
93
+ end
94
+
95
+ return false
96
+ end
97
+
98
+ def __and function_array
99
+ all_true = false
100
+ function_array.each do |each_condition|
101
+ if !get_value(each_condition)
102
+ return false
103
+ else
104
+ all_true = true
105
+ end
106
+ end
107
+
108
+ return all_true
109
+ end
110
+
111
+ def __negate function_array
112
+ value_to_negate = get_value(function_array.shift)
113
+
114
+ if value_to_negate.is_a?(TrueClass)
115
+ negated_value = false
116
+ elsif value_to_negate.is_a?(FalseClass)
117
+ negated_value = true
118
+ else
119
+ begin
120
+ negated_value = value_to_negate * -1
121
+ rescue
122
+ raise ArgumentError.new("Cannot negate this value #{value_to_negate}")
123
+ end
124
+ end
125
+
126
+ return negated_value
127
+ end
128
+
129
+ def __custom_function function_array
130
+ function_name = function_array.shift
131
+
132
+ arguments = @user_defined_functions[function_name]['arguments']
133
+ functions = @user_defined_functions[function_name]['functions']
134
+
135
+ arguments.each do |argument|
136
+ @user_context[argument] = get_value(function_array.shift)
137
+ end
138
+
139
+ functions.each do |function|
140
+ get_value(function)
141
+ end
142
+ end
143
+
144
+ def __puts function_array
145
+ puts(get_value(function_array.shift))
146
+ end
147
+
148
+ def __concat function_array
149
+ result = function_array.map do |each_element|
150
+ get_value(each_element)
151
+ end.join('')
152
+
153
+ result
154
+ end
155
+
156
+ def __blank_array function_array
157
+ return []
158
+ end
159
+
160
+ def __array_value function_array
161
+ context_variable_name = get_variable(function_array.shift)
162
+ if @user_context[context_variable_name].is_a? Array
163
+ @user_context[context_variable_name][get_value(function_array.shift)]
164
+ elsif @main_context[context_variable_name].is_a? Array
165
+ @main_context[context_variable_name][get_value(function_array.shift)]
166
+ else
167
+ raise ArgumentError.new("#{context_variable_name} is not an array.")
168
+ end
169
+ end
170
+
171
+ def __array_push function_array
172
+ context_variable_name = get_variable(function_array.shift)
173
+
174
+ # Only allow modifying the user_context
175
+ if @user_context[context_variable_name].is_a? Array
176
+ @user_context_change.merge!({
177
+ context_variable_name => @user_context[context_variable_name] +
178
+ [get_value(function_array.shift)]
179
+ })
180
+ else
181
+ raise ArgumentError.new("#{context_variable_name} is not an array.")
182
+ end
183
+ end
184
+
185
+ def __eql function_array
186
+ compare_value = get_value(function_array.shift)
187
+
188
+ function_array.each do |each_value_to_compare|
189
+ if get_value(each_value_to_compare) != compare_value
190
+ return false
191
+ end
192
+ end
193
+
194
+ return true
195
+ end
196
+
197
+ def __add function_array
198
+ get_value(function_array.shift) + get_value(function_array.shift)
199
+ end
200
+
201
+ def __subtract function_array
202
+ get_value(function_array.shift) - get_value(function_array.shift)
203
+ end
204
+
205
+ def __multiply function_array
206
+ get_value(function_array.shift) * get_value(function_array.shift)
207
+ end
208
+
209
+ def __divide function_array
210
+ get_value(function_array.shift) / get_value(function_array.shift)
211
+ end
212
+
213
+ def __split function_array
214
+ get_value(function_array.shift).split(function_array.shift)
215
+ end
216
+
217
+ def __count function_array
218
+ get_value(function_array.shift).count
219
+ end
220
+
221
+ private
222
+
223
+ def get_variable variable
224
+ # $$_successful_order_items -> successful_order_items
225
+ if variable.start_with?('$$_')
226
+ return variable[3..-1]
227
+ else
228
+ raise ArgumentError.new('Must assign to an existing variable')
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,62 @@
1
+ require 'hollerith/value_getter'
2
+ require 'hollerith/argument_decoder'
3
+ require 'hollerith/base_functions'
4
+
5
+ class Hollerith::FunctionRunner < Hollerith::BaseFunctions
6
+ attr_reader :user_context_change
7
+
8
+ def initialize declaration, main_context, user_context, user_defined_functions
9
+ @declaration = declaration
10
+ @main_context = main_context
11
+ @user_context = user_context
12
+ @user_context_change = {}
13
+ @user_defined_functions = user_defined_functions
14
+ end
15
+
16
+ def evaluate
17
+ if @declaration.start_with?('%%_')
18
+ function = @declaration[3..-1].split(/,|\(|\)/)
19
+
20
+ # This will create a function array
21
+ # '%%_set(result,%%_make_external_request($$_oi))'
22
+ # becomes
23
+ # ["set", "result", "%%_make_external_request", "$$_oi"]
24
+ if valid_functions.include?(function[0])
25
+ arguments_array = get_arguments_from_declaration
26
+
27
+ if self.respond_to?("__#{function[0]}", arguments_array)
28
+ send("__#{function[0]}", arguments_array)
29
+ else
30
+ raise ArgumentError.new("#{function[0]} is listed as a valid function but not implemented.")
31
+ end
32
+ else
33
+ raise ArgumentError.new("Undefined function #{function[0]}.")
34
+ end
35
+ else
36
+ raise ArgumentError.new("Exepected function, received #{@declaration}.")
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def get_value value_to_get, local_context = {}
43
+ getter = Hollerith::ValueGetter.new(
44
+ @main_context,
45
+ @user_context,
46
+ @user_defined_functions,
47
+ @user_context_change,
48
+ local_context
49
+ )
50
+
51
+ return_value = getter.get(value_to_get)
52
+
53
+ @user_context_change.merge!(getter.user_context_change || {})
54
+
55
+ return_value
56
+ end
57
+
58
+ def get_arguments_from_declaration
59
+ Hollerith::ArgumentDecoder.decode_argument(@declaration)
60
+ end
61
+
62
+ end
@@ -0,0 +1,79 @@
1
+ require "hollerith/function_runner"
2
+
3
+ class Hollerith::ValueGetter
4
+
5
+ attr_reader :user_context_change
6
+
7
+ def initialize user_context, main_context, user_defined_functions, user_context_change, local_context = {}
8
+ # local_context is only used for the getting of this value
9
+ # and does not get bubbled up to other function calls,
10
+ # helpful for each loops in functions.
11
+ @main_context = main_context
12
+ @user_context = user_context.merge(local_context)
13
+ @user_defined_functions = user_defined_functions
14
+ @user_context_change = user_context_change
15
+ end
16
+
17
+ def get value_to_get
18
+ if !value_to_get.is_a? String
19
+ return value_to_get
20
+ elsif value_to_get.downcase == 'true'
21
+ return true
22
+ elsif value_to_get.downcase == 'false'
23
+ return false
24
+ elsif value_to_get.to_i.to_s == value_to_get
25
+ value_to_get.to_i
26
+ elsif value_to_get.start_with?('$$_')
27
+ hash_key = value_to_get[3..-1].strip
28
+ read_hash_value(hash_key)
29
+ elsif value_to_get.start_with?('%%_')
30
+ runner = Hollerith::FunctionRunner.new(
31
+ value_to_get,
32
+ @main_context,
33
+ @user_context.merge(@user_context_change || {}),
34
+ @user_defined_functions
35
+ )
36
+
37
+ result = runner.evaluate
38
+
39
+ @user_context_change.deep_merge!(runner.user_context_change || {})
40
+
41
+ get(result)
42
+ elsif value_to_get.is_a?(String) && (value_to_get.start_with?("'") || value_to_get.start_with?('"')) && (value_to_get.end_with?("'") || value_to_get.end_with?('"'))
43
+ value_to_get[1..-2]
44
+ else
45
+ value_to_get
46
+ end
47
+ end
48
+
49
+ def read_hash_value hash_key
50
+ split_hash_key = hash_key.split('.')
51
+ base_hash_key = split_hash_key.shift
52
+
53
+ if @user_context.has_key?(base_hash_key)
54
+ hash_key_value = @user_context[base_hash_key]
55
+ else
56
+ hash_key_value = @main_context[base_hash_key]
57
+ end
58
+
59
+ split_hash_key.each do |each_hash_key|
60
+ if hash_key_value.is_a?(Hash)
61
+ hash_key_value = hash_key_value[each_hash_key]
62
+ else
63
+ if hash_key_value.respond_to?(:attributes) && hash_key_value.attributes.include?(each_hash_key)
64
+ hash_key_value = hash_key_value.send(each_hash_key)
65
+ elsif hash_key_value.respond_to?(:instance_variables) && hash_key_value.instance_variables.map(&:to_s).include?("@#{each_hash_key}")
66
+ hash_key_value = hash_key_value.send(each_hash_key)
67
+ else
68
+ hash_key_value = nil
69
+ end
70
+ end
71
+ end
72
+
73
+ # TODO: Add dot notation handling here.
74
+ # - Read all rails attributes, hash values or instance variables.
75
+ # FIXME: Variable getting needs to be DRYed up.
76
+
77
+ return hash_key_value
78
+ end
79
+ end
data/lib/hollerith.rb ADDED
@@ -0,0 +1,127 @@
1
+ class Hollerith
2
+
3
+ HOOKS = %w(
4
+ on_place_order
5
+ on_update_order
6
+ )
7
+
8
+ def initialize configuration, main_context = {}
9
+ @configuration = configuration
10
+ @main_context = main_context
11
+ @user_context = @configuration['configuration'] || {}
12
+ @user_defined_functions = @configuration['custom_functions']
13
+ end
14
+
15
+ def trigger_hook hook
16
+ @configuration[hook].each do |each_hook_implementation|
17
+ if hook_should_be_run(each_hook_implementation)
18
+ execute_hook_implementation(each_hook_implementation)
19
+ end
20
+ end
21
+ end
22
+
23
+ def hook_should_be_run(hook_implementation)
24
+ if hook_implementation.has_key?('run_if')
25
+ evaluate_conditional(hook_implementation['run_if'])
26
+ elsif hook_implementation.has_key?('run_unless')
27
+ !evaluate_conditional(hook_implementation['run_unless'])
28
+ else
29
+ true
30
+ end
31
+ end
32
+
33
+ def execute_hook_implementation definition
34
+ if definition['before'] && definition['before']['functions']
35
+ evaluate_functions(definition['before']['functions'])
36
+ end
37
+
38
+ case definition['method']
39
+ when 'none'
40
+ evaluate_functions(definition['functions'])
41
+ when 'conditional'
42
+ # TODO
43
+ raise "Not implemented!"
44
+ when 'for_each'
45
+ before_iteration_functions = definition['for_each']['before_iteration']
46
+ each_iteration_rules = definition['for_each']['each_iteration']
47
+ break_when = definition['for_each']['break_iteration_if']
48
+ next_when = definition['for_each']['next_if']
49
+
50
+ evaluate_functions(before_iteration_functions)
51
+
52
+ get_iterator(definition['loop']) do
53
+ next if evaluate_conditional(next_when)
54
+
55
+ evaluate_functions(each_iteration_rules)
56
+ break if evaluate_conditional(break_when)
57
+ end
58
+ end
59
+
60
+ if definition['finally'] && definition['finally']['functions']
61
+ evaluate_functions(definition['finally']['functions'])
62
+ end
63
+ end
64
+
65
+ def get_iterator iterator_definition
66
+ # `$$_schools as each_school`
67
+ collection_variable, each_value_variable = iterator_definition.split(' as ')
68
+
69
+ get_variable_from_context(collection_variable).each do |each_iteration|
70
+ @user_context[each_value_variable] = each_iteration
71
+ yield
72
+ end
73
+ end
74
+
75
+ def get_variable_from_context variable
76
+ if variable.start_with?('$$_')
77
+ value_to_return = @main_context[variable[3..-1].strip]
78
+
79
+ if value_to_return
80
+ return value_to_return
81
+ else
82
+ raise ArgumentError.new("Variable not found #{value_to_return} in this context")
83
+ end
84
+ else
85
+ raise ArgumentError.new("Invalid variable definition #{variable}")
86
+ end
87
+ end
88
+
89
+ def evaluate_functions functions
90
+ if functions.compact.any?
91
+ functions.compact.each do |function|
92
+ evaluate_function(function)
93
+ end
94
+ end
95
+ end
96
+
97
+ def evaluate_conditional conditional_statement
98
+ return false unless conditional_statement
99
+
100
+ evaluate_function(conditional_statement)
101
+ end
102
+
103
+ def evaluate_function function_definition
104
+ return unless function_definition
105
+
106
+ runner = Hollerith::FunctionRunner.new(
107
+ function_definition,
108
+ @main_context,
109
+ @user_context,
110
+ @user_defined_functions
111
+ )
112
+
113
+ results = runner.evaluate
114
+
115
+ if runner.user_context_change
116
+ @user_context.deep_merge!(runner.user_context_change)
117
+ end
118
+
119
+ return results
120
+ end
121
+
122
+ end
123
+
124
+ require 'hollerith/function_runner'
125
+ require 'hollerith/value_getter'
126
+ require 'hollerith/argument_decoder'
127
+ require 'hollerith/base_functions'
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hollerith
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Noah Kochanowicz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deep_merge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.1
27
+ description: Trigger code through a series of hooks and run code in loops and through
28
+ conditions.
29
+ email: me@noahkoch.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/hollerith.rb
35
+ - lib/hollerith/argument_decoder.rb
36
+ - lib/hollerith/base_functions.rb
37
+ - lib/hollerith/function_runner.rb
38
+ - lib/hollerith/value_getter.rb
39
+ homepage: https://rubygems.org/gems/hollerith
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.0.4
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Create and run functions in JSON.
62
+ test_files: []