saper 0.5.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +126 -0
- data/Rakefile +17 -0
- data/bin/saper +60 -0
- data/lib/lib/json_search.rb +54 -0
- data/lib/lib/mechanize.rb +26 -0
- data/lib/lib/nokogiri.rb +12 -0
- data/lib/saper.rb +37 -0
- data/lib/saper/actions/append_with.rb +14 -0
- data/lib/saper/actions/convert_to_html.rb +14 -0
- data/lib/saper/actions/convert_to_json.rb +14 -0
- data/lib/saper/actions/convert_to_markdown.rb +13 -0
- data/lib/saper/actions/convert_to_time.rb +15 -0
- data/lib/saper/actions/convert_to_xml.rb +14 -0
- data/lib/saper/actions/create_atom.rb +18 -0
- data/lib/saper/actions/fetch.rb +17 -0
- data/lib/saper/actions/find.rb +18 -0
- data/lib/saper/actions/find_first.rb +16 -0
- data/lib/saper/actions/get_attribute.rb +15 -0
- data/lib/saper/actions/get_contents.rb +14 -0
- data/lib/saper/actions/get_text.rb +14 -0
- data/lib/saper/actions/prepend_with.rb +14 -0
- data/lib/saper/actions/remove_after.rb +14 -0
- data/lib/saper/actions/remove_before.rb +14 -0
- data/lib/saper/actions/remove_matching.rb +14 -0
- data/lib/saper/actions/remove_tags.rb +15 -0
- data/lib/saper/actions/replace.rb +15 -0
- data/lib/saper/actions/run_recipe.rb +24 -0
- data/lib/saper/actions/run_recipe_and_save.rb +22 -0
- data/lib/saper/actions/save.rb +14 -0
- data/lib/saper/actions/select_matching.rb +14 -0
- data/lib/saper/actions/set_input.rb +19 -0
- data/lib/saper/actions/skip_tags.rb +15 -0
- data/lib/saper/actions/split.rb +24 -0
- data/lib/saper/arguments/attribute.rb +11 -0
- data/lib/saper/arguments/recipe.rb +42 -0
- data/lib/saper/arguments/text.rb +11 -0
- data/lib/saper/arguments/timezone.rb +11 -0
- data/lib/saper/arguments/variable.rb +11 -0
- data/lib/saper/arguments/xpath.rb +11 -0
- data/lib/saper/core/action.rb +209 -0
- data/lib/saper/core/argument.rb +106 -0
- data/lib/saper/core/browser.rb +87 -0
- data/lib/saper/core/dsl.rb +68 -0
- data/lib/saper/core/error.rb +47 -0
- data/lib/saper/core/item.rb +70 -0
- data/lib/saper/core/keychain.rb +18 -0
- data/lib/saper/core/logger.rb +74 -0
- data/lib/saper/core/namespace.rb +139 -0
- data/lib/saper/core/recipe.rb +134 -0
- data/lib/saper/core/runtime.rb +237 -0
- data/lib/saper/core/type.rb +45 -0
- data/lib/saper/items/atom.rb +64 -0
- data/lib/saper/items/document.rb +66 -0
- data/lib/saper/items/html.rb +85 -0
- data/lib/saper/items/json.rb +67 -0
- data/lib/saper/items/markdown.rb +36 -0
- data/lib/saper/items/nothing.rb +15 -0
- data/lib/saper/items/text.rb +54 -0
- data/lib/saper/items/time.rb +42 -0
- data/lib/saper/items/url.rb +34 -0
- data/lib/saper/items/xml.rb +79 -0
- data/lib/saper/version.rb +3 -0
- data/spec/actions/append_with_spec.rb +30 -0
- data/spec/actions/convert_to_html_spec.rb +24 -0
- data/spec/actions/convert_to_json_spec.rb +24 -0
- data/spec/actions/convert_to_markdown_spec.rb +24 -0
- data/spec/actions/convert_to_time_spec.rb +37 -0
- data/spec/actions/convert_to_xml_spec.rb +24 -0
- data/spec/actions/create_atom_spec.rb +31 -0
- data/spec/actions/fetch_spec.rb +7 -0
- data/spec/actions/find_first_spec.rb +7 -0
- data/spec/actions/find_spec.rb +7 -0
- data/spec/actions/get_attribute_spec.rb +7 -0
- data/spec/actions/get_contents.rb +7 -0
- data/spec/actions/get_text.rb +7 -0
- data/spec/actions/prepend_with_spec.rb +30 -0
- data/spec/actions/remove_after.rb +7 -0
- data/spec/actions/remove_before.rb +7 -0
- data/spec/actions/replace_spec.rb +7 -0
- data/spec/actions/run_recipe_and_save_spec.tmp.rb +52 -0
- data/spec/actions/run_recipe_spec.tmp.rb +53 -0
- data/spec/actions/save_spec.rb +7 -0
- data/spec/actions/select_matching_spec.rb +7 -0
- data/spec/actions/set_input_spec.rb +7 -0
- data/spec/actions/skip_tags_spec.rb +7 -0
- data/spec/actions/split_spec.rb +7 -0
- data/spec/core/action_spec.rb +151 -0
- data/spec/core/argument_spec.rb +79 -0
- data/spec/core/browser_spec.rb +7 -0
- data/spec/core/dsl_spec.rb +7 -0
- data/spec/core/item_spec.rb +7 -0
- data/spec/core/keychain_spec.rb +7 -0
- data/spec/core/logger_spec.rb +7 -0
- data/spec/core/namespace_spec.rb +18 -0
- data/spec/core/recipe_spec.rb +81 -0
- data/spec/core/runtime_spec.rb +165 -0
- data/spec/core/type_spec.rb +7 -0
- data/spec/items/atom_spec.rb +7 -0
- data/spec/items/document_spec.rb +7 -0
- data/spec/items/html_spec.rb +7 -0
- data/spec/items/json_spec.rb +7 -0
- data/spec/items/markdown_spec.rb +7 -0
- data/spec/items/nothing_spec.rb +7 -0
- data/spec/items/text_spec.rb +17 -0
- data/spec/items/time_spec.rb +7 -0
- data/spec/items/url_spec.rb +7 -0
- data/spec/items/xml_spec.rb +17 -0
- data/spec/spec_helper.rb +22 -0
- metadata +355 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Saper
|
2
|
+
|
3
|
+
# Generic exception acting as a parent class for all exceptions in Saper library.
|
4
|
+
class Error < Exception; end
|
5
|
+
|
6
|
+
# FileUnreadable is raised whenever a recipe file cannot be read.
|
7
|
+
class FileUnreadable < Error; end
|
8
|
+
|
9
|
+
# NoAction is raised whenever an implementation of Saper::Action is not found.
|
10
|
+
class ActionNotFound < Error; end
|
11
|
+
|
12
|
+
# NamespaceMissing is raised whenever Saper::Runtime requires a missing namespace.
|
13
|
+
class NamespaceMissing < Error; end
|
14
|
+
|
15
|
+
# NoAction is raised whenever an unsupported object is added to Saper::Recipe.
|
16
|
+
class ActionExpected < Error; end
|
17
|
+
|
18
|
+
# InvalidAction is raised whenever serialized representation of Saper::Action is invalid.
|
19
|
+
class InvalidAction < Error; end
|
20
|
+
|
21
|
+
# InvalidAction is raised whenever serialized representation of Saper::Recipe is invalid.
|
22
|
+
class InvalidRecipe < Error; end
|
23
|
+
|
24
|
+
# InvalidNamespace is raised whenever serialized representation of Saper::Namespace is invalid.
|
25
|
+
class InvalidNamespace < Error; end
|
26
|
+
|
27
|
+
# InvalidType is raised whenever argument type is not recognized.
|
28
|
+
class InvalidType < Error; end
|
29
|
+
|
30
|
+
# InvalidArgument is raised whenever an argument has incorrect type or value.
|
31
|
+
class InvalidArgument < Error; end
|
32
|
+
|
33
|
+
# InvalidInput is raised whenever Saper::Action does not support this type of input.
|
34
|
+
class InvalidInput < Error; end
|
35
|
+
|
36
|
+
# RecipeNotFound is raised whenever action calls a recipe that cannot be found.
|
37
|
+
class RecipeNotFound < Error; end
|
38
|
+
|
39
|
+
# UnreachableURL is raised whenever a connection error occurs.
|
40
|
+
class UnreachableURL < Error; end
|
41
|
+
|
42
|
+
# InvalidItem is raised whenever Saper::Item is initialized with an invalid underlying.
|
43
|
+
class InvalidItem < Error; end
|
44
|
+
|
45
|
+
# @todo
|
46
|
+
class RuntimeMissing < Error; end
|
47
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Saper
|
2
|
+
class Item
|
3
|
+
|
4
|
+
# Tracks subclasses of Saper::Argument.
|
5
|
+
# @return [Class]
|
6
|
+
def self.inherited(base)
|
7
|
+
subclasses[base.type] = base
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a hash of subclasses.
|
11
|
+
# @return [Hash]
|
12
|
+
def self.subclasses
|
13
|
+
@subclasses ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns class name as an underscored string.
|
17
|
+
# @return [String]
|
18
|
+
def self.type
|
19
|
+
name.split("::").last.gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a subclass with specified type.
|
23
|
+
# @param type [Symbol] action type
|
24
|
+
# @return [Saper::Argument]
|
25
|
+
def self.[](type)
|
26
|
+
subclasses[type.to_s] || raise(InvalidItem, "Invalid action argument: %s" % type)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns `true` if there is a subclass with specified type.
|
30
|
+
# @param type [Symbol] action type
|
31
|
+
# @return [Boolean]
|
32
|
+
def self.exists?(type)
|
33
|
+
subclasses.keys.include?(type.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a new instance of Saper::Argument. Returns nil and fails
|
37
|
+
# silently if there is an error during initialization.
|
38
|
+
# @return [Saper::Argument]
|
39
|
+
def self.try(*args, &block)
|
40
|
+
begin
|
41
|
+
new(*args, &block)
|
42
|
+
rescue InvalidItem, ArgumentError
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a new instance of Saper::Argument.
|
48
|
+
# @return [Saper::Argument]
|
49
|
+
def self.new(*args, &block)
|
50
|
+
if self == Item
|
51
|
+
self[args.shift].new(*args, &block)
|
52
|
+
else
|
53
|
+
super(*args, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns `true` if other item contains the same data.
|
58
|
+
# @return [Boolean]
|
59
|
+
def ==(other)
|
60
|
+
to_s == other.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns class name as a Symbol.
|
64
|
+
# @return [Symbol]
|
65
|
+
def type
|
66
|
+
self.class.type.to_sym
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Saper
|
2
|
+
class Keychain
|
3
|
+
|
4
|
+
# Returns a new keychain instance
|
5
|
+
# @return [Saper::Keychain]
|
6
|
+
def initialize
|
7
|
+
# TODO: not yet implemented
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns access credentials for the specified service.
|
11
|
+
# @param service [String, Symbol] service name
|
12
|
+
# @return [Hash, nil]
|
13
|
+
def [](service)
|
14
|
+
# TODO: not yet implemented
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Saper
|
2
|
+
class Logger
|
3
|
+
|
4
|
+
# TODO: requires serious overhaul
|
5
|
+
|
6
|
+
attr_reader :io
|
7
|
+
|
8
|
+
def initialize(io = nil)
|
9
|
+
@io = io || StringIO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def download(url)
|
13
|
+
io.write "%s %s\r\n" % [bold("Downloading"), url]
|
14
|
+
end
|
15
|
+
|
16
|
+
def runtime(instance, indent = 0)
|
17
|
+
io.write bold("Recipe tree\r\n") if indent == 0
|
18
|
+
io.write "%s%s\r\n" % [" " * indent, action(instance)]
|
19
|
+
instance.children.each do |child|
|
20
|
+
runtime(child, indent + 1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def bold(string)
|
25
|
+
"\033[1m%s\033[0m" % string
|
26
|
+
end
|
27
|
+
|
28
|
+
def red(string)
|
29
|
+
"\033[31m%s\033[0m" % string
|
30
|
+
end
|
31
|
+
|
32
|
+
def green(string)
|
33
|
+
"\033[32m%s\033[0m" % string
|
34
|
+
end
|
35
|
+
|
36
|
+
def action(instance)
|
37
|
+
"%s %s > %s" % [action_name(instance), action_input(instance), action_output(instance)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def action_input(instance)
|
41
|
+
item(instance.input)
|
42
|
+
end
|
43
|
+
|
44
|
+
def action_output(instance)
|
45
|
+
item(instance.action.multiple? ? instance.output : instance.output.first)
|
46
|
+
end
|
47
|
+
|
48
|
+
def action_name(instance)
|
49
|
+
instance.error? ? red(instance.action.name) : green(instance.action.name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def item(instance)
|
53
|
+
case instance
|
54
|
+
when Array
|
55
|
+
"[%s]" % instance.map { |i| item(i) }.join(",")
|
56
|
+
when nil
|
57
|
+
'nil'
|
58
|
+
when Items::Document
|
59
|
+
'Document'
|
60
|
+
when Items::HTML
|
61
|
+
'HTML'
|
62
|
+
when String
|
63
|
+
'%s' % instance
|
64
|
+
when Items::Text
|
65
|
+
'%s' % instance.to_s
|
66
|
+
when Items::Atom
|
67
|
+
'%s' % instance.to_hash
|
68
|
+
else
|
69
|
+
instance.class
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Saper
|
2
|
+
|
3
|
+
def run(file, recipe, input = nil)
|
4
|
+
load(file).run(recipe, input)
|
5
|
+
end
|
6
|
+
|
7
|
+
def load(file)
|
8
|
+
parse File.read(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Namespace is a parsing / storage utility for recipes
|
12
|
+
# that were meant to work as a group. It must be initiated
|
13
|
+
# for some actions to work properly (e.g. `run_chain`).
|
14
|
+
class Namespace
|
15
|
+
|
16
|
+
# Parses namespace specified as a string or a block.
|
17
|
+
# @param string [String] list of recipes
|
18
|
+
# @return [Saper::Namespace]
|
19
|
+
def self.parse(string = nil, &block)
|
20
|
+
instance = DSL.new
|
21
|
+
if string.is_a?(String)
|
22
|
+
instance.instance_eval(string)
|
23
|
+
end
|
24
|
+
if block_given?
|
25
|
+
instance.instance_eval(&block)
|
26
|
+
end
|
27
|
+
instance.namespace
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns a new instance of Saper::Namespace
|
31
|
+
# @param data [Hash]
|
32
|
+
# @return [Saper::Namespace]
|
33
|
+
def self.unserialize(data, &block)
|
34
|
+
unless data.is_a?(Hash)
|
35
|
+
raise(InvalidNamespace, data)
|
36
|
+
end
|
37
|
+
unless data[:recipes].is_a?(Array)
|
38
|
+
raise(InvalidNamespace, data)
|
39
|
+
end
|
40
|
+
new do |namespace|
|
41
|
+
data[:recipes].each do |recipe|
|
42
|
+
namespace << Recipe.unserialize(recipe, namespace)
|
43
|
+
end
|
44
|
+
if block_given?
|
45
|
+
yield namespace
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a new Namespace instance
|
51
|
+
# @return [Saper::Namespace]
|
52
|
+
def initialize
|
53
|
+
@recipes = {}
|
54
|
+
if block_given?
|
55
|
+
yield self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @todo
|
60
|
+
def run_by_default(symbol = nil)
|
61
|
+
@default ||= symbol
|
62
|
+
end
|
63
|
+
|
64
|
+
# Runs an recipe and returns resulting Saper:Runtime.
|
65
|
+
# @param input [Object]
|
66
|
+
# @param name [String, Symbol] action name
|
67
|
+
# @param options [Hash] options for Runtime instance
|
68
|
+
# @return [Saper::Runtime]
|
69
|
+
def run(name = nil, input = nil, options ={})
|
70
|
+
self[name].run(input, options.merge(:namespace => self))
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns recipe with specified ID or fails silently if not found.
|
74
|
+
# @param id [String, Symbol] recipe ID
|
75
|
+
# @return [Saper::Recipe, nil]
|
76
|
+
def try(id)
|
77
|
+
begin
|
78
|
+
self[id]
|
79
|
+
rescue
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns recipe with specified ID.
|
85
|
+
# @param id [String, Symbol] recipe ID
|
86
|
+
# @return [Saper::Recipe]
|
87
|
+
def [](id)
|
88
|
+
if @recipes.key?(id.to_s)
|
89
|
+
value = @recipes[id.to_s]
|
90
|
+
if value.is_a?(Recipe)
|
91
|
+
return value
|
92
|
+
end
|
93
|
+
if value.respond_to?(:to_recipe)
|
94
|
+
return @recipes[id.to_s] = value.to_recipe
|
95
|
+
end
|
96
|
+
else
|
97
|
+
find(id)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Caches recipe instance.
|
102
|
+
# @param id [String, Symbol] recipe ID
|
103
|
+
# @param recipe [Saper::Recipe]
|
104
|
+
# @return [Saper::Recipe]
|
105
|
+
def []=(id, recipe)
|
106
|
+
unless recipe.is_a?(Saper::Recipe)
|
107
|
+
unless recipe.respond_to?(:to_recipe)
|
108
|
+
raise ActionExpected
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@recipes[id.to_s] = recipe
|
112
|
+
end
|
113
|
+
|
114
|
+
# Searches for and returns a recipe.
|
115
|
+
# @return [Saper::Recipe]
|
116
|
+
def find(id)
|
117
|
+
raise RecipeNotFound, id
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the number of recipes within this namespace.
|
121
|
+
# @return [Integer]
|
122
|
+
def size
|
123
|
+
@recipes.size
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns a serialized representation of this Namespace.
|
127
|
+
# @return [Hash]
|
128
|
+
def serialize
|
129
|
+
{ :recipes => @recipes.values.map(&:serialize) }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns a JSON representation of this recipe.
|
133
|
+
# @return [String]
|
134
|
+
def to_json(*args)
|
135
|
+
serialize.to_json(*args)
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Saper
|
2
|
+
class Recipe
|
3
|
+
|
4
|
+
# Returns a new instance of Saper::Recipe
|
5
|
+
# @param data [Hash]
|
6
|
+
# @return [Saper::Recipe]
|
7
|
+
def self.unserialize(data, namespace = nil, &block)
|
8
|
+
if data.is_a?(Array)
|
9
|
+
return data.map { |item| unserialize(item, namespace) }
|
10
|
+
end
|
11
|
+
unless data.is_a?(Hash)
|
12
|
+
raise(InvalidRecipe, data)
|
13
|
+
end
|
14
|
+
new(data[:id], :name => data[:name], :namespace => namespace) do |recipe|
|
15
|
+
unless data[:actions].nil?
|
16
|
+
recipe.push *Action.unserialize(data[:actions], namespace)
|
17
|
+
end
|
18
|
+
if block_given?
|
19
|
+
yield recipe
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parses block and returns a Recipe instance.
|
25
|
+
# @param id [Symbol] recipe ID
|
26
|
+
# @return [Saper::Recipe]
|
27
|
+
def self.parse(id = nil, name = nil, &block)
|
28
|
+
DSL::Recipe.parse(id, name, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns unique ID of the recipe.
|
32
|
+
attr_reader :id
|
33
|
+
|
34
|
+
# Returns list of recipe actions.
|
35
|
+
attr_reader :actions
|
36
|
+
|
37
|
+
# Returns a new instance of Saper::Recipe
|
38
|
+
# @param id [String]
|
39
|
+
# @return [Saper::Recipe]
|
40
|
+
def initialize(id = nil, options = {})
|
41
|
+
@id = id || SecureRandom.hex
|
42
|
+
@actions = []
|
43
|
+
@options = options
|
44
|
+
if block_given?
|
45
|
+
yield self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns recipe name.
|
50
|
+
# @return [String, nil]
|
51
|
+
def name
|
52
|
+
@options[:name].nil? ? nil : @options[:name].to_s
|
53
|
+
end
|
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
|
+
# Returns a list of data types required as input.
|
62
|
+
# @return [Array<Symbol>]
|
63
|
+
def input_required
|
64
|
+
empty? ? [] : actions.first.requires
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns `true` if recipe requires some input.
|
68
|
+
# @return [Boolean]
|
69
|
+
def input_required?
|
70
|
+
!input_required.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds a new action to the end of the recipe and returns self.
|
74
|
+
# @param action [Saper::Action]
|
75
|
+
# @return [self]
|
76
|
+
def <<(action)
|
77
|
+
if action.is_a?(Action)
|
78
|
+
@actions.push(action)
|
79
|
+
else
|
80
|
+
raise ActionExpected, action
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds new actions to the end of the recipe and returns self.
|
86
|
+
# @param actions [Array<Saper::Action>]
|
87
|
+
# @return [self]
|
88
|
+
def push(*actions)
|
89
|
+
actions.compact.each do |action|
|
90
|
+
self << action
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# Runs recipe and returns Runtime instance.
|
96
|
+
# @param input [object] input for the first action
|
97
|
+
# @param options [Hash] options for Runtime object
|
98
|
+
# @return [Saper::Runtime]
|
99
|
+
def run(input = nil, options = {})
|
100
|
+
Runtime.new(actions, input, { :namespace => namespace }.merge(options))
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns `true` if recipe contains no actions.
|
104
|
+
# @return [Boolean]
|
105
|
+
def empty?
|
106
|
+
actions.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns `true` if recipe produces multiple results.
|
110
|
+
# @return [Boolean]
|
111
|
+
def multiple?
|
112
|
+
empty? ? false : actions.any?(&:multiple?)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns a serialized representation of this Recipe.
|
116
|
+
# @return [Hash]
|
117
|
+
def serialize
|
118
|
+
{ :id => id, :name => name, :actions => @actions.map(&:serialize) }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns a string representation of this recipe.
|
122
|
+
# @return [String]
|
123
|
+
def to_string
|
124
|
+
"recipe %s do\n%s\nend" % [id.inspect, actions.map(&:to_string).join("\n")]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a JSON representation of this recipe.
|
128
|
+
# @return [String]
|
129
|
+
def to_json(*args)
|
130
|
+
serialize.to_json(*args)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|