flow-lite 0.0.1 → 1.0.3

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