tree_branch 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +8 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +89 -0
- data/Guardfile +16 -0
- data/LICENSE +7 -0
- data/README.md +431 -0
- data/bin/console +15 -0
- data/lib/tree_branch/comparator.rb +25 -0
- data/lib/tree_branch/node.rb +42 -0
- data/lib/tree_branch/processor.rb +47 -0
- data/lib/tree_branch/simple_node.rb +21 -0
- data/lib/tree_branch/tree_branch.rb +38 -0
- data/lib/tree_branch/version.rb +12 -0
- data/lib/tree_branch.rb +10 -0
- data/spec/fixtures/born_after1915.yml +13 -0
- data/spec/fixtures/born_after1915_with_m_or_s.yml +9 -0
- data/spec/fixtures/node.yml +18 -0
- data/spec/fixtures/node_with_injected.yml +22 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/tree_branch/comparator_spec.rb +47 -0
- data/spec/tree_branch/node_spec.rb +24 -0
- data/spec/tree_branch/tree_branch_spec.rb +394 -0
- data/tree_branch.gemspec +29 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 15671327d19ea465e079da408e3e4a35ff0a26d03b8ec33251e7c96a49d9b87d
|
4
|
+
data.tar.gz: b40855cf7a130ed233b9e4da84224d81dd75d54de234cabd628d806bda81c9b2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 30b2df3207d1cea66da5197b9582744b8746b4e5fed1f188975f281e8895657bcdf6a96b07bbff9e86b60ff53d4eec492b3d6d4411af77e3258821e2b4d03b72
|
7
|
+
data.tar.gz: d6bf7412d4db03c36f2ce842f63705b70af25051d337e70672c23fc95ad442762285a57c956ad839781ddd364d043ea64ace974836381be454785218d138106c
|
data/.editorconfig
ADDED
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.0
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tree_branch (1.0.0)
|
5
|
+
acts_as_hashable (~> 1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
acts_as_hashable (1.0.3)
|
11
|
+
ast (2.4.0)
|
12
|
+
coderay (1.1.2)
|
13
|
+
diff-lcs (1.3)
|
14
|
+
ffi (1.9.25)
|
15
|
+
formatador (0.2.5)
|
16
|
+
guard (2.15.0)
|
17
|
+
formatador (>= 0.2.4)
|
18
|
+
listen (>= 2.7, < 4.0)
|
19
|
+
lumberjack (>= 1.0.12, < 2.0)
|
20
|
+
nenv (~> 0.1)
|
21
|
+
notiffany (~> 0.0)
|
22
|
+
pry (>= 0.9.12)
|
23
|
+
shellany (~> 0.0)
|
24
|
+
thor (>= 0.18.1)
|
25
|
+
guard-compat (1.2.1)
|
26
|
+
guard-rspec (4.7.3)
|
27
|
+
guard (~> 2.1)
|
28
|
+
guard-compat (~> 1.1)
|
29
|
+
rspec (>= 2.99.0, < 4.0)
|
30
|
+
jaro_winkler (1.5.1)
|
31
|
+
listen (3.1.5)
|
32
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
33
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
34
|
+
ruby_dep (~> 1.2)
|
35
|
+
lumberjack (1.0.13)
|
36
|
+
method_source (0.9.2)
|
37
|
+
nenv (0.3.0)
|
38
|
+
notiffany (0.1.1)
|
39
|
+
nenv (~> 0.1)
|
40
|
+
shellany (~> 0.0)
|
41
|
+
parallel (1.12.1)
|
42
|
+
parser (2.5.3.0)
|
43
|
+
ast (~> 2.4.0)
|
44
|
+
powerpack (0.1.2)
|
45
|
+
pry (0.12.2)
|
46
|
+
coderay (~> 1.1.0)
|
47
|
+
method_source (~> 0.9.0)
|
48
|
+
rainbow (3.0.0)
|
49
|
+
rb-fsevent (0.10.3)
|
50
|
+
rb-inotify (0.9.10)
|
51
|
+
ffi (>= 0.5.0, < 2)
|
52
|
+
rspec (3.8.0)
|
53
|
+
rspec-core (~> 3.8.0)
|
54
|
+
rspec-expectations (~> 3.8.0)
|
55
|
+
rspec-mocks (~> 3.8.0)
|
56
|
+
rspec-core (3.8.0)
|
57
|
+
rspec-support (~> 3.8.0)
|
58
|
+
rspec-expectations (3.8.2)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.8.0)
|
61
|
+
rspec-mocks (3.8.0)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.8.0)
|
64
|
+
rspec-support (3.8.0)
|
65
|
+
rubocop (0.59.2)
|
66
|
+
jaro_winkler (~> 1.5.1)
|
67
|
+
parallel (~> 1.10)
|
68
|
+
parser (>= 2.5, != 2.5.1.1)
|
69
|
+
powerpack (~> 0.1)
|
70
|
+
rainbow (>= 2.2.2, < 4.0)
|
71
|
+
ruby-progressbar (~> 1.7)
|
72
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
73
|
+
ruby-progressbar (1.10.0)
|
74
|
+
ruby_dep (1.5.0)
|
75
|
+
shellany (0.0.1)
|
76
|
+
thor (0.20.3)
|
77
|
+
unicode-display_width (1.4.0)
|
78
|
+
|
79
|
+
PLATFORMS
|
80
|
+
ruby
|
81
|
+
|
82
|
+
DEPENDENCIES
|
83
|
+
guard-rspec (~> 4.7)
|
84
|
+
rspec (~> 3.8)
|
85
|
+
rubocop (~> 0.59)
|
86
|
+
tree_branch!
|
87
|
+
|
88
|
+
BUNDLED WITH
|
89
|
+
1.17.2
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
4
|
+
require 'guard/rspec/dsl'
|
5
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
ruby = dsl.ruby
|
15
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2018 Blue Marble Payroll, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,431 @@
|
|
1
|
+
# TreeBranch
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/bluemarblepayroll/tree_branch.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/tree_branch)
|
4
|
+
|
5
|
+
This library allows you to traverse an entire tree structure, compare all nodes, and choose a tree structure to return. The basic input is defined as:
|
6
|
+
|
7
|
+
1. Initial Tree structure root node (required)
|
8
|
+
2. Comparison classes or functions (optional)
|
9
|
+
3. Block to convert each matching node (optional)
|
10
|
+
|
11
|
+
And the output is defined as:
|
12
|
+
|
13
|
+
1. Compared and/or converted tree structure (root node)
|
14
|
+
|
15
|
+
The specific use-case this was designed for was a dynamic web application menu. In this specific example, we wanted either a static file or a database to store and define all possible menus. Then we wanted to input a request's lifecycle context (user, url, parameters, authorization, etc.) and return the menu that matched the current spot in the application.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
To install through Rubygems:
|
20
|
+
|
21
|
+
````
|
22
|
+
gem install install tree_branch
|
23
|
+
````
|
24
|
+
|
25
|
+
You can also add this to your Gemfile:
|
26
|
+
|
27
|
+
````
|
28
|
+
bundle add tree_branch
|
29
|
+
````
|
30
|
+
|
31
|
+
## Examples
|
32
|
+
|
33
|
+
### Word Processor Application Menu Example
|
34
|
+
|
35
|
+
Take the following application menu structure:
|
36
|
+
|
37
|
+
````ruby
|
38
|
+
menu = {
|
39
|
+
data: { name: 'Menu' },
|
40
|
+
children: [
|
41
|
+
{
|
42
|
+
data: { name: 'File' },
|
43
|
+
children: [
|
44
|
+
{ data: { name: 'Open', command: :open } },
|
45
|
+
{ data: { name: 'Save', command: :save, right: :write } },
|
46
|
+
{ data: { name: 'Close', command: :close } },
|
47
|
+
{
|
48
|
+
data: { name: 'Print', command: :print },
|
49
|
+
children: [
|
50
|
+
{ data: { name: 'Print' } },
|
51
|
+
{ data: { name: 'Print Preview' } },
|
52
|
+
]
|
53
|
+
},
|
54
|
+
]
|
55
|
+
},
|
56
|
+
{
|
57
|
+
data: { name: 'Edit' },
|
58
|
+
children: [
|
59
|
+
{ data: { name: 'Cut', command: :cut } },
|
60
|
+
{ data: { name: 'Copy', command: :copy } },
|
61
|
+
{ data: { name: 'Paste', command: :paste } }
|
62
|
+
]
|
63
|
+
}
|
64
|
+
]
|
65
|
+
}
|
66
|
+
````
|
67
|
+
|
68
|
+
There are three application states:
|
69
|
+
|
70
|
+
1. No file open (user has no file currently editing): NONE
|
71
|
+
2. Passive file open: PASSIVE
|
72
|
+
3. Active file open: ACTIVE
|
73
|
+
|
74
|
+
The user is allowed only access to specific menu items depending on their state:
|
75
|
+
|
76
|
+
1. NONE: open
|
77
|
+
2. PASSIVE: open, save, close, print
|
78
|
+
3. ACTIVE: open, save, close, print, cut, copy, paste
|
79
|
+
|
80
|
+
We can implement this as a comparator class:
|
81
|
+
|
82
|
+
````ruby
|
83
|
+
class StateComparator < ::TreeBranch::Comparator
|
84
|
+
STATE_OPS = {
|
85
|
+
none: %i[open],
|
86
|
+
passive: %i[open save close print],
|
87
|
+
active: %i[open save close print cut copy paste]
|
88
|
+
}
|
89
|
+
|
90
|
+
def valid?
|
91
|
+
data.command.nil? || Array(STATE_OPS[context.state]).include?(data.command)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
````
|
95
|
+
|
96
|
+
Finally, we can process this for all three states:
|
97
|
+
|
98
|
+
````ruby
|
99
|
+
no_file_menu =
|
100
|
+
::TreeBranch.process(
|
101
|
+
node: menu,
|
102
|
+
comparators: StateComparator,
|
103
|
+
context: { state: :none }
|
104
|
+
)
|
105
|
+
|
106
|
+
passive_file_menu =
|
107
|
+
::TreeBranch.process(
|
108
|
+
node: menu,
|
109
|
+
comparators: StateComparator,
|
110
|
+
context: { state: :passive }
|
111
|
+
)
|
112
|
+
|
113
|
+
active_file_menu =
|
114
|
+
::TreeBranch.process(
|
115
|
+
node: menu,
|
116
|
+
comparators: StateComparator,
|
117
|
+
context: { state: :active }
|
118
|
+
)
|
119
|
+
````
|
120
|
+
|
121
|
+
We would get the following structure back (in the form of a root Node object but expressed as a hash below):
|
122
|
+
|
123
|
+
##### No File Menu Result
|
124
|
+
|
125
|
+
````ruby
|
126
|
+
{
|
127
|
+
data: { name: 'Menu' },
|
128
|
+
children: [
|
129
|
+
{
|
130
|
+
data: { name: 'File' },
|
131
|
+
children: [
|
132
|
+
{ data: { name: 'Open', command: :open } }
|
133
|
+
]
|
134
|
+
},
|
135
|
+
{
|
136
|
+
data: { name: 'Edit' }
|
137
|
+
}
|
138
|
+
]
|
139
|
+
}
|
140
|
+
````
|
141
|
+
|
142
|
+
##### Passive File Menu Result
|
143
|
+
|
144
|
+
````ruby
|
145
|
+
{
|
146
|
+
data: { name: 'Menu' },
|
147
|
+
children: [
|
148
|
+
{
|
149
|
+
data: { name: 'File' },
|
150
|
+
children: [
|
151
|
+
{ data: { name: 'Open', command: :open } },
|
152
|
+
{ data: { name: 'Save', command: :save, right: :write } },
|
153
|
+
{ data: { name: 'Close', command: :close } },
|
154
|
+
{
|
155
|
+
data: { name: 'Print', command: :print },
|
156
|
+
children: [
|
157
|
+
{ data: { name: 'Print' } },
|
158
|
+
{ data: { name: 'Print Preview' } }
|
159
|
+
]
|
160
|
+
}
|
161
|
+
]
|
162
|
+
},
|
163
|
+
{
|
164
|
+
data: { name: 'Edit' }
|
165
|
+
}
|
166
|
+
]
|
167
|
+
}
|
168
|
+
````
|
169
|
+
|
170
|
+
##### Active File Menu Result
|
171
|
+
|
172
|
+
````ruby
|
173
|
+
{
|
174
|
+
data: { name: 'Menu' },
|
175
|
+
children: [
|
176
|
+
{
|
177
|
+
data: { name: 'File' },
|
178
|
+
children: [
|
179
|
+
{ data: { name: 'Open', command: :open } },
|
180
|
+
{ data: { name: 'Save', command: :save, right: :write } },
|
181
|
+
{ data: { name: 'Close', command: :close } },
|
182
|
+
{
|
183
|
+
data: { name: 'Print', command: :print },
|
184
|
+
children: [
|
185
|
+
{ data: { name: 'Print' } },
|
186
|
+
{ data: { name: 'Print Preview' } },
|
187
|
+
]
|
188
|
+
},
|
189
|
+
]
|
190
|
+
},
|
191
|
+
{
|
192
|
+
data: { name: 'Edit' },
|
193
|
+
children: [
|
194
|
+
{ data: { name: 'Cut', command: :cut } },
|
195
|
+
{ data: { name: 'Copy', command: :copy } },
|
196
|
+
{ data: { name: 'Paste', command: :paste } }
|
197
|
+
]
|
198
|
+
}
|
199
|
+
]
|
200
|
+
}
|
201
|
+
````
|
202
|
+
|
203
|
+
### Stacking Comparators
|
204
|
+
|
205
|
+
You can also choose to input multiple comparators (technically 0 to N). For example, let's stack authorization into our application menu example using this comparator:
|
206
|
+
|
207
|
+
````ruby
|
208
|
+
class AuthorizationComparator < ::TreeBranch::Comparator
|
209
|
+
def valid?
|
210
|
+
data.right.nil? || Array(context.rights).include?(data.right)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
````
|
214
|
+
|
215
|
+
Now, we can pass in our current user's rights and use them when appropriate:
|
216
|
+
|
217
|
+
````ruby
|
218
|
+
passive_read_only_menu =
|
219
|
+
::TreeBranch.process(
|
220
|
+
node: menu,
|
221
|
+
comparators: [StateComparator, AuthorizationComparator],
|
222
|
+
context: { state: :passive }
|
223
|
+
)
|
224
|
+
|
225
|
+
passive_read_write_menu =
|
226
|
+
::TreeBranch.process(
|
227
|
+
node: menu,
|
228
|
+
comparators: [StateComparator, AuthorizationComparator],
|
229
|
+
context: { state: :passive, rights: :write }
|
230
|
+
)
|
231
|
+
````
|
232
|
+
|
233
|
+
##### Read-Only User Passively Editing Result
|
234
|
+
|
235
|
+
````ruby
|
236
|
+
{
|
237
|
+
data: { name: 'Menu' },
|
238
|
+
children: [
|
239
|
+
{
|
240
|
+
data: { name: 'File' },
|
241
|
+
children: [
|
242
|
+
{ data: { name: 'Open', command: :open } },
|
243
|
+
{ data: { name: 'Close', command: :close } },
|
244
|
+
{
|
245
|
+
data: { name: 'Print', command: :print },
|
246
|
+
children: [
|
247
|
+
{ data: { name: 'Print' } },
|
248
|
+
{ data: { name: 'Print Preview' } }
|
249
|
+
]
|
250
|
+
}
|
251
|
+
]
|
252
|
+
},
|
253
|
+
{
|
254
|
+
data: { name: 'Edit' }
|
255
|
+
}
|
256
|
+
]
|
257
|
+
}
|
258
|
+
````
|
259
|
+
|
260
|
+
##### Read/Write User Passively Editing Result
|
261
|
+
|
262
|
+
````ruby
|
263
|
+
{
|
264
|
+
data: { name: 'Menu' },
|
265
|
+
children: [
|
266
|
+
{
|
267
|
+
data: { name: 'File' },
|
268
|
+
children: [
|
269
|
+
{ data: { name: 'Open', command: :open } },
|
270
|
+
{ data: { name: 'Save', command: :save, right: :write } },
|
271
|
+
{ data: { name: 'Close', command: :close } },
|
272
|
+
{
|
273
|
+
data: { name: 'Print', command: :print },
|
274
|
+
children: [
|
275
|
+
{ data: { name: 'Print' } },
|
276
|
+
{ data: { name: 'Print Preview' } }
|
277
|
+
]
|
278
|
+
}
|
279
|
+
]
|
280
|
+
},
|
281
|
+
{
|
282
|
+
data: { name: 'Edit' }
|
283
|
+
}
|
284
|
+
]
|
285
|
+
}
|
286
|
+
````
|
287
|
+
|
288
|
+
Notice now our read-only menu is missing the 'save' item.
|
289
|
+
|
290
|
+
### Comparator Creation
|
291
|
+
|
292
|
+
There are two ways to create comparators:
|
293
|
+
|
294
|
+
1. Subclass ::TreeBranch::Comparator and implement the ```valid?``` method to return true/false
|
295
|
+
2. Create lambda/proc that accepts two arguments: data and context and returns true/false
|
296
|
+
|
297
|
+
Option one is shown in the above example, while option two can be illustrated as:
|
298
|
+
|
299
|
+
````ruby
|
300
|
+
auth_comparator = lambda do |data, context|
|
301
|
+
data.right.nil? || Array(context.rights).include?(data.right)
|
302
|
+
end
|
303
|
+
|
304
|
+
passive_read_only_menu =
|
305
|
+
::TreeBranch.process(
|
306
|
+
node: menu,
|
307
|
+
comparators: [StateComparator, auth_comparator],
|
308
|
+
context: { state: :passive }
|
309
|
+
)
|
310
|
+
````
|
311
|
+
|
312
|
+
### Node Post-Processing / Conversion
|
313
|
+
|
314
|
+
After a node has been compared and is deemed to be valid, it will either return one of two things:
|
315
|
+
|
316
|
+
1. ::TreeBranch::Node instance
|
317
|
+
2. The return of the block passed into the process method produced *(Note: If nil it will be ignored as if it was invalid.)*
|
318
|
+
|
319
|
+
In our above example we did not pass in a block so they would all return Node instances. The passed in block is your chance to return instances of another class, or even do some other post-processing routines. For example, lets return an instance of a new type: MenuItem as shown below:
|
320
|
+
|
321
|
+
````ruby
|
322
|
+
class MenuItem
|
323
|
+
acts_as_hashable
|
324
|
+
|
325
|
+
attr_reader :menu_items, :name
|
326
|
+
|
327
|
+
def initialize(name: '', menu_items: [])
|
328
|
+
@name = name
|
329
|
+
@menu_items = self.class.array(menu_items)
|
330
|
+
end
|
331
|
+
|
332
|
+
def eql?(other)
|
333
|
+
name == other.name && menu_items == other.menu_items
|
334
|
+
end
|
335
|
+
|
336
|
+
def ==(other)
|
337
|
+
eql?(other)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
````
|
341
|
+
|
342
|
+
We can now convert this in the block:
|
343
|
+
|
344
|
+
````ruby
|
345
|
+
passive_read_write_menu =
|
346
|
+
::TreeBranch.process(
|
347
|
+
node: menu,
|
348
|
+
comparators: [StateComparator, auth_comparator],
|
349
|
+
context: { state: :passive, rights: :write }
|
350
|
+
) { |data, children, context| MenuItem.new(data.name, children) }
|
351
|
+
````
|
352
|
+
|
353
|
+
Our resulting data set (visualized as a hash):
|
354
|
+
|
355
|
+
````ruby
|
356
|
+
{
|
357
|
+
name: 'Menu',
|
358
|
+
menu_items: [
|
359
|
+
{
|
360
|
+
name: 'File',
|
361
|
+
menu_items: [
|
362
|
+
{ name: 'Open' },
|
363
|
+
{ name: 'Save' },
|
364
|
+
{ name: 'Close' },
|
365
|
+
{
|
366
|
+
name: 'Print',
|
367
|
+
menu_items: [
|
368
|
+
{ name: 'Print' },
|
369
|
+
{ name: 'Print Preview' }
|
370
|
+
]
|
371
|
+
}
|
372
|
+
]
|
373
|
+
},
|
374
|
+
{
|
375
|
+
name: 'Edit'
|
376
|
+
}
|
377
|
+
]
|
378
|
+
}
|
379
|
+
````
|
380
|
+
|
381
|
+
## Contributing
|
382
|
+
|
383
|
+
### Development Environment Configuration
|
384
|
+
|
385
|
+
Basic steps to take to get this repository compiling:
|
386
|
+
|
387
|
+
1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (check tree_branch.gemspec for versions supported)
|
388
|
+
2. Install bundler (gem install bundler)
|
389
|
+
3. Clone the repository (git clone git@github.com:bluemarblepayroll/tree_branch.git)
|
390
|
+
4. Navigate to the root folder (cd tree_branch)
|
391
|
+
5. Install dependencies (bundle)
|
392
|
+
|
393
|
+
### Running Tests
|
394
|
+
|
395
|
+
To execute the test suite run:
|
396
|
+
|
397
|
+
````
|
398
|
+
bundle exec rspec spec --format documentation
|
399
|
+
````
|
400
|
+
|
401
|
+
Alternatively, you can have Guard watch for changes:
|
402
|
+
|
403
|
+
````
|
404
|
+
bundle exec guard
|
405
|
+
````
|
406
|
+
|
407
|
+
Also, do not forget to run Rubocop:
|
408
|
+
|
409
|
+
````
|
410
|
+
bundle exec rubocop
|
411
|
+
````
|
412
|
+
|
413
|
+
### Publishing
|
414
|
+
|
415
|
+
Note: ensure you have proper authorization before trying to publish new versions.
|
416
|
+
|
417
|
+
After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
|
418
|
+
|
419
|
+
1. Merge Pull Request into master
|
420
|
+
2. Update [lib/tree_branch/version.rb](https://github.com/bluemarblepayroll/tree_branch/blob/master/lib/tree_branch/version.rb) [version number](https://semver.org/)
|
421
|
+
3. Bundle
|
422
|
+
4. Update CHANGELOG.md
|
423
|
+
5. Commit & Push master to remote and ensure CI builds master successfully
|
424
|
+
6. Build the project locally: `gem build tree_branch`
|
425
|
+
7. Publish package to NPM: `gem push tree_branch-X.gem` where X is the version to push
|
426
|
+
8. Tag master with new version: `git tag <version>`
|
427
|
+
9. Push tags remotely: `git push origin --tags`
|
428
|
+
|
429
|
+
## License
|
430
|
+
|
431
|
+
This project is MIT Licensed.
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'tree_branch'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-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 TreeBranch
|
11
|
+
# This is the base class for all plug in comparators. Derive subclasses from this class
|
12
|
+
# and declare them when calling ::TreeBranch::Node#process or ::TreeBranch#process.
|
13
|
+
class Comparator
|
14
|
+
attr_reader :data, :context
|
15
|
+
|
16
|
+
def initialize(data: {}, context: {})
|
17
|
+
@data = data || {}
|
18
|
+
@context = context || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-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 TreeBranch
|
11
|
+
# Main class the outlines the basic operations and structure of a node in the tree.
|
12
|
+
class Node
|
13
|
+
attr_reader :data, :children
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@data = data
|
17
|
+
@children = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def add(*children_to_add)
|
21
|
+
children_to_add.flatten.each do |child|
|
22
|
+
raise ArgumentError, "Improper class: #{child.class.name}" unless child.is_a?(self.class)
|
23
|
+
|
24
|
+
@children << child
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def eql?(other)
|
31
|
+
data == other.data && children == other.children
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
eql?(other)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
"[#{self.class.name}] Data: #{data}, Child Count: #{children.length}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|