flor 0.16.1 → 0.16.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/CREDITS.md +1 -0
- data/Makefile +1 -1
- data/README.md +82 -6
- data/lib/flor.rb +1 -1
- data/lib/flor/conf.rb +19 -6
- data/lib/flor/core/executor.rb +45 -16
- data/lib/flor/core/node.rb +4 -4
- data/lib/flor/core/procedure.rb +40 -0
- data/lib/flor/djan.rb +5 -2
- data/lib/flor/flor.rb +92 -7
- data/lib/flor/id.rb +19 -0
- data/lib/flor/migrations/0001_tables.rb +6 -6
- data/lib/flor/migrations/0005_pointer_content.rb +20 -0
- data/lib/flor/pcore/_apply.rb +103 -57
- data/lib/flor/pcore/_att.rb +15 -1
- data/lib/flor/pcore/_ref.rb +2 -1
- data/lib/flor/pcore/arith.rb +46 -9
- data/lib/flor/pcore/break.rb +1 -1
- data/lib/flor/pcore/case.rb +41 -0
- data/lib/flor/pcore/collect.rb +1 -1
- data/lib/flor/pcore/cursor.rb +1 -1
- data/lib/flor/pcore/define.rb +32 -6
- data/lib/flor/pcore/iterator.rb +12 -0
- data/lib/flor/pcore/on_cancel.rb +1 -1
- data/lib/flor/pcore/set.rb +14 -4
- data/lib/flor/punit/{ccollect.rb → c_collect.rb} +2 -2
- data/lib/flor/punit/c_each.rb +11 -0
- data/lib/flor/punit/c_for_each.rb +41 -0
- data/lib/flor/punit/c_iterator.rb +160 -0
- data/lib/flor/punit/c_map.rb +43 -0
- data/lib/flor/punit/concurrence.rb +43 -200
- data/lib/flor/punit/graft.rb +3 -2
- data/lib/flor/punit/m_ram.rb +281 -0
- data/lib/flor/unit.rb +1 -0
- data/lib/flor/unit/caller.rb +6 -1
- data/lib/flor/unit/executor.rb +17 -4
- data/lib/flor/unit/ganger.rb +12 -1
- data/lib/flor/unit/hloader.rb +251 -0
- data/lib/flor/unit/hook.rb +74 -15
- data/lib/flor/unit/hooker.rb +9 -12
- data/lib/flor/unit/loader.rb +41 -17
- data/lib/flor/unit/models.rb +54 -18
- data/lib/flor/unit/models/execution.rb +15 -4
- data/lib/flor/unit/models/pointer.rb +11 -0
- data/lib/flor/unit/scheduler.rb +126 -30
- data/lib/flor/unit/spooler.rb +5 -3
- data/lib/flor/unit/storage.rb +40 -13
- data/lib/flor/unit/waiter.rb +165 -26
- data/lib/flor/unit/wlist.rb +98 -5
- metadata +10 -4
- data/lib/flor/punit/cmap.rb +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c913a70a19f7d01588c3cd927cb529d64538177
|
4
|
+
data.tar.gz: 25d6262fe8024c0159a172f392ef65b203f72a1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d9834e300f1d5158f42027e51966efcfc500edeedf3c7495a42985b77d24dbcd87aafdd1061776c04cc0713181ced907d535cc5c59032b13b355211fab2dad8
|
7
|
+
data.tar.gz: ecdd517230e0f0602fb8b617b9d57ce94938864a30ecd7df827dab486de1f85de55ef296f8d0964fc878ccd764685230d2d73bea0e3c9aa53e144ccf52df8dee
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## flor 0.16.2 not yet released
|
6
|
+
|
7
|
+
- Allow for `[ 'he' 'll' 'o' ] | + join: '.'` (yields "he.ll.o")
|
8
|
+
- Allow for `[ 1 2 3 ] | + _` (yields `6`)
|
9
|
+
- Make "child_on_error:"/"children_on_error:" a common attribute
|
10
|
+
- Ensure "on_cancel" sets only one handler
|
11
|
+
|
12
|
+
|
5
13
|
## flor 0.16.1 released 2019-02-05
|
6
14
|
|
7
15
|
- Depend on Sequel 5 (Sequel 4 and 5 seem OK)
|
data/CREDITS.md
CHANGED
data/Makefile
CHANGED
@@ -71,7 +71,7 @@ mk:
|
|
71
71
|
$(RUBY) -Ilib -e "require 'flor/tools/env'; Flor::Tools::Env.make('tmp', '$(FLOR_ENV)', gitkeep: true)"
|
72
72
|
|
73
73
|
doc:
|
74
|
-
$(RUBY) -Imak -r '
|
74
|
+
$(RUBY) -Imak -r 'doc_procedures' -e "make_doc_procedures()"
|
75
75
|
doct:
|
76
76
|
@$(RUBY) mak/ptree.rb
|
77
77
|
|
data/README.md
CHANGED
@@ -10,10 +10,11 @@ Flor is a "Ruby workflow engine", if that makes any sense.
|
|
10
10
|
* [floraison mailing list](https://groups.google.com/forum/#!forum/floraison)
|
11
11
|
* [twitter.com/@flor_workflow](https://twitter.com/flor_workflow)
|
12
12
|
|
13
|
-
|
13
|
+
|
14
|
+
## design
|
14
15
|
|
15
16
|
* Strives to propose a scheming interpreter for long running executions
|
16
|
-
* Is written in Ruby a rather straightforward language with at least two
|
17
|
+
* Is written in Ruby, a rather straightforward language with at least two
|
17
18
|
wonderful implementations (MRI and JRuby, which is enterprise-friendly)
|
18
19
|
* Stores everything as JSON (if it breaks it's still readable)
|
19
20
|
* Stores in any database supported by [Sequel](http://sequel.jeremyevans.net/)
|
@@ -22,14 +23,89 @@ Flor is a "Ruby workflow engine", if that makes any sense.
|
|
22
23
|
* All in all should be easy to maintain (engine itself and executions running
|
23
24
|
on top of it)
|
24
25
|
|
25
|
-
## Quickstart
|
26
26
|
|
27
|
-
|
27
|
+
## quickstart
|
28
|
+
|
29
|
+
This quickstart sets up a flor unit tied to a SQLite database, resets the databse, binds two taskers and then launches a flow execution involving the two taskers. Finally, it prints out the resulting workitem as the execution has just terminated.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'flor/unit'
|
33
|
+
|
34
|
+
#ENV['FLOR_DEBUG'] = 'dbg,sto,stdout' # full sql + flor debug output
|
35
|
+
#ENV['FLOR_DEBUG'] = 'dbg,stdout' # flor debug output
|
36
|
+
# uncomment to see the flor activity
|
37
|
+
|
38
|
+
sto_uri = 'sqlite://flor_qs.db'
|
39
|
+
sto_uri = 'jdbc:sqlite://flor_qs.db' if RUBY_PLATFORM.match(/java/)
|
40
|
+
|
41
|
+
flor = Flor::Unit.new(loader: Flor::HashLoader, sto_uri: sto_uri)
|
42
|
+
# instantiate flor unit
|
43
|
+
|
44
|
+
flor.storage.delete_tables
|
45
|
+
flor.storage.migrate
|
46
|
+
# blank slate database
|
47
|
+
|
48
|
+
class DemoTasker < Flor::BasicTasker
|
49
|
+
def task(message)
|
50
|
+
(attd['times'] || 1).times do
|
51
|
+
message['payload']['log'] << "#{tasker}: #{task_name}"
|
52
|
+
end
|
53
|
+
reply
|
54
|
+
end
|
55
|
+
end
|
56
|
+
flor.add_tasker(:alice, DemoTasker)
|
57
|
+
flor.add_tasker(:bob, DemoTasker)
|
58
|
+
# a simple logging tasker implementation bound under
|
59
|
+
# two different tasker names
|
60
|
+
|
61
|
+
flor.start
|
62
|
+
# start the flor unit, so that it can process executions
|
63
|
+
|
64
|
+
exid = flor.launch(
|
65
|
+
%q{
|
66
|
+
sequence
|
67
|
+
alice 'hello' times: 2
|
68
|
+
bob 'world'
|
69
|
+
},
|
70
|
+
payload: { log: [ "started at #{Time.now}" ] })
|
71
|
+
# launch a new execution, one that chains alice and bob work
|
28
72
|
|
29
|
-
|
73
|
+
#r = flor.wait(exid, 'terminated')
|
74
|
+
r = flor.wait(exid)
|
75
|
+
# wait for the execution to terminate or to fail
|
76
|
+
|
77
|
+
p r['point']
|
78
|
+
# "terminated" hopefully
|
79
|
+
p r['payload']['log']
|
80
|
+
# [ "started at 2019-03-31 10:20:18 +0900",
|
81
|
+
# "alice: hello", "alice: hello",
|
82
|
+
# "bob: world" ]
|
83
|
+
```
|
84
|
+
|
85
|
+
This quickstart is at [doc/quickstart0/](doc/quickstart0/), it's a minimal, one-file Ruby quickstart.
|
86
|
+
|
87
|
+
There is also [doc/quickstart1/](doc/quickstart1/), a more complex example, that shows a flor setup, where taskers and flows are layed out in a flor directory tree.
|
88
|
+
|
89
|
+
|
90
|
+
## documentation
|
30
91
|
|
31
92
|
See [doc/](doc/).
|
32
93
|
|
94
|
+
* [doc/procedures/](doc/procedures/#procedures) - the basic building blocks of the flor language
|
95
|
+
* [doc/glossary](doc/glossary.md) - words and their meaning in the flor context
|
96
|
+
* [doc/patterns](doc/patterns.md) - workflow patterns and their flor (tentative) implementations
|
97
|
+
|
98
|
+
|
99
|
+
## related projects
|
100
|
+
|
101
|
+
* [mantor/floristry](https://github.com/mantor/floristry) - visualize and interact with flor through Rails facilities
|
102
|
+
* [floraison/pollen](https://github.com/floraison/pollen) - a set of flor hooks that emit over the http
|
103
|
+
* [floraison/florist](https://github.com/floraison/florist) - a flor worklist implementation
|
104
|
+
* [floraison/flack](https://github.com/floraison/flack) - a flor wrapping [Rack](https://github.com/rack/rack) app
|
105
|
+
* [floraison/fugit](https://github.com/floraison/fugit) - a time library for flor and [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
106
|
+
* [floraison/raabro](https://github.com/floraison/raabro) - the PEG library flor uses for its parsing needs
|
107
|
+
|
108
|
+
|
33
109
|
## blog posts and presentations
|
34
110
|
|
35
111
|
* [the flor language](http://jmettraux.skepti.ch/20180927.html?t=the_flor_language) - on the flor workflow definition language itself
|
@@ -39,7 +115,7 @@ See [doc/](doc/).
|
|
39
115
|
* [flor 2017](https://speakerdeck.com/jmettraux/flor-2017) - q1 2017 - very dry deck
|
40
116
|
|
41
117
|
|
42
|
-
##
|
118
|
+
## license
|
43
119
|
|
44
120
|
MIT, see [LICENSE.txt](LICENSE.txt)
|
45
121
|
|
data/lib/flor.rb
CHANGED
data/lib/flor/conf.rb
CHANGED
@@ -48,6 +48,15 @@ module Flor
|
|
48
48
|
# "reserved" state, messages are put back in the "created" state
|
49
49
|
# (by a running unit (scheduler) if any).
|
50
50
|
#
|
51
|
+
# * :db_migration / :sto_migration / :db_migration_dir / :sto_migration_dir
|
52
|
+
# (Flor::Storage#migrate option)
|
53
|
+
# Points the migrator to its Ruby Sequel migration directory.
|
54
|
+
#
|
55
|
+
# * :db_sparse_migrations / :sto_sparse_migrations
|
56
|
+
# (Flor::Storage#migrate option)
|
57
|
+
# Setting this to true is equivalent to calling
|
58
|
+
# `unit.storage.migration(allow_missing_migration_files: true)`
|
59
|
+
#
|
51
60
|
# And finally:
|
52
61
|
#
|
53
62
|
# * :flor_debug or :debug
|
@@ -71,8 +80,12 @@ module Flor
|
|
71
80
|
|
72
81
|
def prepare(conf, over_conf)
|
73
82
|
|
74
|
-
c =
|
75
|
-
|
83
|
+
c =
|
84
|
+
case conf
|
85
|
+
when String then Flor::ConfExecutor.interpret_path_or_source(conf)
|
86
|
+
when Hash then Flor.to_string_keyed_hash(conf)
|
87
|
+
else conf
|
88
|
+
end
|
76
89
|
|
77
90
|
fail ArgumentError.new(
|
78
91
|
"cannot extract conf out of #{c.inspect} (#{conf.class})"
|
@@ -92,10 +105,10 @@ module Flor
|
|
92
105
|
|
93
106
|
def get_class(conf, key)
|
94
107
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
108
|
+
case v = conf[key]
|
109
|
+
when Class then v
|
110
|
+
when String then Flor.const_lookup(v)
|
111
|
+
else nil
|
99
112
|
end
|
100
113
|
end
|
101
114
|
|
data/lib/flor/core/executor.rb
CHANGED
@@ -69,18 +69,31 @@ module Flor
|
|
69
69
|
|
70
70
|
def trigger_hook(hook, message)
|
71
71
|
|
72
|
-
|
72
|
+
m =
|
73
|
+
case
|
74
|
+
when hook.respond_to?(:notify) then :notify
|
75
|
+
when hook.respond_to?(:on_message) then :on_message
|
76
|
+
else :on
|
77
|
+
end
|
78
|
+
as =
|
79
|
+
case hook.method(m).arity
|
80
|
+
when 3 then [ @unit, self, message ]
|
81
|
+
when 2 then [ self, message ]
|
82
|
+
else [ message ]
|
83
|
+
end
|
84
|
+
|
85
|
+
r = hook.send(m, *as)
|
86
|
+
|
87
|
+
Flor.is_array_of_messages?(r) ? r : []
|
73
88
|
end
|
74
89
|
|
75
90
|
def trigger_block(block, opts, message)
|
76
91
|
|
77
92
|
r =
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
block.call(self, message, opts)
|
93
|
+
case block.arity
|
94
|
+
when 1 then block.call(message)
|
95
|
+
when 2 then block.call(message, opts)
|
96
|
+
else block.call(self, message, opts)
|
84
97
|
end
|
85
98
|
|
86
99
|
r.is_a?(Array) && r.all? { |e| e.is_a?(Hash) } ? r : []
|
@@ -146,9 +159,10 @@ module Flor
|
|
146
159
|
# cnid: closure nid
|
147
160
|
# dbg: used to debug messages (useful @node['dbg'] when 'receive')
|
148
161
|
|
149
|
-
|
150
|
-
|
151
|
-
|
162
|
+
%w[ error cancel timeout ]
|
163
|
+
.each { |k|
|
164
|
+
h = message["on_#{k}_handler"]
|
165
|
+
node["on_#{k}"] = [ h ] if h }
|
152
166
|
|
153
167
|
@execution['nodes'][nid] = node
|
154
168
|
end
|
@@ -228,6 +242,19 @@ module Flor
|
|
228
242
|
heap = node['heap']
|
229
243
|
|
230
244
|
heac = Flor::Procedure[heap]
|
245
|
+
unless heac
|
246
|
+
puts "v" * 80
|
247
|
+
puts "===node:"
|
248
|
+
p node['nid']
|
249
|
+
p heap
|
250
|
+
puts "."
|
251
|
+
pp node
|
252
|
+
puts "===message:"
|
253
|
+
p message['point']
|
254
|
+
puts "."
|
255
|
+
pp message
|
256
|
+
puts "." * 80
|
257
|
+
end
|
231
258
|
fail NameError.new("unknown procedure #{heap.inspect}") unless heac
|
232
259
|
|
233
260
|
head = heac.new(self, node, message)
|
@@ -426,7 +453,7 @@ module Flor
|
|
426
453
|
ms = []
|
427
454
|
ms += @unit.notify(self, message) # pre
|
428
455
|
|
429
|
-
ms +=
|
456
|
+
ms += send(message['point'], message)
|
430
457
|
|
431
458
|
message['payload'] = message.delete('pld') if message.has_key?('pld')
|
432
459
|
message['consumed'] = Flor.tstamp
|
@@ -456,11 +483,6 @@ module Flor
|
|
456
483
|
[]
|
457
484
|
end
|
458
485
|
|
459
|
-
def entered(message); []; end
|
460
|
-
def left(message); []; end
|
461
|
-
|
462
|
-
def ceased(message); []; end
|
463
|
-
|
464
486
|
def terminated(message)
|
465
487
|
|
466
488
|
message['vars'] = @execution['nodes']['0']['vars']
|
@@ -496,6 +518,13 @@ module Flor
|
|
496
518
|
end
|
497
519
|
|
498
520
|
def signal(message); []; end
|
521
|
+
def entered(message); []; end
|
522
|
+
def left(message); []; end
|
523
|
+
def ceased(message); []; end
|
524
|
+
#
|
525
|
+
# Return an empty array of new messages. No direct effect.
|
526
|
+
#
|
527
|
+
# Some trap, hook, and/or waiter might lie in wait though.
|
499
528
|
|
500
529
|
def lookup_on_error_parent(message)
|
501
530
|
|
data/lib/flor/core/node.rb
CHANGED
@@ -441,12 +441,12 @@ class Flor::Node
|
|
441
441
|
if node == nil || mod == 'd'
|
442
442
|
|
443
443
|
return lookup_arg_container(key) \
|
444
|
-
if mod == '' && %w[ args argv argh ].include?(key)
|
444
|
+
if mod == '' && %w[ arga args argv argh argd ].include?(key)
|
445
445
|
|
446
|
-
if vwl = node['vwlist']
|
446
|
+
if vwl = node['vwlist'] # variable white list
|
447
447
|
return lookup_dvar_container(mod, key) unless var_match?(vwl, key)
|
448
448
|
end
|
449
|
-
if vbl = node['vblist']
|
449
|
+
if vbl = node['vblist'] # variable black list
|
450
450
|
return lookup_dvar_container(mod, key) if var_match?(vbl, key)
|
451
451
|
end
|
452
452
|
|
@@ -502,7 +502,7 @@ class Flor::Node
|
|
502
502
|
|
503
503
|
val =
|
504
504
|
case key
|
505
|
-
when 'args', 'argv' then args.collect(&:last)
|
505
|
+
when 'arga', 'args', 'argv' then args.collect(&:last)
|
506
506
|
else args.inject({}) { |h, (k, v)| h[k] = v if k; h }
|
507
507
|
end
|
508
508
|
|
data/lib/flor/core/procedure.rb
CHANGED
@@ -615,6 +615,13 @@ class Flor::Procedure < Flor::Node
|
|
615
615
|
# was considering passing the whole vars back (as 'varz'), but
|
616
616
|
# it got in the way... and it might be heavy
|
617
617
|
|
618
|
+
%w[ error cancel timeout ]
|
619
|
+
.each { |k|
|
620
|
+
co = @node["child_on_#{k}"]
|
621
|
+
next unless co
|
622
|
+
kri = [ '*' ]
|
623
|
+
m["on_#{k}_handler"] = [ kri, co ] }
|
624
|
+
|
618
625
|
[ m ]
|
619
626
|
end
|
620
627
|
|
@@ -787,6 +794,10 @@ class Flor::Procedure < Flor::Node
|
|
787
794
|
#
|
788
795
|
# an idea from "sort" apply, may be useful later on...
|
789
796
|
|
797
|
+
if pl = opts[:payload]
|
798
|
+
ms.first['payload'] = pl
|
799
|
+
end
|
800
|
+
|
790
801
|
ms
|
791
802
|
end
|
792
803
|
|
@@ -893,6 +904,35 @@ class Flor::Procedure < Flor::Node
|
|
893
904
|
wrap_cancel_children('flavour' => 'kill') +
|
894
905
|
wrap_cancelled
|
895
906
|
end
|
907
|
+
|
908
|
+
def do_add
|
909
|
+
|
910
|
+
return [] unless node_open?
|
911
|
+
# if the node is closed or ended, discard the add message
|
912
|
+
|
913
|
+
add
|
914
|
+
end
|
915
|
+
|
916
|
+
def add
|
917
|
+
|
918
|
+
fail Flor::FlorError.new(
|
919
|
+
"procedure does not accept add-iteration", self
|
920
|
+
) if message['elements']
|
921
|
+
|
922
|
+
# TODO fail if the procedure changed
|
923
|
+
# could the message contain a SHA for the node as was when the
|
924
|
+
# that message got emitted?
|
925
|
+
# well, could that be applied to other messages? too late?
|
926
|
+
|
927
|
+
#puts " === add " + ("=" * 40)
|
928
|
+
#pp message
|
929
|
+
#puts " === add. " + ("=" * 39)
|
930
|
+
t = tree
|
931
|
+
i = Flor.child_id(message['tnid'])
|
932
|
+
t[1].insert(i, *message['trees'])
|
933
|
+
|
934
|
+
[]
|
935
|
+
end
|
896
936
|
end
|
897
937
|
|
898
938
|
|
data/lib/flor/djan.rb
CHANGED
@@ -88,10 +88,13 @@ module Flor
|
|
88
88
|
|
89
89
|
if kt = opts[:keytab]
|
90
90
|
out << ' ' * kt
|
91
|
+
:indent
|
91
92
|
elsif opts[:indent]
|
92
93
|
newline(out, opts)
|
94
|
+
:newline
|
93
95
|
elsif ! opts[:compact]
|
94
96
|
space(out, opts)
|
97
|
+
:space
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
@@ -152,9 +155,9 @@ module Flor
|
|
152
155
|
c_inf(':', out, opts)
|
153
156
|
|
154
157
|
kt = key_max_len ? key_max_len - kl : nil
|
155
|
-
newline_or_space(out, opts.merge(keytab: kt))
|
158
|
+
r = newline_or_space(out, opts.merge(keytab: kt))
|
156
159
|
|
157
|
-
to_d(v, out, indent(opts, inc: 2, keytab: kt))
|
160
|
+
to_d(v, out, indent(opts, inc: 2, keytab: r == :newline ? kt : 1))
|
158
161
|
|
159
162
|
if ii < x.size - 1
|
160
163
|
c_inf(',', out, opts)
|
data/lib/flor/flor.rb
CHANGED
@@ -52,15 +52,29 @@ module Flor
|
|
52
52
|
end
|
53
53
|
def dupm(h, hh); self.dup_and_merge(h, hh); end
|
54
54
|
|
55
|
-
def
|
55
|
+
def deep_merge(o0, o1, in_place=false)
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
t0 = type(o0)
|
58
|
+
t1 = type(o1)
|
59
|
+
|
60
|
+
return o1 if t1 != t0
|
61
|
+
|
62
|
+
if t0 == :array
|
63
|
+
o1.each_with_index.inject(in_place ? o0 : o0.dup) { |a, (e1, i)|
|
64
|
+
a[i] = deep_merge(o0[i], e1, in_place)
|
65
|
+
a }
|
66
|
+
elsif t0 == :object
|
67
|
+
o1.inject(in_place ? o0 : o0.dup) { |h, (k, v1)|
|
68
|
+
h[k] = deep_merge(o0[k], v1, in_place)
|
69
|
+
h }
|
70
|
+
else
|
71
|
+
o1
|
61
72
|
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def deep_merge!(o0, o1)
|
62
76
|
|
63
|
-
|
77
|
+
deep_merge(o0, o1, true)
|
64
78
|
end
|
65
79
|
|
66
80
|
def false?(o)
|
@@ -314,7 +328,14 @@ module Flor
|
|
314
328
|
"not a sub domain #{sub.inspect}"
|
315
329
|
) unless potential_domain_name?(sub)
|
316
330
|
|
317
|
-
|
331
|
+
sub_domain?(dom, sub)
|
332
|
+
end
|
333
|
+
|
334
|
+
def sub_domain?(dom, sub)
|
335
|
+
|
336
|
+
dom == '' ||
|
337
|
+
sub == dom ||
|
338
|
+
sub[0, dom.length + 1] == dom + '.'
|
318
339
|
end
|
319
340
|
|
320
341
|
def split_domain_unit(s)
|
@@ -443,6 +464,36 @@ module Flor
|
|
443
464
|
o[1].match(/\A\/.*\/[a-zA-Z]*\z/)
|
444
465
|
end
|
445
466
|
|
467
|
+
def is_sqs_tree?(o)
|
468
|
+
|
469
|
+
o.is_a?(Array) &&
|
470
|
+
o[0] == '_sqs' &&
|
471
|
+
o[2].is_a?(Integer) &&
|
472
|
+
o[1].is_a?(String)
|
473
|
+
end
|
474
|
+
|
475
|
+
def is_num_tree?(o)
|
476
|
+
|
477
|
+
o.is_a?(Array) &&
|
478
|
+
o[0] == '_num' &&
|
479
|
+
o[2].is_a?(Integer) &&
|
480
|
+
o[1].is_a?(Numeric)
|
481
|
+
end
|
482
|
+
|
483
|
+
def is_ref_tree?(o)
|
484
|
+
|
485
|
+
o.is_a?(Array) &&
|
486
|
+
(o[0] == '_ref' || o[0] == '_reff') &&
|
487
|
+
o[2].is_a?(Integer) &&
|
488
|
+
o[1].is_a?(Array) &&
|
489
|
+
o[1].all? { |e| is_sqs_tree?(e) || is_num_tree?(e) }
|
490
|
+
end
|
491
|
+
|
492
|
+
def ref_to_path(t)
|
493
|
+
|
494
|
+
t[1].collect { |tt| tt[1].to_s }.join('.')
|
495
|
+
end
|
496
|
+
|
446
497
|
# Returns [ st, i ], the parent subtree for the final i index of the nid
|
447
498
|
# Used when inserting updated subtrees.
|
448
499
|
#
|
@@ -543,6 +594,40 @@ module Flor
|
|
543
594
|
|
544
595
|
indent == '' ? out.string : nil
|
545
596
|
end
|
597
|
+
|
598
|
+
def to_string_keyed_hash(o)
|
599
|
+
|
600
|
+
case o
|
601
|
+
when Array
|
602
|
+
o.collect { |e| to_string_keyed_hash(e) }
|
603
|
+
when Hash
|
604
|
+
o.inject({}) { |h, (k, v)| h[k.to_s] = to_string_keyed_hash(v); h }
|
605
|
+
else
|
606
|
+
o
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
def to_camel_case(s)
|
611
|
+
|
612
|
+
s.sub(/\A[a-z]/) { |m| m.upcase }.gsub(/_[a-z]/) { |m| m[1, 1].upcase }
|
613
|
+
end
|
614
|
+
|
615
|
+
# Available as `Flor.migration_dir`
|
616
|
+
#
|
617
|
+
def migration_dir
|
618
|
+
|
619
|
+
File.absolute_path(
|
620
|
+
File.join(
|
621
|
+
File.dirname(__FILE__), 'migrations'))
|
622
|
+
end
|
623
|
+
|
624
|
+
def caller_fname
|
625
|
+
|
626
|
+
caller
|
627
|
+
.find { |l| ! l.match(/\/lib\/flor\//) }
|
628
|
+
.match(/\A([^:]+:\d+)/)[1]
|
629
|
+
.strip
|
630
|
+
end
|
546
631
|
end
|
547
632
|
end
|
548
633
|
|