hollerith 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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: []