saper 0.5.1 → 0.5.2

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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/Rakefile +6 -6
  4. data/bin/saper +17 -36
  5. data/lib/saper.rb +63 -20
  6. data/lib/saper/actions/append_with.rb +1 -1
  7. data/lib/saper/actions/convert_to_html.rb +1 -1
  8. data/lib/saper/actions/convert_to_json.rb +6 -2
  9. data/lib/saper/actions/convert_to_markdown.rb +1 -1
  10. data/lib/saper/actions/convert_to_time.rb +38 -1
  11. data/lib/saper/actions/convert_to_xml.rb +1 -1
  12. data/lib/saper/actions/create_atom.rb +6 -2
  13. data/lib/saper/actions/fetch.rb +3 -2
  14. data/lib/saper/actions/fetch_with_token.rb +18 -0
  15. data/lib/saper/actions/find.rb +1 -1
  16. data/lib/saper/actions/find_first.rb +1 -1
  17. data/lib/saper/actions/get_attribute.rb +7 -2
  18. data/lib/saper/actions/get_contents.rb +1 -1
  19. data/lib/saper/actions/get_text.rb +1 -1
  20. data/lib/saper/actions/nothing.rb +11 -0
  21. data/lib/saper/actions/prepend_with.rb +2 -2
  22. data/lib/saper/actions/remove_after.rb +2 -2
  23. data/lib/saper/actions/remove_before.rb +2 -2
  24. data/lib/saper/actions/remove_matching.rb +2 -2
  25. data/lib/saper/actions/remove_tags.rb +1 -1
  26. data/lib/saper/actions/replace.rb +1 -1
  27. data/lib/saper/actions/run_recipe.rb +2 -2
  28. data/lib/saper/actions/run_recipe_and_save.rb +3 -3
  29. data/lib/saper/actions/save.rb +2 -2
  30. data/lib/saper/actions/select_matching.rb +2 -2
  31. data/lib/saper/actions/set_input.rb +1 -1
  32. data/lib/saper/actions/skip_tags.rb +1 -1
  33. data/lib/saper/actions/split.rb +1 -1
  34. data/lib/saper/arguments/attribute.rb +6 -0
  35. data/lib/saper/arguments/recipe.rb +12 -6
  36. data/lib/saper/arguments/service.rb +12 -0
  37. data/lib/saper/arguments/text.rb +1 -0
  38. data/lib/saper/arguments/timezone.rb +2 -1
  39. data/lib/saper/arguments/url.rb +12 -0
  40. data/lib/saper/arguments/variable.rb +2 -1
  41. data/lib/saper/arguments/xpath.rb +2 -1
  42. data/lib/saper/core/action.rb +43 -13
  43. data/lib/saper/core/argument.rb +30 -14
  44. data/lib/saper/core/browser.rb +29 -28
  45. data/lib/saper/core/dsl.rb +14 -16
  46. data/lib/saper/core/error.rb +1 -1
  47. data/lib/saper/core/item.rb +2 -2
  48. data/lib/saper/core/keychain.rb +58 -5
  49. data/lib/saper/core/namespace.rb +15 -18
  50. data/lib/saper/core/recipe.rb +53 -12
  51. data/lib/saper/core/result.rb +72 -0
  52. data/lib/saper/core/runtime.rb +44 -177
  53. data/lib/saper/core/stack.rb +57 -0
  54. data/lib/saper/items/atom.rb +18 -1
  55. data/lib/saper/items/document.rb +39 -19
  56. data/lib/saper/items/html.rb +52 -7
  57. data/lib/saper/items/json.rb +19 -1
  58. data/lib/saper/items/markdown.rb +14 -4
  59. data/lib/saper/items/nothing.rb +6 -0
  60. data/lib/saper/items/text.rb +28 -3
  61. data/lib/saper/items/time.rb +25 -2
  62. data/lib/saper/items/url.rb +8 -2
  63. data/lib/saper/items/xml.rb +51 -11
  64. data/lib/{lib → saper/patches}/mechanize.rb +0 -0
  65. data/lib/{lib → saper/patches}/nokogiri.rb +0 -0
  66. data/lib/saper/version.rb +2 -2
  67. data/spec/actions/append_with_spec.rb +29 -52
  68. data/spec/actions/convert_to_html_spec.rb +13 -30
  69. data/spec/actions/convert_to_json_spec.rb +13 -30
  70. data/spec/actions/convert_to_markdown_spec.rb +17 -19
  71. data/spec/actions/convert_to_time_spec.rb +25 -43
  72. data/spec/actions/convert_to_xml_spec.rb +15 -11
  73. data/spec/actions/create_atom_spec.rb +11 -19
  74. data/spec/actions/fetch_spec.rb +3 -8
  75. data/spec/actions/find_first_spec.rb +38 -40
  76. data/spec/actions/find_spec.rb +23 -39
  77. data/spec/actions/get_attribute_spec.rb +30 -3
  78. data/spec/actions/{get_contents.rb → get_contents_spec.rb} +0 -0
  79. data/spec/actions/{get_text.rb → get_text_spec.rb} +0 -0
  80. data/spec/actions/nothing_spec.rb +7 -0
  81. data/spec/actions/prepend_with_spec.rb +31 -18
  82. data/spec/actions/{remove_after.rb → remove_after_spec.rb} +0 -0
  83. data/spec/actions/{remove_before.rb → remove_before_spec.rb} +0 -0
  84. data/spec/actions/remove_matching_spec.rb +7 -0
  85. data/spec/actions/remove_tags_spec.rb +7 -0
  86. data/spec/actions/run_recipe_and_save_spec.rb +7 -0
  87. data/spec/actions/run_recipe_spec.rb +7 -0
  88. data/spec/arguments/attribute_spec.rb +7 -0
  89. data/spec/arguments/recipe_spec.rb +7 -0
  90. data/spec/arguments/text_spec.rb +7 -0
  91. data/spec/arguments/timezone_spec.rb +7 -0
  92. data/spec/arguments/variable_spec.rb +7 -0
  93. data/spec/arguments/xpath_spec.rb +7 -0
  94. data/spec/core/action_spec.rb +13 -142
  95. data/spec/core/argument_spec.rb +38 -71
  96. data/spec/core/browser_spec.rb +18 -3
  97. data/spec/core/dsl_spec.rb +1 -1
  98. data/spec/core/item_spec.rb +1 -1
  99. data/spec/core/namespace_spec.rb +0 -11
  100. data/spec/core/recipe_spec.rb +54 -77
  101. data/spec/core/result_spec.rb +7 -0
  102. data/spec/core/runtime_spec.rb +20 -151
  103. data/spec/core/stack_spec.rb +7 -0
  104. data/spec/integration/simple_invalid_spec.rb +39 -0
  105. data/spec/integration/simple_valid_spec.rb +39 -0
  106. data/spec/items/atom_spec.rb +6 -1
  107. data/spec/items/document_spec.rb +93 -15
  108. data/spec/items/html_spec.rb +45 -28
  109. data/spec/items/json_spec.rb +10 -10
  110. data/spec/items/markdown_spec.rb +24 -3
  111. data/spec/items/nothing_spec.rb +1 -1
  112. data/spec/items/text_spec.rb +13 -41
  113. data/spec/items/time_spec.rb +25 -4
  114. data/spec/items/url_spec.rb +1 -7
  115. data/spec/items/xml_spec.rb +46 -39
  116. data/spec/spec_helper.rb +2 -21
  117. metadata +63 -60
  118. data/lib/lib/json_search.rb +0 -54
  119. data/spec/actions/run_recipe_and_save_spec.tmp.rb +0 -52
  120. data/spec/actions/run_recipe_spec.tmp.rb +0 -53
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :text
6
6
  accepts :text, :returns => :text
