flow-lite 0.0.1 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4af502cc9764efd28671cbd3e4e498c0cc9dbd87
4
- data.tar.gz: af96b08715862f411dfb3cdc7a2b87bc8ac4c8e6
3
+ metadata.gz: bec58eb7b17f61cd186fa21cf7b57bf3b93a8ee0
4
+ data.tar.gz: b2c6da0f1440e70133c79b4ab2bdac478c08e8a0
5
5
  SHA512:
6
- metadata.gz: c2edc964d13234f76c67c52989e4ff9e664164a61ba185f472d6f24a6300d1ee87cdc8ec30eb40a88258fd9beb63e133f95e852249b7f0d5b624e17430a86b31
7
- data.tar.gz: 90a4af15059474729db047b8ad658c36cfacbcdfd0db1802ec29423d22893806eea0a9beeb371364a05f848ffbf8b82aa15333b32fbf3cfe630d8a73287e7eee
6
+ metadata.gz: 6eb5a662ed0393f8a77df8077a7df4c9da69038529fa53ef235d299298e33724aaf764cb4fd3fcbcf9cf53606f5bd22143a1f98d89758ff7905a22b9dd9559ea
7
+ data.tar.gz: d68c1d0439b921ca86a36e850ecb6837b1e3e5dfbfb1cd411c30466d69d30de6909d8315895b69f8d0db9a4fc1d34578bce789d8d1f36a00137384f697116a2b
data/README.md CHANGED
@@ -3,16 +3,264 @@
3
3
  flow-lite gem - (yet) another (lite) workflow engine; let's you define your workflow steps in Flowfiles; incl. the flow command line tool
4
4
 
5
5
 
