bahuvrihi-tap 0.10.3 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ module Tap
2
+ module Support
3
+ class Combinator
4
+ attr_reader :a, :b
5
+
6
+ def initialize(*sets)
7
+ @a = make_set(sets.shift)
8
+ @b = make_set(*sets)
9
+ end
10
+
11
+ def sets
12
+ sets_in(@a) + sets_in(@b)
13
+ end
14
+
15
+ def empty?
16
+ @a.empty? && @b.empty?
17
+ end
18
+
19
+ def length
20
+ case
21
+ when !(@a.empty? || @b.empty?)
22
+ @a.length * @b.length
23
+ when @a.empty?
24
+ @b.length
25
+ when @b.empty?
26
+ @a.length
27
+ end
28
+ end
29
+
30
+ def each
31
+ case
32
+ when !(@a.empty? || @b.empty?)
33
+ @a.each do |*a|
34
+ @b.each do |*b|
35
+ yield(*(a + b))
36
+ end
37
+ end
38
+ when @a.empty?
39
+ @b.each {|*b| yield(*b) }
40
+ when @b.empty?
41
+ @a.each {|*a| yield(*a) }
42
+ end
43
+ end
44
+
45
+ def collect
46
+ cc = []
47
+ each do |*c|
48
+ cc << (block_given? ? yield(*c) : c)
49
+ end
50
+ cc
51
+ end
52
+
53
+ protected
54
+
55
+ def sets_in(set)
56
+ set.kind_of?(Array) ? (set.empty? ? [] : [set]) : set.sets
57
+ end
58
+
59
+ def make_set(*sets)
60
+ return Combinator.new(*sets) if sets.length > 1
61
+ return [] if sets.empty?
62
+
63
+ set = sets.first
64
+ case set
65
+ when Combinator then set
66
+ when Array then set
67
+ when nil then []
68
+ else [set]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -78,20 +78,24 @@ module Tap
78
78
  end
79
79
 
80
80
  unless dependencies.kind_of?(Array)
81
- dependencies = [dependencies]
81
+ raise ArgumentError, "dependencies should be specified as an array (was #{dependencies.class})"
82
82
  end
83
83
 
84
84
  unless dependencies.empty?
85
- dependencies.collect! do |dependency|
86
- case dependency
87
- when Array then dependency
88
- when String, Symbol then [dependency, declare(Tap::Task, dependency)]
89
- else
90
- if dependency.kind_of?(Class) && dependency.ancestors.include?(Tap::Task)
91
- [File.basename(dependency.default_name), dependency]
92
- else
93
- raise ArgumentError, "malformed dependency declaration: #{dependency}"
94
- end
85
+ dependencies.collect! do |entry|
86
+ dependency, argv = case entry
87
+ when Array then entry
88
+ else [entry, []]
89
+ end
90
+
91
+ unless dependency.kind_of?(Class)
92
+ dependency = declare(Tap::Task, dependency)
93
+ end
94
+
95
+ if dependency.ancestors.include?(Tap::Task)
96
+ [File.basename(dependency.default_name), dependency, argv]
97
+ else
98
+ raise ArgumentError, "malformed dependency declaration: #{dependency}"
95
99
  end
96
100
  end
97
101
  end
@@ -0,0 +1,73 @@
1
+ module Tap
2
+ module Support
3
+
4
+ # Dependable encapsulates the module-level methods facilitating
5
+ # dependencies in Executable.
6
+ module Dependable
7
+ def self.extended(base)
8
+ base.clear_dependencies
9
+ end
10
+
11
+ # An array of registered [instance, argv] pairs.
12
+ attr_reader :registry
13
+
14
+ # An array of results matching the registry, produced
15
+ # during dependency resolution by instance._execute(*argv).
16
+ attr_reader :results
17
+
18
+ # Clears all registered dependencies and results.
19
+ def clear_dependencies
20
+ @registry = []
21
+ @results = []
22
+ end
23
+
24
+ # Returns the index of the [instance, argv] pair in self,
25
+ # or nil if the pair is not registered,
26
+ def index(instance, argv=[])
27
+ registry.each_with_index do |entry, index|
28
+ return index if entry[0] == instance && entry[1] == argv
29
+ end
30
+ nil
31
+ end
32
+
33
+ # Registers an [instance, argv] pair with self and returns the index of
34
+ # the pair in the registry; returns the index of a matching pair in the
35
+ # registry if the instance and argv are already registered.
36
+ def register(instance, argv=[])
37
+ if existing = index(instance, argv)
38
+ return existing
39
+ end
40
+
41
+ registry << [instance, argv]
42
+ registry.length - 1
43
+ end
44
+
45
+ # Resolves the instance-argv pairs at the specified indicies by calling
46
+ # instance._execute(*argv). Results are collected in results; a pair is
47
+ # only resolved if an existing result does not exist. Returns self.
48
+ def resolve(indicies)
49
+ indicies.each do |index|
50
+ next if results[index]
51
+ instance, inputs = registry[index]
52
+ results[index] = instance._execute(*inputs)
53
+ end
54
+ self
55
+ end
56
+
57
+ # Returns true if the results at the specified index are non-nil (note
58
+ # that Dependable expects instance-argv pairs to resolve to an Audit;
59
+ # the current value of the Audit may, of course, be nil).
60
+ def resolved?(index)
61
+ results[index] != nil
62
+ end
63
+
64
+ # Sets the results at specified indicies to nil so that their
65
+ # instance-argv pairs will re-execute on resolve. Returns self.
66
+ def reset(indicies)
67
+ indicies.each {|index| results[index] = nil }
68
+ self
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -1,4 +1,5 @@
1
1
  require 'tap/support/audit'