7
7
 
8
- run do |input, string|
9
- "%s%s" % [string, input]
8
+ run do |runtime, input, string|
9
+ Items::Text.new "%s%s" % [string, input]
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :text
6
6
  accepts :text, :returns => :text
7
7
 
8
- run do |input, separator|
9
- input.split(separator, 2).first
8
+ run do |runtime, input, separator|
9
+ Items::Text.new input.split(separator, 2).first
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :text
6
6
  accepts :text, :returns => :text
7
7
 
8
- run do |input, separator|
9
- input.split(separator, 2).last
8
+ run do |runtime, input, separator|
9
+ Items::Text.new input.split(separator, 2).last
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :text
6
6
  accepts :text, :returns => :text
7
7
 
8
- run do |input, pattern|
9
- input.to_s =~ Regexp.new(pattern) ? nil : input
8
+ run do |runtime, input, pattern|
9
+ input.to_s =~ Regexp.new(pattern) ? Items::Nothing.new : input
10
10
  end
11
11
 
12
12
  end
@@ -6,7 +6,7 @@ module Saper
6
6
  accepts :xml, :returns => :xml
7
7
  accepts :html, :returns => :html
8
8
 
9
- run do |input, xpath|
9
+ run do |runtime, input, xpath|
10
10
  input.remove_children_with_content(xpath)
