saper 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/Rakefile +6 -6
- data/bin/saper +17 -36
- data/lib/saper.rb +63 -20
- data/lib/saper/actions/append_with.rb +1 -1
- data/lib/saper/actions/convert_to_html.rb +1 -1
- data/lib/saper/actions/convert_to_json.rb +6 -2
- data/lib/saper/actions/convert_to_markdown.rb +1 -1
- data/lib/saper/actions/convert_to_time.rb +38 -1
- data/lib/saper/actions/convert_to_xml.rb +1 -1
- data/lib/saper/actions/create_atom.rb +6 -2
- data/lib/saper/actions/fetch.rb +3 -2
- data/lib/saper/actions/fetch_with_token.rb +18 -0
- data/lib/saper/actions/find.rb +1 -1
- data/lib/saper/actions/find_first.rb +1 -1
- data/lib/saper/actions/get_attribute.rb +7 -2
- data/lib/saper/actions/get_contents.rb +1 -1
- data/lib/saper/actions/get_text.rb +1 -1
- data/lib/saper/actions/nothing.rb +11 -0
- data/lib/saper/actions/prepend_with.rb +2 -2
- data/lib/saper/actions/remove_after.rb +2 -2
- data/lib/saper/actions/remove_before.rb +2 -2
- data/lib/saper/actions/remove_matching.rb +2 -2
- data/lib/saper/actions/remove_tags.rb +1 -1
- data/lib/saper/actions/replace.rb +1 -1
- data/lib/saper/actions/run_recipe.rb +2 -2
- data/lib/saper/actions/run_recipe_and_save.rb +3 -3
- data/lib/saper/actions/save.rb +2 -2
- data/lib/saper/actions/select_matching.rb +2 -2
- data/lib/saper/actions/set_input.rb +1 -1
- data/lib/saper/actions/skip_tags.rb +1 -1
- data/lib/saper/actions/split.rb +1 -1
- data/lib/saper/arguments/attribute.rb +6 -0
- data/lib/saper/arguments/recipe.rb +12 -6
- data/lib/saper/arguments/service.rb +12 -0
- data/lib/saper/arguments/text.rb +1 -0
- data/lib/saper/arguments/timezone.rb +2 -1
- data/lib/saper/arguments/url.rb +12 -0
- data/lib/saper/arguments/variable.rb +2 -1
- data/lib/saper/arguments/xpath.rb +2 -1
- data/lib/saper/core/action.rb +43 -13
- data/lib/saper/core/argument.rb +30 -14
- data/lib/saper/core/browser.rb +29 -28
- data/lib/saper/core/dsl.rb +14 -16
- data/lib/saper/core/error.rb +1 -1
- data/lib/saper/core/item.rb +2 -2
- data/lib/saper/core/keychain.rb +58 -5
- data/lib/saper/core/namespace.rb +15 -18
- data/lib/saper/core/recipe.rb +53 -12
- data/lib/saper/core/result.rb +72 -0
- data/lib/saper/core/runtime.rb +44 -177
- data/lib/saper/core/stack.rb +57 -0
- data/lib/saper/items/atom.rb +18 -1
- data/lib/saper/items/document.rb +39 -19
- data/lib/saper/items/html.rb +52 -7
- data/lib/saper/items/json.rb +19 -1
- data/lib/saper/items/markdown.rb +14 -4
- data/lib/saper/items/nothing.rb +6 -0
- data/lib/saper/items/text.rb +28 -3
- data/lib/saper/items/time.rb +25 -2
- data/lib/saper/items/url.rb +8 -2
- data/lib/saper/items/xml.rb +51 -11
- data/lib/{lib → saper/patches}/mechanize.rb +0 -0
- data/lib/{lib → saper/patches}/nokogiri.rb +0 -0
- data/lib/saper/version.rb +2 -2
- data/spec/actions/append_with_spec.rb +29 -52
- data/spec/actions/convert_to_html_spec.rb +13 -30
- data/spec/actions/convert_to_json_spec.rb +13 -30
- data/spec/actions/convert_to_markdown_spec.rb +17 -19
- data/spec/actions/convert_to_time_spec.rb +25 -43
- data/spec/actions/convert_to_xml_spec.rb +15 -11
- data/spec/actions/create_atom_spec.rb +11 -19
- data/spec/actions/fetch_spec.rb +3 -8
- data/spec/actions/find_first_spec.rb +38 -40
- data/spec/actions/find_spec.rb +23 -39
- data/spec/actions/get_attribute_spec.rb +30 -3
- data/spec/actions/{get_contents.rb → get_contents_spec.rb} +0 -0
- data/spec/actions/{get_text.rb → get_text_spec.rb} +0 -0
- data/spec/actions/nothing_spec.rb +7 -0
- data/spec/actions/prepend_with_spec.rb +31 -18
- data/spec/actions/{remove_after.rb → remove_after_spec.rb} +0 -0
- data/spec/actions/{remove_before.rb → remove_before_spec.rb} +0 -0
- data/spec/actions/remove_matching_spec.rb +7 -0
- data/spec/actions/remove_tags_spec.rb +7 -0
- data/spec/actions/run_recipe_and_save_spec.rb +7 -0
- data/spec/actions/run_recipe_spec.rb +7 -0
- data/spec/arguments/attribute_spec.rb +7 -0
- data/spec/arguments/recipe_spec.rb +7 -0
- data/spec/arguments/text_spec.rb +7 -0
- data/spec/arguments/timezone_spec.rb +7 -0
- data/spec/arguments/variable_spec.rb +7 -0
- data/spec/arguments/xpath_spec.rb +7 -0
- data/spec/core/action_spec.rb +13 -142
- data/spec/core/argument_spec.rb +38 -71
- data/spec/core/browser_spec.rb +18 -3
- data/spec/core/dsl_spec.rb +1 -1
- data/spec/core/item_spec.rb +1 -1
- data/spec/core/namespace_spec.rb +0 -11
- data/spec/core/recipe_spec.rb +54 -77
- data/spec/core/result_spec.rb +7 -0
- data/spec/core/runtime_spec.rb +20 -151
- data/spec/core/stack_spec.rb +7 -0
- data/spec/integration/simple_invalid_spec.rb +39 -0
- data/spec/integration/simple_valid_spec.rb +39 -0
- data/spec/items/atom_spec.rb +6 -1
- data/spec/items/document_spec.rb +93 -15
- data/spec/items/html_spec.rb +45 -28
- data/spec/items/json_spec.rb +10 -10
- data/spec/items/markdown_spec.rb +24 -3
- data/spec/items/nothing_spec.rb +1 -1
- data/spec/items/text_spec.rb +13 -41
- data/spec/items/time_spec.rb +25 -4
- data/spec/items/url_spec.rb +1 -7
- data/spec/items/xml_spec.rb +46 -39
- data/spec/spec_helper.rb +2 -21
- metadata +63 -60
- data/lib/lib/json_search.rb +0 -54
- data/spec/actions/run_recipe_and_save_spec.tmp.rb +0 -52
- data/spec/actions/run_recipe_spec.tmp.rb +0 -53
data/lib/saper/core/browser.rb
CHANGED
@@ -10,13 +10,26 @@ module Saper
|
|
10
10
|
# Approximate number of bytes sent.
|
11
11
|
attr_reader :sent
|
12
12
|
|
13
|
+
AGENTS = {
|
14
|
+
:ie6 => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
15
|
+
:ie7 => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
|
16
|
+
:ie8 => 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
|
17
|
+
:ie9 => 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)',
|
18
|
+
:mozilla => 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6',
|
19
|
+
:safari => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/534.51.22',
|
20
|
+
:iphone => 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C28 Safari/419.3',
|
21
|
+
:ipad => 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10',
|
22
|
+
:android => 'Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
|
23
|
+
:saper => 'Mozilla/5.0 (compatible; Saper Ruby client %s)' % Saper::VERSION
|
24
|
+
}
|
25
|
+
|
13
26
|
# Returns a new Browser instance.
|
14
27
|
# @option options [Symbol] :agent User agent
|
15
28
|
# @option options [Hash] :headers Additional request headers
|
16
29
|
# @option options [Logger] :logger Logger instance
|
17
30
|
# @return [Saper::Browser]
|
18
31
|
def initialize(options = {})
|
19
|
-
@agent = options.delete(:agent)
|
32
|
+
@agent = options.delete(:agent) || :saper
|
20
33
|
@headers = options.delete(:headers)
|
21
34
|
@logger = options.delete(:logger) || Saper::Logger.new
|
22
35
|
@history = []
|
@@ -61,46 +74,34 @@ module Saper
|
|
61
74
|
@logger.download(url)
|
62
75
|
@history.push url
|
63
76
|
data = @mech.get(url, query)
|
64
|
-
Saper::Items::Document.new data
|
77
|
+
Saper::Items::Document.new data.body, data.uri, data.header
|
78
|
+
rescue Mechanize::ResponseCodeError
|
79
|
+
Saper::Items::Nothing.new # TODO: change to custom exception
|
65
80
|
end
|
66
81
|
|
67
82
|
# Performs a POST request and returns Saper::Document.
|
68
83
|
# @param url [String] URL to request
|
69
84
|
# @param query [Hash] payload
|
70
85
|
# @return [Saper::Document]
|
71
|
-
def post(url, query = {})
|
86
|
+
def post(url, query = {}, headers = {})
|
72
87
|
@logger.download(url)
|
73
88
|
@history.push url
|
74
|
-
data = @mech.post(url, query)
|
75
|
-
Saper::Items::Document.new data
|
89
|
+
data = @mech.post(url, query, headers)
|
90
|
+
Saper::Items::Document.new data.body, data.uri, data.header
|
91
|
+
rescue Mechanize::ResponseCodeError
|
92
|
+
Saper::Items::Nothing.new # TODO: change to custom exception
|
93
|
+
end
|
94
|
+
|
95
|
+
# @todo
|
96
|
+
def post_with_bearer_token(url, query, token)
|
97
|
+
post(url, query, { "Authorization" => "Bearer %s" % token })
|
76
98
|
end
|
77
99
|
|
78
100
|
# Returns User-Agent string used with requests.
|
79
101
|
# @return [String]
|
80
102
|
def agent
|
81
|
-
|
82
|
-
when :ie6
|
83
|
-
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
|
84
|
-
when :ie7
|
85
|
-
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)'
|
86
|
-
when :ie8
|
87
|
-
'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)'
|
88
|
-
when :ie9
|
89
|
-
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
|
90
|
-
when :mozilla
|
91
|
-
'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6'
|
92
|
-
when :safari
|
93
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/534.51.22'
|
94
|
-
when :iphone
|
95
|
-
'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C28 Safari/419.3'
|
96
|
-
when :ipad
|
97
|
-
'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10'
|
98
|
-
when :android
|
99
|
-
'Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13'
|
100
|
-
else
|
101
|
-
'Mozilla/5.0 (compatible; Saper Ruby client %s)' % Saper::VERSION
|
102
|
-
end
|
103
|
+
AGENTS[@agent.to_sym] || @agent.to_s
|
103
104
|
end
|
104
|
-
|
105
|
+
|
105
106
|
end
|
106
107
|
end
|
data/lib/saper/core/dsl.rb
CHANGED
@@ -16,26 +16,21 @@ module Saper
|
|
16
16
|
# A mixin module with a set of parsing methods
|
17
17
|
module Methods
|
18
18
|
|
19
|
+
# @todo
|
19
20
|
def namespace
|
20
21
|
@namespace ||= Saper::Namespace.new
|
21
22
|
end
|
22
23
|
|
24
|
+
# @todo
|
23
25
|
def recipe(id, name = nil, &block)
|
24
|
-
namespace[id] = Recipe.parse(id, name,
|
26
|
+
namespace[id] = Recipe.parse(id, name, namespace, &block)
|
25
27
|
end
|
26
28
|
|
29
|
+
# @todo
|
27
30
|
def [](name)
|
28
31
|
namespace[name]
|
29
32
|
end
|
30
33
|
|
31
|
-
def run_by_default(*args)
|
32
|
-
namespace.run_by_default(*args)
|
33
|
-
end
|
34
|
-
|
35
|
-
def run(*args)
|
36
|
-
namespace.run(*args)
|
37
|
-
end
|
38
|
-
|
39
34
|
end
|
40
35
|
|
41
36
|
class Recipe
|
@@ -45,9 +40,9 @@ module Saper
|
|
45
40
|
# `#to_recipe`).
|
46
41
|
# @param id [Symbol] recipe ID
|
47
42
|
# @return [Saper::Recipe, Saper::DSL::Recipe]
|
48
|
-
def self.parse(id, name = nil,
|
49
|
-
instance = self.new(id,
|
50
|
-
if
|
43
|
+
def self.parse(id = nil, name = nil, ns = nil, &block)
|
44
|
+
instance = self.new(id, name, ns, &block)
|
45
|
+
if ns.is_a?(Namespace)
|
51
46
|
instance
|
52
47
|
else
|
53
48
|
instance.to_recipe
|
@@ -56,16 +51,19 @@ module Saper
|
|
56
51
|
|
57
52
|
attr_reader :recipe
|
58
53
|
|
59
|
-
|
60
|
-
|
54
|
+
# @todo
|
55
|
+
def initialize(id, name = nil, ns = nil, &block)
|
56
|
+
@recipe, @ns, @block = Saper::Recipe.new(id, :name => name), ns, block
|
61
57
|
end
|
62
58
|
|
63
|
-
|
59
|
+
# @todo
|
60
|
+
def to_recipe(*args)
|
64
61
|
self.instance_eval(&@block) if recipe.empty?; recipe
|
65
62
|
end
|
66
63
|
|
64
|
+
# @todo
|
67
65
|
def method_missing(name, *args, &block)
|
68
|
-
@recipe << Saper::Action.new(name, *args, :namespace => @
|
66
|
+
@recipe << Saper::Action.new(name, *args, :namespace => @ns, &block)
|
69
67
|
end
|
70
68
|
|
71
69
|
end
|
data/lib/saper/core/error.rb
CHANGED
@@ -9,7 +9,7 @@ module Saper
|
|
9
9
|
# NoAction is raised whenever an implementation of Saper::Action is not found.
|
10
10
|
class ActionNotFound < Error; end
|
11
11
|
|
12
|
-
# NamespaceMissing is raised whenever Saper::
|
12
|
+
# NamespaceMissing is raised whenever Saper::Action requires a missing namespace.
|
13
13
|
class NamespaceMissing < Error; end
|
14
14
|
|
15
15
|
# NoAction is raised whenever an unsupported object is added to Saper::Recipe.
|
data/lib/saper/core/item.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Saper
|
2
2
|
class Item
|
3
3
|
|
4
|
-
# Tracks subclasses of Saper::
|
4
|
+
# Tracks subclasses of Saper::Item.
|
5
5
|
# @return [Class]
|
6
6
|
def self.inherited(base)
|
7
7
|
subclasses[base.type] = base
|
@@ -66,7 +66,7 @@ module Saper
|
|
66
66
|
self.class.type.to_sym
|
67
67
|
end
|
68
68
|
|
69
|
-
#
|
69
|
+
# @todo
|
70
70
|
def serialize
|
71
71
|
to_native
|
72
72
|
end
|
data/lib/saper/core/keychain.rb
CHANGED
@@ -1,18 +1,71 @@
|
|
1
1
|
module Saper
|
2
2
|
class Keychain
|
3
|
-
|
3
|
+
|
4
|
+
# @todo
|
5
|
+
def self.load(path)
|
6
|
+
case File.extname(path)
|
7
|
+
when '.yaml'
|
8
|
+
load_yaml(path)
|
9
|
+
when '.yml'
|
10
|
+
load_yaml(path)
|
11
|
+
when '.json'
|
12
|
+
load_json(path)
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @todo
|
19
|
+
def self.load_yaml(path)
|
20
|
+
new YAML.load_file(path)
|
21
|
+
rescue
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# @todo
|
26
|
+
def self.load_json(path)
|
27
|
+
new JSON.parse(IO.read(path), :symbolize_names => true)
|
28
|
+
rescue
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
4
32
|
# Returns a new keychain instance
|
5
33
|
# @return [Saper::Keychain]
|
6
|
-
def initialize
|
7
|
-
|
34
|
+
def initialize(services = {})
|
35
|
+
@services = Hash[services.map { |k,v| [k.to_s, v] }]
|
8
36
|
end
|
9
37
|
|
10
38
|
# Returns access credentials for the specified service.
|
11
39
|
# @param service [String, Symbol] service name
|
12
40
|
# @return [Hash, nil]
|
13
41
|
def [](service)
|
14
|
-
|
42
|
+
@services[service.to_s] || {}
|
43
|
+
end
|
44
|
+
|
45
|
+
# @todo
|
46
|
+
def login(service)
|
47
|
+
self[service][:login]
|
48
|
+
end
|
49
|
+
|
50
|
+
# @todo
|
51
|
+
def password(service)
|
52
|
+
self[service][:password]
|
53
|
+
end
|
54
|
+
|
55
|
+
# @todo
|
56
|
+
def token(service)
|
57
|
+
self[service][:token]
|
58
|
+
end
|
59
|
+
|
60
|
+
# @todo
|
61
|
+
def has?(service)
|
62
|
+
!self[service].empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
# @todo
|
66
|
+
def services
|
67
|
+
@services.keys.select { |service| has?(service) }
|
15
68
|
end
|
16
69
|
|
17
70
|
end
|
18
|
-
end
|
71
|
+
end
|
data/lib/saper/core/namespace.rb
CHANGED
@@ -5,14 +5,14 @@ module Saper
|
|
5
5
|
# @param recipe [Symbol] recipe name
|
6
6
|
# @param input [Object] input for recipe
|
7
7
|
# @return [Saper::Runtime]
|
8
|
-
def self.
|
9
|
-
load(file).run(recipe, input)
|
8
|
+
def self.run_from_file(file, recipe, input, opts = {})
|
9
|
+
load(file).run(recipe, input, opts)
|
10
10
|
end
|
11
11
|
|
12
12
|
# Parses recipes saved in a file.
|
13
13
|
# @param file [String] file name
|
14
14
|
# @return [Saper::Namespace]
|
15
|
-
def self.load(
|
15
|
+
def self.load(path)
|
16
16
|
Namespace.parse File.read(path)
|
17
17
|
end
|
18
18
|
|
@@ -64,18 +64,13 @@ module Saper
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
# @todo
|
68
|
-
def run_by_default(symbol = nil)
|
69
|
-
@default ||= symbol
|
70
|
-
end
|
71
|
-
|
72
67
|
# Runs an recipe and returns resulting Saper:Runtime.
|
73
68
|
# @param input [Object]
|
74
69
|
# @param name [String, Symbol] action name
|
75
70
|
# @param options [Hash] options for Runtime instance
|
76
71
|
# @return [Saper::Runtime]
|
77
72
|
def run(name = nil, input = nil, options ={})
|
78
|
-
self[name].run(input, options
|
73
|
+
self[name].run(input, options)
|
79
74
|
end
|
80
75
|
|
81
76
|
# Returns recipe with specified ID or fails silently if not found.
|
@@ -93,17 +88,19 @@ module Saper
|
|
93
88
|
# @param id [String, Symbol] recipe ID
|
94
89
|
# @return [Saper::Recipe]
|
95
90
|
def [](id)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
return value
|
100
|
-
end
|
101
|
-
if value.respond_to?(:to_recipe)
|
102
|
-
return @recipes[id.to_s] = value.to_recipe
|
103
|
-
end
|
91
|
+
id = id.to_s
|
92
|
+
if @recipes.key?(id)
|
93
|
+
value = @recipes[id]
|
104
94
|
else
|
105
|
-
find(id)
|
95
|
+
value = find(id)
|
106
96
|
end
|
97
|
+
if value.is_a?(Recipe)
|
98
|
+
return value
|
99
|
+
end
|
100
|
+
if value.respond_to?(:to_recipe)
|
101
|
+
return @recipes[id] = value.to_recipe
|
102
|
+
end
|
103
|
+
raise RecipeNotFound, id
|
107
104
|
end
|
108
105
|
|
109
106
|
# Caches recipe instance.
|
data/lib/saper/core/recipe.rb
CHANGED
@@ -11,7 +11,7 @@ module Saper
|
|
11
11
|
unless data.is_a?(Hash)
|
12
12
|
raise(InvalidRecipe, data)
|
13
13
|
end
|
14
|
-
new(data[:id], :name => data[:name]
|
14
|
+
new(data[:id], :name => data[:name]) do |recipe|
|
15
15
|
unless data[:actions].nil?
|
16
16
|
recipe.push *Action.unserialize(data[:actions], namespace)
|
17
17
|
end
|
@@ -52,12 +52,6 @@ module Saper
|
|
52
52
|
@options[:name].nil? ? nil : @options[:name].to_s
|
53
53
|
end
|
54
54
|
|
55
|
-
# Returns Saper::Namespace instance.
|
56
|
-
# @return [Namespace]
|
57
|
-
def namespace
|
58
|
-
@options[:namespace].is_a?(Namespace) ? @options[:namespace] : nil
|
59
|
-
end
|
60
|
-
|
61
55
|
# Returns a list of data types required as input.
|
62
56
|
# @return [Array<Symbol>]
|
63
57
|
def input_required
|
@@ -92,12 +86,17 @@ module Saper
|
|
92
86
|
self
|
93
87
|
end
|
94
88
|
|
95
|
-
# Runs recipe and returns
|
89
|
+
# Runs recipe and returns Result instance.
|
96
90
|
# @param input [object] input for the first action
|
97
|
-
# @param
|
98
|
-
# @return [Saper::
|
99
|
-
def run(input = nil,
|
100
|
-
|
91
|
+
# @param runtime [Runtime, Hash] Runtime instance or options for a new one
|
92
|
+
# @return [Saper::Result]
|
93
|
+
def run(input = nil, runtime = nil)
|
94
|
+
unless runtime.is_a?(Runtime)
|
95
|
+
runtime = Runtime.new({}, runtime || {})
|
96
|
+
end
|
97
|
+
stack = Stack.new(runtime, input)
|
98
|
+
enum = enum_for(:each_stack, stack)
|
99
|
+
Result.new(enum.to_a)
|
101
100
|
end
|
102
101
|
|
103
102
|
# Returns `true` if recipe contains no actions.
|
@@ -130,5 +129,47 @@ module Saper
|
|
130
129
|
serialize.to_json(*args)
|
131
130
|
end
|
132
131
|
|
132
|
+
private
|
133
|
+
|
134
|
+
# Yields resulting Stack instances.
|
135
|
+
# @param stack [Saper::Stack]
|
136
|
+
# @param index [Integer] action undex
|
137
|
+
# @yield [Saper::Stack]
|
138
|
+
# @return [nil]
|
139
|
+
def each_stack(stack, index = 0, &block)
|
140
|
+
if stack.error?
|
141
|
+
yield stack
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
if index >= actions.size
|
145
|
+
yield stack
|
146
|
+
return nil
|
147
|
+
end
|
148
|
+
each_action_result(stack, actions[index]) do |stack|
|
149
|
+
each_stack(stack, index + 1, &block)
|
150
|
+
end
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# Yields action results one-by-one.
|
155
|
+
# @param stack [Saper::Stack]
|
156
|
+
# @param action [Saper::Action]
|
157
|
+
# @yield [Saper::Item]
|
158
|
+
# @return [nil]
|
159
|
+
def each_action_result(stack, action)
|
160
|
+
begin
|
161
|
+
results = action.run(stack.last, stack.runtime)
|
162
|
+
rescue Saper::Error => error
|
163
|
+
results = error
|
164
|
+
end
|
165
|
+
unless results.is_a?(Array)
|
166
|
+
yield stack.add(results); return nil
|
167
|
+
end
|
168
|
+
results.each do |result|
|
169
|
+
yield stack.copy.add(result)
|
170
|
+
end
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
133
174
|
end
|
134
175
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Saper
|
2
|
+
class Result
|
3
|
+
|
4
|
+
# @todo
|
5
|
+
attr_reader :stacks
|
6
|
+
|
7
|
+
# @todo
|
8
|
+
def initialize(stacks = [])
|
9
|
+
@stacks = stacks.to_a
|
10
|
+
if block_given?
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# @todo
|
16
|
+
def success?
|
17
|
+
not failure?
|
18
|
+
end
|
19
|
+
|
20
|
+
# @todo
|
21
|
+
def failure?
|
22
|
+
stacks.all?(&:error?)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @todo
|
26
|
+
def size
|
27
|
+
stacks.size
|
28
|
+
end
|
29
|
+
|
30
|
+
# @todo
|
31
|
+
def multiple?
|
32
|
+
size > 1
|
33
|
+
end
|
34
|
+
|
35
|
+
# @todo
|
36
|
+
def to_saper_array
|
37
|
+
stacks.map(&:result)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @todo
|
41
|
+
def to_native_array
|
42
|
+
to_saper_array.map(&:to_native)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @todo
|
46
|
+
def to_serialized_array
|
47
|
+
to_saper_array.map(&:serialize)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @todo
|
51
|
+
def to_saper
|
52
|
+
multiple? ? to_saper_array : to_saper_array.first
|
53
|
+
end
|
54
|
+
|
55
|
+
# @todo
|
56
|
+
def to_native
|
57
|
+
multiple? ? to_native_array : to_native_array.first
|
58
|
+
end
|
59
|
+
|
60
|
+
# @todo
|
61
|
+
def serialize
|
62
|
+
multiple? ? to_serialized_array : to_serialized_array.first
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns runtime results as JSON.
|
66
|
+
# @return [Array]
|
67
|
+
def to_json(*args)
|
68
|
+
JSON.dump(serialize)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|