2
+ require 'tap/support/dependable'
2
3
 
3
4
  module Tap
4
5
  module Support
@@ -7,77 +8,28 @@ module Tap
7
8
  # wrapped by extending the object that receives them; the easiest way
8
9
  # to make an object executable is to use Object#_method.
9
10
  module Executable
11
+ extend Dependable
10
12
 
11
13
  # The method called when an Executable is executed via _execute
12
14
  attr_reader :_method_name
13
15
 
14
- # Indicates whether or not to execute in multithread mode.
15
- attr_accessor :multithread
16
-
17
- # Stores the on complete block.
16
+ # Stores the on complete block
18
17
  attr_reader :on_complete_block
19
18
 
19
+ # An array of dependency indexes that will be resolved on _execute
20
20
  attr_reader :dependencies
21
21
 
22
22
  public
23
23
 
24
24
  # Extends obj with Executable and sets up all required variables. The
25
25
  # specified method will be called on _execute.
26
- def self.initialize(obj, method_name, multithread=false, &on_complete_block)
26
+ def self.initialize(obj, method_name, &on_complete_block)
27
27
  obj.extend Executable
28
28
  obj.instance_variable_set(:@_method_name, method_name)
29
- obj.instance_variable_set(:@multithread, multithread)
30
29
  obj.instance_variable_set(:@on_complete_block, on_complete_block)
31
30
  obj.instance_variable_set(:@dependencies, [])
32
31
  obj
33
32
  end
34
-
35
- def self.clear_dependencies
36
- @registry = []
37
- @results = []
38
- end
39
-
40
- def self.registry
41
- @registry
42
- end
43
-
44
- def self.results
45
- @results
46
- end
47
-
48
- def self.index(instance, args)
49
- @registry.each_with_index do |entry, index|
50
- return index if entry[0] == instance && entry[1] == args
51
- end
52
- nil
53
- end
54
-
55
- def self.resolved?(index)
56
- @results[index] != nil
57
- end
58
-
59
- def self.resolve(indicies)
60
- indicies.each do |index|
61
- next if @results[index]
62
- instance, inputs = @registry[index]
63
- @results[index] = instance._execute(*inputs)
64
- end
65
- end
66
-
67
- def self.reset(indicies)
68
- indicies.each {|index| @results[index] = nil }
69
- end
70
-
71
- def self.register(instance, args)
72
- if existing = index(instance, args)
73
- return existing
74
- end
75
-
76
- @registry << [instance, args]
77
- @registry.length - 1
78
- end
79
-
80
- clear_dependencies
81
33
 
82
34
  # Sets a block to receive the results of _execute. Raises an error
83
35
  # if an on_complete block is already set. Override an existing
@@ -93,8 +45,8 @@ module Tap
93
45
  end
94
46
 
95
47
  # Adds the dependency to self, making self dependent on the dependency.
96
- # The dependency will be called with the input arguments during
97
- # resolve_dependencies.
48
+ # The dependency will be resolved by calling dependency._execute with
49
+ # the input arguments during resolve_dependencies.
98
50
  def depends_on(dependency, *inputs)
99
51
  raise ArgumentError, "not an Executable: #{dependency}" unless dependency.kind_of?(Executable)
100
52
  raise ArgumentError, "cannot depend on self" if dependency == self
@@ -104,13 +56,15 @@ module Tap
104
56
  index
105
57
  end
106
58
 