11
11
  end
12
12
 
@@ -6,7 +6,7 @@ module Saper
6
6
  argument :text
7
7
  accepts :text, :returns => :text
8
8
 
9
- run do |input, what, with|
9
+ run do |runtime, input, what, with|
10
10
  input.gsub(what, with)
11
11
  end
12
12
 
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :recipe
6
6
  accepts :anything, :returns => Proc.new { |instance| nil } # TODO
7
7
 
8
- run do |input, subrecipe|
9
- recipe(subrecipe, input)
8
+ run do |runtime, input, subrecipe|
9
+ subrecipe.run(input).to_saper
10
10
  end
11
11
 
12
12
  # Overrides default logic
@@ -6,9 +6,9 @@ module Saper
6
6
  argument :recipe
7
7
  accepts :anything
8
8
 
9
- run do |input, variable, subrecipe|
10
- result = recipe(subrecipe, input)
11
- self[variable] = result unless result.nil?
9
+ run do |runtime, input, variable, subrecipe|
10
+ result = subrecipe.run(input).to_saper
11
+ runtime[variable] = result unless result.nil?
12
12
  input
13
13
  end
14
14
 
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :variable
6
6
  accepts :anything
7
7
 
8
- run do |input, variable|
9
- self[variable] = input
8
+ run do |runtime, input, variable|
9
+ runtime[variable] = input
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Saper
5
5
  argument :text
6
6
  accepts :text, :returns => :text
7
7
 
8
- run do |input, pattern|
9
- input.to_s =~ Regexp.new(pattern) ? input : nil
8
+ run do |runtime, input, pattern|
9
+ input.to_s =~ Regexp.new(pattern) ? input : Items::Nothing.new
10
10
  end
11
11
 
12
12
  end
@@ -5,7 +5,7 @@ module Saper
5
5
  argument :text
6
6
  accepts :anything, :returns => :text
7
7
 
8
- run do |input, string|
8
+ run do |runtime, input, string|
9
9
  string
10
10
  end
11
11
 
@@ -6,7 +6,7 @@ module Saper
6
6
  accepts :xml, :returns => :xml
7
7
  accepts :html, :returns => :html
8
8
 
9
- run do |input, xpath|
9
+ run do |runtime, input, xpath|
10
10
  input.remove_children_preserving_content(xpath)
11
11
  end
12
12
 
@@ -8,7 +8,7 @@ module Saper
8
8
  accepts :document, :returns => :text
9
9
  accepts :html, :returns => :html
10
10
 
11
- run do |input, separator|
11
+ run do |runtime, input, separator|
12
12
  case input
13
13
  when Items::HTML
14
14
  input.inner_html.split(separator).map { |string| Item.new(:text, string).to_html }
@@ -2,10 +2,16 @@ module Saper
2
2
  module Arguments
3
3
  class Attribute < Argument
4
4
 
5
+ # @todo
5
6
  def valid?(value)
6
7
  value.is_a?(Symbol) || value.is_a?(String)
7
8
  end
8
9
 
10
+ # @todo
11
+ def normalize(value)
12
+ value.to_s
13
+ end
14
+
9
15
  end
10
16
  end
11
17
  end
@@ -1,19 +1,24 @@
1
1
  module Saper
2
2
  module Arguments
3
3
  class Recipe < Argument
4
-
4
+
5
+ # @todo
5
6
  def valid?(value)
6
- value.is_a?(Symbol) || value.is_a?(String)
7
+ value.is_a?(Symbol) || value.is_a?(String) || value.is_a?(Numeric)
7
8
  end
8
9
 