6
- * home :: [github.com/rubycoco/git](https://github.com/rubycoco/git)
7
- * bugs :: [github.com/rubycoco/git/issues](https://github.com/rubycoco/git/issues)
6
+ * home :: [github.com/rubycoco/flow](https://github.com/rubycoco/flow)
7
+ * bugs :: [github.com/rubycoco/flow/issues](https://github.com/rubycoco/flow/issues)
8
8
  * gem :: [rubygems.org/gems/flow-lite](https://rubygems.org/gems/flow-lite)
9
9
  * rdoc :: [rubydoc.info/gems/flow-lite](http://rubydoc.info/gems/flow-lite)
10
10
 
11
11
 
12
12
 
13
+
13
14
  ## Usage
14
15
 
15
- To be done
16
+
17
+ Define the workflow steps in a Flowfile. Example:
18
+
19
+
20
+ ``` ruby
21
+ step :first_step do
22
+ puts "first_step"
23
+ step :second_step # note: you can call other steps with step
24
+ end
25
+
26
+ step :second_step do
27
+ puts "second_step"
28
+ step :third_step
29
+ end
30
+
31
+ step :third_step do
32
+ puts "third_step"
33
+ end
34
+ ```
35
+
36
+ And then use the `flow` command line tool to run a step.
37
+ Example:
38
+
39
+ ```
40
+ $ flow first_step
41
+ ```
42
+
43
+ Note: By default the `flow` command line tool reads in and looks for `flowfile`, `Flowfile`, `flowfile.rb`, `Flowfile.rb`
44
+ in that order.
45
+ Use the `-f/--flowfile` option to use a different file.
46
+
47
+
48
+
49
+ **Prelude / Prolog**
50
+
51
+ Use the `-r/--require` option to (auto-)require
52
+ some extra libraries or scripts.
53
+ By default for now the "prelude / prolog" that always
54
+ gets auto-required includes:
55
+
56
+ ``` ruby
57
+ require 'pp'
58
+ require 'time'
59
+ require 'date'
60
+ require 'json'
61
+ require 'yaml'
62
+ require 'fileutils'
63
+
64
+ require 'uri'
65
+ require 'net/http'
66
+ require 'net/https'
67
+ ```
68
+
69
+ Tip: See the [`flow-lite.rb`](https://github.com/rubycoco/flow/blob/master/flow-lite/lib/flow-lite.rb) source for the definite always up-to-date list.
70
+
71
+
72
+ That's it for now.
73
+
74
+
75
+
76
+ ## Backstage Internals / Inside Flowfiles
77
+
78
+
79
+ If you read in a `Flowfile` the flow machinery
80
+ builds a new (regular) class derived from `Flow::Base`
81
+ and every step becomes a (regular) method. Example:
82
+
83
+ ``` ruby
84
+ require 'flow-lite'
85
+
86
+ flowfile = Flow::Flowfile.load( <<TXT )
87
+ step :hello do
88
+ puts "Hello, world!"
89
+ end
90
+
91
+ step :hola do
92
+ puts "¡Hola, mundo!"
93
+ end
94
+ TXT
95
+
96
+ flow = flowfile.flow_class.new # (auto-)build a flow class (see Note 1)
97
+ # and construct/return a new instance
98
+ flow.step_hello #=> "Hello, world!"
99
+ flow.step_hola #=> "¡Hola, mundo!"
100
+ flow.step( :hello ) #=> "Hello, world!"
101
+ flow.step( :hola ) #=> "¡Hola, mundo!"
102
+ flow.class.step_methods #=> [:hello, :hola]
103
+
104
+ # or use ruby's (regular) message/metaprogramming machinery
105
+ flow.send( :step_hello ) #=> "Hello, world!"
106
+ flow.send( :step_hola ) #=> "¡Hola, mundo!"
107
+ # or try
108
+ flow.class.instance_methods.grep( /^step_/ ) #=> [:step_hello, :step_hola]
109
+ # ...
110
+ ```
111
+
112
+ Note 1: The Flowfile "source / configuration":
113
+
114
+ ``` ruby
115
+ step :hello do
116
+ puts "Hello, world!"
117
+ end
118
+
119
+ step :hola do
120
+ puts "¡Hola, mundo!"
121
+ end
122
+ ```
123
+
124
+ gets used to (auto-) build (via metaprogramming) a flow class like:
125
+
126
+ ``` ruby
127
+ class Greeter < Flow::Base
128
+ def step_hello
129
+ puts "Hello, world!"
130
+ end
131
+
132
+ def step_hola
133
+ puts "¡Hola, mundo!"
134
+ end
135
+ end
136
+ ```
137
+
138
+
139
+ Note: Behind the stage the metaprogramming "class macro"
140
+ `define_step( symbol, method )`
141
+ or `define_step( symbol ) { block }` defined in `Flow::Base`
142
+ gets used, thus,
143
+ if you want to create steps in a "hand-coded" class
144
+ use:
145
+
146
+
147
+ ``` ruby
148
+ class Greeter < Flow::Base
149
+ define_step :hello do
150
+ puts "Hello, world!"
151
+ end
152
+
153
+ define_step :hola do
154
+ puts "¡Hola, mundo!"
155
+ end
156
+ end
157
+ ```
158
+
159
+
160
+
161
+
162
+
163
+ **Tips & Tricks**
164
+
165
+ Auto-include pre-build / pre-defined steps. Use a (regular) Module e.g.:
166
+
167
+ ``` ruby
168
+ module GitHubActions
169
+ def step_setup
170
+ # setup ssh
171
+ ssh_key = ENV['SSH_KEY']
172
+ ssh_path = File.expand_path( '~/.ssh' )
173
+
174
+ FileUtils.mkdir_p( ssh_path ) # make sure path exists
175
+ File.open( "#{ssh_path}/id_rsa", 'w' ) { |f| f.write( ssh_key ) }
176
+ File.chmod( 0600, "#{ssh_path}/id_rsa" )
177
+
178
+ # setup git
179
+ user_name = ENV['GITHUB_NAME'] || 'you'
180
+ user_email = ENV['GITHUB_EMAIL'] || 'you@example.com'
181
+
182
+ Git.config( 'user.name', user_name, global: true )
183
+ Git.config( 'user.email', user_email, global: true )
184
+
185
+ # ...
186
+ end
187
+ end
188
+ ```
189
+
190
+ and than use (regular) include e.g.:
191
+
192
+ ``` ruby
193
+ class Flow::Base
194
+ include GitHubActions
195
+ end
196
+
197
+ #-or-
198
+
199
+ Flow::Base.include( GitHubActions )
200
+ ```
201
+
202
+ Now all your flows can (re)use `setup` or any other step methods you define.
203
+
204
+
205
+
206
+
207
+ ## Real World Examples
208
+
209
+ **Collect GitHub Statistics**
210
+
211
+ Use GitHub Actions to collect
212
+ GitHub Statistics via GitHub API calls
213
+ and update the JSON datasets
214
+ in the `/cache.github` repo at the `yorobot` org(anization):
215
+
216
+ ``` ruby
217
+ step :clone do
218
+ Mono.clone( '@yorobot/cache.github' )
219
+ end
220
+
221
+
222
+ step :update do
223
+ Hubba.config.data_dir = Mono.real_path( '@yorobot/cache.github' )
224
+
225
+ username = 'geraldb'
226
+ h = Hubba.reposet( username )
227
+ pp h
228
+
229
+ Hubba.update_stats( h )
230
+ Hubba.update_traffic( h )
231
+ puts "Done."
232
+ end
233
+
234
+
235
+ step :push do
236
+ msg = "auto-update week #{Date.today.cweek}"
237
+
238
+ Mono.open( '@yorobot/cache.github' ) do |proj|
239
+ if proj.changes?
240
+ proj.add( "." )
241
+ proj.commit( msg )
242
+ proj.push
243
+ end
244
+ end
245
+ end
246
+ ```
247
+
248
+ (Sources: [`Flowfile`](https://github.com/yorobot/backup/blob/master/Flowfile.rb), [`workflows/update.yml`](https://github.com/yorobot/backup/blob/master/.github/workflows/update.yml) @ `yorobot/backup`)
249
+
250
+
251
+
252
+
253
+
254
+
255
+ ## Installation
256
+
257
+ Use
258
+
259
+ gem install flow-lite
260
+
261
+ or add to your Gemfile
262
+
263
+ gem 'flow-lite'
16
264
 
17
265
 
18
266
 
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Hoe.spec 'flow-lite' do
8
8
  self.summary = "flow-lite gem - (yet) another (lite) workflow engine; let's you define your workflow steps in Flowfiles; incl. the flow command line tool"
9
9
  self.description = summary
10
10
 
11
- self.urls = { home: 'https://github.com/rubycoco/git' }
11
+ self.urls = { home: 'https://github.com/rubycoco/flow' }
12
12
 
13
13
  self.author = 'Gerald Bauer'
14
14
  self.email = 'ruby-talk@ruby-lang.org'
@@ -17,11 +17,7 @@ Hoe.spec 'flow-lite' do
17
17
  self.readme_file = 'README.md'
18
18
  self.history_file = 'CHANGELOG.md'
19
19
 
20
- self.extra_deps = [
21
- ['gitti', '>= 0.6.1' ],
22
- ['hubba', '>= 1.0.0' ],
23
- ['monos', '>= 0.5.0' ],
24
- ]
20
+ self.extra_deps = []
25
21
 
26
22
  self.licenses = ['Public Domain']
27
23
 
@@ -1,15 +1,19 @@
1
+ ##
2
+ ## "prelude / prolog " add some common used stdlibs
3
+ ## add more - why? why not?
1
4
  require 'pp'
2
- require 'optparse'
5
+ require 'time'
6
+ require 'date'
7
+ require 'json'
8
+ require 'yaml'
9
+ require 'fileutils'
3
10
 
11
+ require 'uri'
12
+ require 'net/http'
13
+ require 'net/https'
4
14
 
5
15
 
6
- ####################
7
- # "prelude/prolog" add some 3rd party libs/gems
8
- # -- our own
9
- require 'gitti'
10
- require 'hubba' ## todo/fix: rename to gitti-api/gitti-apis
11
- require 'mono'
12
- # -- some more ???
16
+ require 'optparse'
13
17
 
14
18
 
15
19
 
@@ -40,12 +44,86 @@ end # class Step
40
44
 
41
45
 
42
46
  class Base ## base class for flow class (auto)-constructed/build from flowfile
43
- end
47
+ def self.define_step( name_or_names, &block )
48
+ names = if name_or_names.is_a?( Array )
49
+ name_or_names
50
+ else
51
+ [name_or_names] ## assume single symbol (name); wrap in array
52
+ end
53
+ names = names.map {|name| name.to_sym } ## make sure we always use symbols
54
+
55
+
56
+ name = names[0]
57
+ puts "[flow] adding step >#{name}<..."
58
+ define_method( :"step_#{name}", &block )
59
+
60
+ alt_names = names[1..-1]
61
+ alt_names.each do |alt_name|
62
+ puts "[flow] adding alias >#{alt_name}< for >#{name}<..."
63
+ alias_method( :"step_#{alt_name}", :"step_#{name}" )
64
+ end
65
+ end # method self.define_step
66
+
67
+
68
+
69
+ ## run step by symbol/name (e.g. step :hello - etc.)
70
+ def step( name )
71
+ step_name = :"step_#{name}" ## note: make sure we always use symbols
72
+ if respond_to?( step_name )
73
+ #######
74
+ ## check: track (and report) call stack - why? why not?
75
+ ## e.g.
76
+ ## [flow >(1) first_step)] step >first_step< - starting...
77
+ ## [flow >(2) ..first_step > second_step)] step >second_step< - starting...
78
+ ## [flow >(3) ....first_step > second_step > third_step)] step >third_step< - starting...
79
+ @stack ||= [] ## use a call stack of step names
80
+ @stack.push( name )
81
+
82
+ puts "[flow >(#{@stack.size}) #{'..'*(@stack.size-1)}#{@stack.join(' > ')})] step >#{name}< - starting..."
83
+ start_time = Time.now ## todo: use Timer? t = Timer.start / stop / diff etc. - why? why not?
84
+
85
+ __send__( step_name )
86
+
87
+ end_time = Time.now
88
+ diff_time = end_time - start_time
89
+ puts "[flow <(#{@stack.size}) #{'..'*(@stack.size-1)}#{@stack.join(' < ')})] step >#{name}< - done in #{diff_time} sec(s)"
90
+ @stack.pop
91
+ else
92
+ puts "!! ERROR: step definition >#{name}< not found; cannot run/execute - known (defined) steps include:"
93
+ pp self.class.step_methods #=> e.g. [:hello, ...]
94
+ exit 1
95
+ end
96
+ end # method step
97
+
98
+
99
+ def self.step_methods
100
+ names = instance_methods.reduce([]) do |names, name|
101
+ names << $1.to_sym if name =~ /^step_(.+)/
102
+ names
103
+ end
104
+ names
105
+ end
106
+ end # class Base
107
+
108
+
44
109
 
45
110
 
46
111
  class Flowfile
112
+
113
+ ## find flowfile path by convention
114
+ ## check for name by convention in this order:
115
+ NAMES = ['flowfile', 'Flowfile',
116
+ 'flowfile.rb', 'Flowfile.rb']
117
+ def self.find_file
118
+ NAMES.each do |name|
119
+ return "./#{name}" if File.exist?( "./#{name}" )
120
+ end
121
+ nil
122
+ end # method self.find_file
123
+
124
+
47
125
  ## convenience method - use like Flowfile.load_file()
48
- def self.load_file( path='./Flowfile' )
126
+ def self.load_file( path )
49
127
  code = File.open( path, 'r:utf-8' ) { |f| f.read }
50
128
  load( code )
51
129
  end
@@ -59,27 +137,23 @@ class Flowfile
59
137
 
60
138
 
61
139
 
62
- def flow ## build flow class
140
+ def flow
141
+ ## todo/check: always return a new instance why? why not?
142
+ flow_class.new
143
+ end
144
+
145
+ def flow_class
63
146
  @flow_class ||= build_flow_class
64
- @flow_class.new ## todo/check: always return a new instance why? why not?
65
147
  end
66
148
 
67
149
  def build_flow_class
68
- puts " building flow class..."
69
- flowfile = self
70
- klass = Class.new( Base ) do
71
- flowfile.steps.each_with_index do |step,i|
72
- name = step.names[0]
73
- puts " adding step #{i+1}/#{flowfile.steps.size} >#{name}<..."
74
- define_method( name, &step.block )
75
-
76
- alt_names = step.names[1..-1]
77
- alt_names.each do |alt_name|
78
- puts " adding alias >#{alt_name}< for >#{name}<..."
79
- alias_method( alt_name, name )
80
- end
81
- end
150
+ puts "[flow] building flow class..."
151
+ klass = Class.new( Base )
152
+
153
+ steps.each do |step|
154
+ klass.define_step( step.names, &step.block )
82
155
  end
156
+
83
157
  klass
84
158
  end
85
159
 
@@ -97,21 +171,15 @@ class Flowfile
97
171
  @steps << Step.new( name, block )
98
172
  end
99
173
 
100
-
101
174
  def run( name )
102
- name = name.to_sym ## make sure we always use symbols
103
- if flow.respond_to?( name )
104
- flow.send( name )
105
- else
106
- puts "!! ERROR: step definition >#{name}< not found; cannot run/execute - known steps include:"
107
- pp @steps
108
- exit 1
109
- end
175
+ ## todo/check: always return/use a new instance why? why not?
176
+ flow_class.new.step( name )
110
177
  end
111
178
  end # class Flowfile
112
179
 
113
180
 
114
181
 
182
+
115
183
  class Tool
116
184
  def self.main( args=ARGV )
117
185
  options = {}
@@ -119,17 +187,56 @@ class Tool
119
187
  parser.on( '-f FILENAME', '--flowfile FILENAME' ) do |filename|
120
188
  options[:flowfile] = filename
121
189
  end
190
+
191
+ ## note:
192
+ ## you can add many/multiple modules
193
+ ## e.g. -r gitti -r mono etc.
194
+ parser.on( '-r NAME', '--require NAME') do |name|
195
+ options[:requires] ||= []
196
+ options[:requires] << name
197
+ end
122
198
  end.parse!( args )
123
199
 
124
- path = options[:flowfile] || './Flowfile'
200
+
201
+ ## check for any (dynamic/auto) requires
202
+ if options[:requires]
203
+ names = options[:requires]
204
+ names.each do |name|
205
+ ## todo/check: add some error/exception handling here - why? why not?
206
+ puts "[flow] auto-require >#{name}<..."
207
+ require( name )
208
+ end
209
+ else ## use/try defaults
210
+ config_path = "./config.rb"
211
+ if File.exist?( config_path )
212
+ puts "[flow] auto-require (default) >#{config_path}<..."
213
+ require( config_path )
214
+ end
215
+ end
216
+
217
+
218
+ path = nil
219
+ if options[:flowfile]
220
+ path = options[:flowfile]
221
+ else
222
+ path = Flowfile.find_file
223
+
224
+ if path.nil?
225
+ STDERR.puts "!! ERROR - no flowfile found, sorry - looking for: #{Flowfile::NAMES.join(', ')} in (#{Dir.pwd})"
226
+ exit 1
227
+ end
228
+ end
229
+
230
+ puts "[flow] loading >#{path}<..."
125
231
  flowfile = Flowfile.load_file( path )
126
232
 
233
+
127
234
  ## allow multipe steps getting called - why? why not?
128
235
  ## flow setup clone update etc??
129
236
  args.each do |arg|
130
237
  flowfile.run( arg )
131
238
  end
132
- end
239
+ end # method self.main
133
240
  end # class Tool
134
241
 
135
242
  end # module Flow
@@ -1,8 +1,8 @@
1
1
  module FlowLite
2
2
 
3
- MAJOR = 0 ## todo: namespace inside version or something - why? why not??
3
+ MAJOR = 1 ## todo: namespace inside version or something - why? why not??
4
4
  MINOR = 0
5
- PATCH = 1
5
+ PATCH = 3
6
6
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
7
 
8
8
  def self.version
metadata CHANGED
@@ -1,57 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flow-lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-23 00:00:00.000000000 Z
11
+ date: 2020-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: gitti
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 0.6.1
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 0.6.1
27
- - !ruby/object:Gem::Dependency
28
- name: hubba
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 1.0.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: 1.0.0
41
- - !ruby/object:Gem::Dependency
42
- name: monos
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 0.5.0
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 0.5.0
55
13
  - !ruby/object:Gem::Dependency
56
14
  name: rdoc
57
15
  requirement: !ruby/object:Gem::Requirement
@@ -104,7 +62,7 @@ files:
104
62
  - bin/flow
105
63
  - lib/flow-lite.rb
106
64
  - lib/flow-lite/version.rb
107
- homepage: https://github.com/rubycoco/git
65
+ homepage: https://github.com/rubycoco/flow
108
66
  licenses:
109
67
  - Public Domain
110
68
  metadata: {}