107
- # Resolves dependencies by calling dependency.resolve with
108
- # the dependency arguments.
59
+ # Resolves dependencies by calling dependency._execute with
60
+ # the dependency arguments. (See Dependable#resolve).
109
61
  def resolve_dependencies
110
62
  Executable.resolve(dependencies)
111
63
  self
112
64
  end
113
65
 
66
+ # Resets dependencies so they will be re-resolved on resolve_dependencies.
67
+ # (See Dependable#reset).
114
68
  def reset_dependencies
115
69
  Executable.reset(dependencies)
116
70
  self
@@ -171,7 +125,7 @@ end
171
125
  # push_to_array = array._method(:push)
172
126
  #
173
127
  # task = Tap::Task.new
174
- # task.app.sequence(task, push_to_array)
128
+ # task.sequence(push_to_array)
175
129
  #
176
130
  # task.enq(1).enq(2,3)
177
131
  # task.app.run
@@ -181,10 +135,10 @@ end
181
135
  class Object
182
136
 
183
137
  # Initializes a Tap::Support::Executable using the Method returned by
184
- # Object#method(method_name), setting multithread and the on_complete
185
- # block as specified. Returns nil if Object#method returns nil.
186
- def _method(method_name, multithread=false, &on_complete_block) # :yields: _result
138
+ # Object#method(method_name), setting the on_complete block as specified.
139
+ # Returns nil if Object#method returns nil.
140
+ def _method(method_name, &on_complete_block) # :yields: _result
187
141
  return nil unless m = method(method_name)
188
- Tap::Support::Executable.initialize(m, :call, multithread, &on_complete_block)
142
+ Tap::Support::Executable.initialize(m, :call, &on_complete_block)
189
143
  end
190
144
  end
@@ -5,18 +5,6 @@ module Tap
5
5
  module Support
6
6
  module Gems
7
7
 
8
- Tap::Env.manifest(:public_paths, "public") do |search_path|
9
- Dir.glob(File.join(search_path, "**/*")).collect do |path|
10
- ["/" + Tap::Root.relative_filepath(search_path, path), path]
11
- end
12
- end
13
-
14
- Tap::Env.manifest(:templates, "template") do |search_path|
15
- Dir.glob(File.join(search_path, "**/*.erb")).collect do |path|
16
- ["/" + Tap::Root.relative_filepath(search_path, path), path]
17
- end
18
- end
19
-
20
8
  Tap::Env.manifest(:cgis, "cgi") do |search_path|
21
9
  Dir.glob(File.join(search_path, "**/*.rb")).collect do |path|
22
10
  ["/" + Tap::Root.relative_filepath(search_path, path), path]
@@ -28,10 +16,10 @@ module Tap
28
16
  path = env['PATH_INFO']
29
17
 
30
18
  case
31
- when public_page = search(:public_paths, path)
19
+ when public_page = known_path(:public, path)
32
20
  # serve named static pages
33
21
  response(env) { File.read(public_page) }
34
-
22
+
35
23
  when cgi_page = search(:cgis, path)
36
24
  # serve cgis relative to a cgi path
37
25
  run_cgi(cgi_page, env)
@@ -45,21 +33,41 @@ module Tap
45
33
  end
46
34
  template_response('index', env)
47
35
 
36
+ when config[:development] && template_page = known_path(:template, path)
37
+ response(env) { template(File.read(template_page), env) }
38
+
48
39
  else
49
40
  # handle all other requests as errors
50
41
  template_response('404', env)
51
42
 
52
43
  end
53
44
  end
45
+
46
+ def known_path(dir, path)
47
+ each do |env|
48
+ directory = env.root.filepath(dir)
49
+ file = env.root.filepath(dir, path)
50
+
51
+ if file != directory && file.index(directory) == 0 && File.exists?(file)
52
+ return file
53
+ end
54
+ end
55
+
56
+ nil
57
+ end
54
58
 
55
59
  #--
56
60
  # Runs a cgi and returns an array as demanded by rack.
57
61
  def run_cgi(cgi_path, env)
58
- current_output = $>
62
+ current_input = $stdin
63
+ current_output = $stdout
64
+
65
+ cgi_input = env['rack.input']
59
66
  cgi_output = StringIO.new("")
60
67
 
61
68
  begin
62
- $> = cgi_output
69
+ $stdin = cgi_input
70
+ $stdout = cgi_output
63
71
 
64
72
  with_env(env) { load(cgi_path) }
65
73
 
@@ -80,7 +88,8 @@ module Tap
80
88
  # when an error occurs, return a standard cgi error with backtrace