10
+ # @todo
9
11
  def normalize(value)
10
12
  if value.is_a?(Recipe)
11
13
  return value
12
14
  end
13
- if value.is_a?(String)
14
- value = value.to_sym
15
+ if value.is_a?(Symbol)
16
+ value = value.to_s
17
+ end
18
+ if value.is_a?(Numeric)
19
+ value = value.to_s
15
20
  end
16
- unless value.is_a?(Symbol)
21
+ unless value.is_a?(String)
17
22
  raise InvalidArgument
18
23
  end
19
24
  if action.nil?
@@ -28,11 +33,12 @@ module Saper
28
33
  value
29
34
  end
30
35
 
31
- #
36
+ # @todo
32
37
  def serialize
33
38
  value.id
34
39
  end
35
40
 
41
+ # @todo
36
42
  def to_string
37
43
  value.id.inspect
38
44
  end
@@ -0,0 +1,12 @@
1
+ module Saper
2
+ module Arguments
3
+ class Service < Argument
4
+
5
+ # @todo
6
+ def valid?(value)
7
+ value.is_a?(Symbol) || value.is_a?(String)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -2,6 +2,7 @@ module Saper
2
2
  module Arguments
3
3
  class Text < Argument
4
4
 
5
+ # @todo
5
6
  def valid?(value)
6
7
  value.is_a?(String)
7
8
  end
@@ -1,7 +1,8 @@
1
1
  module Saper
2
2
  module Arguments
3
3
  class Timezone < Argument
4
-
4
+
5
+ # @todo
5
6
  def valid?(value)
6
7
  value.is_a?(String)
7
8
  end
@@ -0,0 +1,12 @@
1
+ module Saper
2
+ module Arguments
3
+ class URL < Argument
4
+
5
+ # @todo
6
+ def valid?(value)
7
+ value.is_a?(String)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,8 @@
1
1
  module Saper
2
2
  module Arguments
3
3
  class Variable < Argument
4
-
4
+
5
+ # @todo
5
6
  def valid?(value)
6
7
  value.is_a?(String) || value.is_a?(Symbol)
7
8
  end
@@ -1,7 +1,8 @@
1
1
  module Saper
2
2
  module Arguments
3
3
  class XPath < Argument
4
-
4
+
5
+ # @todo
5
6
  def valid?(value)
6
7
  value.is_a?(String)
7
8
  end
@@ -52,7 +52,7 @@ module Saper
52
52
  return @types.nil? ? [] : @types.keys
53
53
  end
54
54
  if input == :anything
55
- return Item.subclasses.keys.map { |type| accepts(type.to_sym, options) }
55
+ return @types = {}
56
56
  end
57
57
  output = options[:returns] || input
58
58
  unless Item.exists?(input)
@@ -65,11 +65,35 @@ module Saper
65
65
  @types[input] = output
66
66
  end
67
67
 
68
+ # Returns `true` if action accepts no input.
69
+ # @return [Boolean]
70
+ def self.accepts_nothing?
71
+ @types.nil?
72
+ end
73
+
74
+ # Returns `true` if action accepts at least some kind of input.
75
+ # @return [Boolean]
76
+ def self.accepts_something?
77
+ !accepts_nothing? && !accepts_anything?
78
+ end
79
+
80
+ # Returns `true` if action accepts any kind of input.
81
+ # @return [Boolean]
82
+ def self.accepts_anything?
83
+ @types.empty?
84
+ end
85
+
68
86
  # Returns `true` if action accepts specified type as input.
69
87
  # @param type [Symbol]
70
88
  # @return [Boolean]
71
89
  def self.accepts?(type)
72
- @types.nil? ? false : @types.keys.include?(type)
90
+ if accepts_nothing?
91
+ return false
92
+ end
93
+ if accepts_anything?
94
+ return true
95
+ end
96
+ accepts.include?(type)
73
97
  end
74
98
 
75
99
  # Saves Proc that encapsulates action logic and will be used later for data processing.
@@ -116,8 +140,12 @@ module Saper
116
140
  end
117
141
  end
118
142
 
143
+ # @todo
119
144
  attr_reader :options
120
145
 
146
+ # @todo
147
+ attr_reader :arguments
148
+
121
149
  # Returns a new instance of Saper::Action.
