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