81
89
  [500, {'Content-Type' => 'text/plain'}, %Q{#{$!.class}: #{$!.message}\n#{$!.backtrace.join("\n")}}]
82
90
  ensure
83
- $> = current_output
91
+ $stdin = current_input
92
+ $stdout = current_output
84
93
  end
85
94
  end
86
95
 
@@ -92,7 +101,7 @@ module Tap
92
101
  begin
93
102
  ENV.clear
94
103
  env.each_pair {|key, value| ENV[key] = value if value.kind_of?(String)}
95
-
104
+
96
105
  yield
97
106
  ensure
98
107
  ENV.clear
@@ -123,6 +132,11 @@ module Tap
123
132
  end
124
133
  end
125
134
  end
135
+
136
+ def cgi_template(name, attributes={})
137
+ path = root.filepath(:template, "#{name}.erb")
138
+ Templater.new( File.read(path), {:server => self}.merge(attributes) ).build
139
+ end
126
140
 
127
141
  def template(template, env, attributes={})
128
142
  # partition and sort the env variables into
@@ -142,7 +156,7 @@ module Tap
142
156
  end
143
157
 
144
158
  def template_response(name, env)
145
- path = search(:templates, name)
159
+ path = known_path(:template, "#{name}.erb")
146
160
  response(env) { template(File.read(path), env) }
147
161
  end
148
162
  end
@@ -5,7 +5,7 @@ module Tap
5
5
  module Parsers
6
6
 
7
7
  # rounds syntax
8
- # 0[task]=dump&0[config][key]=value&0[input][]=a&0[input][]=b
8
+ # 0[tasc]=dump&0[config][key]=value&0[input][]=a&0[input][]=b&0[selected]
9
9
  # sequence[1]=1,2,3
10
10
  # fork[1]=2,3
11
11
  # round[0]=1,2,3
@@ -15,13 +15,13 @@ module Tap
15
15
 
16
16
  class << self
17
17
  def parse_argv(hash)
18
- raise ArgumentError, "no task specified" unless hash.has_key?('task')
19
-
18
+ raise ArgumentError, "no task specified" unless hash.has_key?('tasc')
19
+
20
20
  # parse task
21
- argv = [hash['task']]
21
+ argv = [hash.delete('tasc')]
22
22
 
23
23
  # parse configs
24
- configs = hash['config']
24
+ configs = hash.delete('config')
25
25
  configs = YAML.load(configs) if configs.kind_of?(String)
26
26
 
27
27
  case configs
@@ -35,7 +35,7 @@ module Tap
35
35
  end
36
36
 
37
37
  # parse inputs
38
- inputs = hash['inputs']
38
+ inputs = hash.delete('inputs')
39
39
  inputs = YAML.load(inputs) if inputs.kind_of?(String)
40
40
 
41
41
  case inputs
@@ -49,25 +49,55 @@ module Tap
49
49
 
50
50
  def parse_pairs(values)
51
51
  [*values].collect do |value|
52
- value.split(',').collect {|i| i.to_i }
52
+ case value
53
+ when String then value.split(',').collect {|i| i.to_i }
54
+ when Array then value
55
+ else raise ArgumentError, "non-array inputs specified: #{value}"
56
+ end
53
57
  end.collect do |split|
54
58
  next if split.empty?
55
59
  [split.shift, split]
56
60
  end.compact
57
61
  end
62
+
63
+ def compact(argh)
64
+ compact = {}
65
+ argh.each_pair do |key, value|
66
+ compact[key] = case value
67
+ when Array
68
+ value.length == 1 && value[0].kind_of?(String) ? value[0] : value
69
+ when Hash
70
+ compact(value)
71
+ else
72
+ value
73
+ end
74
+ end
75
+ compact
76
+ end
58
77
  end
59
78
 
60
79
  INDEX = /\A\d+\z/
61
80
 
81
+ attr_reader :attributes
82
+
62
83
  def initialize(argh)
63
84
  @argvs = []
85
+ @attributes = []
64
86
 
65
87
  argh.each_pair do |key, value|
66
88
  case key
67
89
  when INDEX
68
90
  argvs[key.to_i] = Server.parse_argv(value)
69
- else
70
- instance_variable_set("@#{key}s", Server.parse_pairs(value))
91
+ attributes[key.to_i] = value
92
+ when "workflow"
93
+ hash = value.kind_of?(String) ? YAML.load(value) : value
94
+ unless hash.kind_of?(Hash)
95
+ raise ArgumentError, "non-hash workflow specified: #{value}"
96
+ end
97
+
98
+ hash.each_pair do |type, entries|
99
+ instance_variable_set("@#{type}s", Server.parse_pairs(entries))
100
+ end
71
101
  end
72
102
  end
73
103