122
150
  # @return [Saper::Action]
123
151
  def initialize(*args)
@@ -139,24 +167,26 @@ module Saper
139
167
  # @param input [object] input
140
168
  # @return [void] depends on action type
141
169
  def run(input = nil, runtime = nil)
170
+ runtime ||= Runtime.new
171
+ input = validate_input(input)
172
+ block.call(runtime, input, *args)
173
+ end
174
+
175
+ # @todo
176
+ def validate_input(input)
177
+ unless self.class.accepts_something?
178
+ return input
179
+ end
142
180
  unless input.is_a?(Item)
143
181
  input = self.class.accepts.map { |type| Item.try(type, input) }.compact.first
144
182
  end
145
183
  if input.nil?
146
- input = Items::Nothing.new
184
+ raise(InvalidInput, input)
147
185
  end
148
186
  unless self.class.accepts?(input.type)
149
187
  raise(InvalidInput, input)
150
188
  end
151
- if runtime.nil?
152
- begin
153
- block.call(input, *args)
154
- rescue NameError
155
- raise RuntimeMissing
156
- end
157
- else
158
- runtime.instance_exec(input, *args, &self.block)
159
- end
189
+ input
160
190
  end
161
191
 
162
192
  # Returns human readable action name.
@@ -198,7 +228,7 @@ module Saper
198
228
  # Returns Proc that encapsulates action logic (i.e. processes data).
199
229
  # @return [Proc]
200
230
  def block
201
- self.class.run || Proc.new { |input, *args| input }
231
+ self.class.run || Proc.new { |runtime, input, *args| input }
202
232
  end
203
233
 
204
234
  # Returns string representation of Action.
@@ -43,7 +43,11 @@ module Saper
43
43
  end
44
44
  end
45
45
 
46
- # @todo
46
+ # Returns a new instacne of Saper::Argument.
47
+ # @option opts[Object] :value
48
+ # @option opts[Saper::Action] :action
49
+ # @option opts[Boolean] :optional
50
+ # @return [Saper::Argument]
47
51
  def initialize(opts = {})
48
52
  @value = nil
49
53
  @opts = opts
@@ -52,7 +56,9 @@ module Saper
52
56
  end
53
57
  end
54
58
 
55
- # @todo
59
+ # Sets new value and returns self.
60
+ # @param value [Object]
61
+ # @return [self]
56
62
  def set(value)
57
63
  unless valid?(value)
58
64
  raise InvalidArgument, value
@@ -62,42 +68,52 @@ module Saper
62
68
  self
63
69
  end
64
70
 
65
- # @todo
71
+ # Returns true if supplied value is accepted by this argument type. Subclasses should override this method.
72
+ # @param value [Object]
73
+ # @return [Boolean]
66
74
  def valid?(value)
67
75
  true
68
76
  end
69
77
 
70
- # @todo
78
+ # Returns normalized value.
79
+ # Subclasses should overrid this method. Subclasses may override this method.
80
+ # @param value [Object]
81
+ # @param value [Object]
71
82
  def normalize(value)
72
83
  value
73
84
  end
74
85
 
75
- # @todo
86
+ # Returns argument value. Subclasses may override this method.
87
+ # @return [Object]
76
88
  def value
77
89
  @value
78
90
  end
79
91
 
80
- # @todo
92
+ # Returns serialized representation of argument's value. Subclasses may override this method.
81
93
  def serialize
82
94
  value
83
95
  end
84
96
 
85
- # @todo
97
+ # Returns true if argument value is mandatory.
98
+ # @return [Boolean]
86
99
  def mandatory?
87
100
  not optional?
88
101
  end
89
102
 
90
- # @todo
91
- def action
92
- @opts[:action]
93
- end
94
-
95
- # @todo
103
+ # Returns true if argument value is optional.
104
+ # @return [Boolean]
96
105
  def optional?
97
106
  @opts[:optional] == true
98
107
  end
99
108
 
100
- # @todo
109
+ # Returns parent Action instance that this Argument belongs to.
110
+ # @return [Saper::Action]
111
+ def action
112
+ @opts[:action]
113
+ end
114
+
115
+ # Returns String representation of argument's value.
116
+ # @return [String]
101
117
  def to_string
102
118
  value.to_s.inspect
103
119
  end