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.
- 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/runtime.rb
CHANGED
@@ -1,52 +1,51 @@
|
|
1
1
|
module Saper
|
2
|
+
# Runtime provides a set of auxiliary functions to Saper actions:
|
3
|
+
# browser requests, keychain access, storage of variables.
|
2
4
|
class Runtime
|
3
5
|
|
4
|
-
# Returns
|
5
|
-
|
6
|
-
|
7
|
-
# Returns action output.
|
8
|
-
attr_reader :output
|
9
|
-
|
10
|
-
# Returns error instance (if any)
|
11
|
-
attr_reader :error
|
12
|
-
|
13
|
-
# Returns action instance.
|
14
|
-
attr_reader :action
|
15
|
-
|
16
|
-
# Returns embedded recipes
|
17
|
-
attr_reader :subrecipes
|
6
|
+
# Returns Saper::Browser instance used by runtime.
|
7
|
+
# @return [Saper::Browser]
|
8
|
+
attr_reader :browser
|
18
9
|
|
19
|
-
# Returns
|
20
|
-
|
10
|
+
# Returns Saper::Keychain instance used by runtime.
|
11
|
+
# @return [Saper::Keychain]
|
12
|
+
attr_reader :keychain
|
21
13
|
|
22
|
-
# Returns
|
23
|
-
|
14
|
+
# Returns a Hash of variables stored by runtime.
|
15
|
+
# @return [Hash]
|
16
|
+
attr_reader :variables
|
24
17
|
|
25
18
|
# Returns a new Saper::Runtime instance.
|
26
|
-
# @param
|
27
|
-
# @param input [Object, nil] action input
|
19
|
+
# @param variables [Hash]
|
28
20
|
# @param options [Hash]
|
21
|
+
# @option options [Browser] :browser Browser instance
|
22
|
+
# @option options [String, Keychain] :keychain Keychain file or instance
|
23
|
+
# @option options [String, Symbol] :agent User-Agent type or string
|
29
24
|
# @return [Saper::Runtime]
|
30
|
-
def initialize(
|
31
|
-
if
|
32
|
-
|
25
|
+
def initialize(variables = {}, options = {})
|
26
|
+
if options.nil?
|
27
|
+
puts self.inspect
|
28
|
+
raise 'stop'
|
29
|
+
end
|
30
|
+
if options[:keychain].is_a?(Keychain)
|
31
|
+
@keychain = options[:keychain]
|
32
|
+
end
|
33
|
+
if options[:keychain].is_a?(String)
|
34
|
+
@keychain = Keychain.load(options[:keychain])
|
35
|
+
end
|
36
|
+
unless @keychain.is_a?(Keychain)
|
37
|
+
@keychain = Keychain.new
|
33
38
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
@
|
41
|
-
@depth = stack.size
|
42
|
-
@options[:keychain] ||= Keychain.new
|
43
|
-
@options[:logger] ||= Logger.new(@options[:log])
|
44
|
-
@options[:browser] ||= Browser.new(:logger => logger)
|
45
|
-
@options[:variables] ||= {}
|
39
|
+
if options[:browser].is_a?(Browser)
|
40
|
+
@browser = options[:browser]
|
41
|
+
end
|
42
|
+
unless @browser.is_a?(Browser)
|
43
|
+
@browser = Browser.new(:agent => options[:agent])
|
44
|
+
end
|
45
|
+
@variables = variables
|
46
46
|
if block_given?
|
47
47
|
yield self
|
48
48
|
end
|
49
|
-
execute(stack.dup)
|
50
49
|
end
|
51
50
|
|
52
51
|
# Returns volume of incoming traffic (in bytes)
|
@@ -71,7 +70,7 @@ module Saper
|
|
71
70
|
# @param name [Symbol]
|
72
71
|
# @return [Object]
|
73
72
|
def [](name)
|
74
|
-
@
|
73
|
+
@variables[name]
|
75
74
|
end
|
76
75
|
|
77
76
|
# Set variable value
|
@@ -79,62 +78,7 @@ module Saper
|
|
79
78
|
# @param value [Object]
|
80
79
|
# @return [Object]
|
81
80
|
def []=(name, value)
|
82
|
-
@
|
83
|
-
end
|
84
|
-
|
85
|
-
# Return a hash with all variables
|
86
|
-
# @return [Hash]
|
87
|
-
def variables
|
88
|
-
@options[:variables].dup
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns descendant runtime instances at a given depth level.
|
92
|
-
# @param level [Integer]
|
93
|
-
# @return [Array<Saper::Runtime>]
|
94
|
-
def descendants(level = 0)
|
95
|
-
level < 1 ? [self] : children.map { |i| i.descendants(level - 1) }.flatten
|
96
|
-
end
|
97
|
-
|
98
|
-
# Returns runtime results.
|
99
|
-
# @return [Array<Object>, Object]
|
100
|
-
def results
|
101
|
-
native_results
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns an array of results.
|
105
|
-
# @return [Array]
|
106
|
-
def result_array
|
107
|
-
native_result_array
|
108
|
-
end
|
109
|
-
|
110
|
-
# Returns `true` if this or any of the subsequent actions produces multiple results.
|
111
|
-
# @return [Boolean]
|
112
|
-
def multiple?
|
113
|
-
(action.nil? ? false : action.multiple?) || children.any?(&:multiple?)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Returns Saper::Browser instance used by runtime.
|
117
|
-
# @return [Saper::Browser]
|
118
|
-
def browser
|
119
|
-
@options[:browser]
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns Saper::Keychain instance used by runtime.
|
123
|
-
# @return [Saper::Keychain]
|
124
|
-
def keychain
|
125
|
-
@options[:keychain]
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns Saper::Logger.
|
129
|
-
# @return [Saper::Logger]
|
130
|
-
def logger
|
131
|
-
@options[:logger]
|
132
|
-
end
|
133
|
-
|
134
|
-
# Writes full runtime log to logger instance.
|
135
|
-
# @return [void]
|
136
|
-
def backtrace
|
137
|
-
logger.runtime(self)
|
81
|
+
@variables[name] = value
|
138
82
|
end
|
139
83
|
|
140
84
|
# Returns access credentials for specified service
|
@@ -144,93 +88,16 @@ module Saper
|
|
144
88
|
keychain[service]
|
145
89
|
end
|
146
90
|
|
147
|
-
#
|
148
|
-
# @param recipe [Saper::Recipe]
|
149
|
-
# @param input [Object, nil]
|
91
|
+
# Returns a duplicate instance of self.
|
150
92
|
# @return [Saper::Runtime]
|
151
|
-
def
|
152
|
-
|
153
|
-
raise RecipeNotFound, recipe
|
154
|
-
end
|
155
|
-
runtime = Runtime.new(recipe, input, @options.merge(:variables => {}))
|
156
|
-
if runtime.error?
|
157
|
-
@error = runtime.error
|
158
|
-
end
|
159
|
-
subrecipes << runtime
|
160
|
-
runtime.non_native_results
|
161
|
-
end
|
162
|
-
|
163
|
-
# Returns `true` if action raised no errors.
|
164
|
-
# @return [Boolean]
|
165
|
-
def success?
|
166
|
-
error.nil?
|
167
|
-
end
|
168
|
-
|
169
|
-
# Returns `true` if action raised at least one error.
|
170
|
-
# @return [Boolean]
|
171
|
-
def error?
|
172
|
-
!success?
|
173
|
-
end
|
174
|
-
|
175
|
-
# Returns runtime results as JSON.
|
176
|
-
# @return [Array]
|
177
|
-
def to_json(*args)
|
178
|
-
JSON.generate non_native_results.map(&:serialize)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Returns runtime results as Saper::Item instances.
|
182
|
-
# @return [Saper::Item, Array<Saper::Item>]
|
183
|
-
def non_native_results
|
184
|
-
multiple? ? non_native_result_array : non_native_result_array.first
|
185
|
-
end
|
186
|
-
|
187
|
-
# Returns runtime results as native Ruby objects.
|
188
|
-
# @return [Object, Array<Object>]
|
189
|
-
def native_results
|
190
|
-
multiple? ? native_result_array : native_result_array.first
|
191
|
-
end
|
192
|
-
|
193
|
-
private
|
194
|
-
|
195
|
-
# Returns runtime results as an array of Saper::Item instances.
|
196
|
-
# @return [Array<Saper::Item>]
|
197
|
-
def non_native_result_array
|
198
|
-
descendants(depth - 1).map(&:output).flatten.compact
|
199
|
-
end
|
200
|
-
|
201
|
-
# Returns runtime results as an array of native Ruby objects.
|
202
|
-
# @return [Array<Object>]
|
203
|
-
def native_result_array
|
204
|
-
non_native_result_array.map { |result| result.is_a?(Item) ? result.to_native : result }
|
93
|
+
def copy
|
94
|
+
Runtime.new(variables.dup, :browser => browser, :keychain => keychain)
|
205
95
|
end
|
206
96
|
|
207
|
-
#
|
208
|
-
# @return [
|
209
|
-
def
|
210
|
-
|
211
|
-
@action = stack.shift
|
212
|
-
if action.nil?
|
213
|
-
return self
|
214
|
-
end
|
215
|
-
unless action.is_a?(Saper::Action)
|
216
|
-
raise ActionExpected, action
|
217
|
-
end
|
218
|
-
begin
|
219
|
-
@output = action.run(input, self)
|
220
|
-
rescue Saper::Error => e
|
221
|
-
@output = nil
|
222
|
-
@error = e
|
223
|
-
end
|
224
|
-
unless @output.is_a?(Array)
|
225
|
-
@output = [@output]
|
226
|
-
end
|
227
|
-
if stack.empty?
|
228
|
-
return self
|
229
|
-
end
|
230
|
-
@children = @output.map do |item|
|
231
|
-
Runtime.new(stack, item, @options.dup)
|
232
|
-
end
|
233
|
-
self
|
97
|
+
# Returns a duplicate instance of self without the stored variables.
|
98
|
+
# @return [Saper::Runtime]
|
99
|
+
def copy_without_variables
|
100
|
+
Runtime.new({}, :browser => browser, :keychain => keychain)
|
234
101
|
end
|
235
102
|
|
236
103
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Saper
|
2
|
+
class Stack
|
3
|
+
|
4
|
+
# Returns Runtime instance used by the stack
|
5
|
+
# @return [Saper::Runtime]
|
6
|
+
attr_reader :runtime
|
7
|
+
|
8
|
+
# Returns all items in the stack.
|
9
|
+
# @return [Array<Saper::Item>]
|
10
|
+
attr_reader :items
|
11
|
+
|
12
|
+
# Returns a new Stack instance.
|
13
|
+
# @param runtime [Saper::Runtime]
|
14
|
+
# @param items [Saper::Item]
|
15
|
+
# @return [Saper::Stack]
|
16
|
+
def initialize(runtime, *items)
|
17
|
+
@runtime, @items = runtime, items.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a duplicate instance of self.
|
21
|
+
# @return [Saper::Stack]
|
22
|
+
def copy
|
23
|
+
Stack.new(runtime.copy, *to_a)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds action result to the end of the stack and returns self.
|
27
|
+
# @param item [Saper::Item]
|
28
|
+
# @return [self]
|
29
|
+
def add(item)
|
30
|
+
@items.push(item); self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the result of the last action or nil (in case of an error).
|
34
|
+
# @return [Saper::Item]
|
35
|
+
def result
|
36
|
+
error? ? Items::Nothing.new : last
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the result of the last action.
|
40
|
+
# @return [Saper::Item]
|
41
|
+
def last
|
42
|
+
@items.last || Items::Nothing.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# @todo
|
46
|
+
def error?
|
47
|
+
last.is_a?(Saper::Error)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns an array of action results.
|
51
|
+
# @return [Array<Saper::Item>]
|
52
|
+
def to_a
|
53
|
+
@items.dup
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
data/lib/saper/items/atom.rb
CHANGED
@@ -2,6 +2,7 @@ module Saper
|
|
2
2
|
module Items
|
3
3
|
class Atom < Item
|
4
4
|
|
5
|
+
# @todo
|
5
6
|
def self.new(item)
|
6
7
|
super case item
|
7
8
|
when Hash
|
@@ -11,22 +12,32 @@ module Saper
|
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
# @todo
|
14
16
|
def initialize(hash)
|
15
17
|
@atts = hash
|
16
18
|
end
|
17
19
|
|
20
|
+
# @todo
|
21
|
+
def ==(other)
|
22
|
+
to_hash == other
|
23
|
+
end
|
24
|
+
|
25
|
+
# @todo
|
18
26
|
def to_hash
|
19
27
|
@atts.dup
|
20
28
|
end
|
21
29
|
|
30
|
+
# @todo
|
22
31
|
def [](name)
|
23
32
|
@atts[name]
|
24
33
|
end
|
25
34
|
|
35
|
+
# @todo
|
26
36
|
def serialize
|
27
|
-
@atts.
|
37
|
+
Hash[@atts.map { |key, value| [key, value.respond_to?(:serialize) ? value.serialize : value] }]
|
28
38
|
end
|
29
39
|
|
40
|
+
# @todo
|
30
41
|
def to_native(object = nil)
|
31
42
|
if object.nil?
|
32
43
|
return to_native(to_hash)
|
@@ -43,10 +54,16 @@ module Saper
|
|
43
54
|
object
|
44
55
|
end
|
45
56
|
|
57
|
+
# @todo
|
46
58
|
def to_json
|
47
59
|
JSON.new(@hash, true)
|
48
60
|
end
|
49
61
|
|
62
|
+
# @todo
|
63
|
+
def to_s
|
64
|
+
to_json
|
65
|
+
end
|
66
|
+
|
50
67
|
end
|
51
68
|
end
|
52
69
|
end
|
data/lib/saper/items/document.rb
CHANGED
@@ -2,67 +2,87 @@ module Saper
|
|
2
2
|
module Items
|
3
3
|
class Document < Item
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
when Mechanize::File
|
8
|
-
item
|
9
|
-
else
|
10
|
-
raise(InvalidItem, item)
|
11
|
-
end
|
12
|
-
end
|
5
|
+
# @todo
|
6
|
+
attr_reader :body
|
13
7
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
8
|
+
# @todo
|
9
|
+
attr_reader :uri
|
17
10
|
|
18
|
-
|
19
|
-
|
11
|
+
# @todo
|
12
|
+
attr_reader :headers
|
13
|
+
|
14
|
+
# @todo
|
15
|
+
def self.new(body, uri = nil, headers = {})
|
16
|
+
unless body.is_a?(String)
|
17
|
+
raise(InvalidItem)
|
18
|
+
end
|
19
|
+
unless headers.is_a?(Hash)
|
20
|
+
raise(InvalidItem)
|
21
|
+
end
|
22
|
+
super
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
# @todo
|
26
|
+
def initialize(body, uri = nil, headers = {})
|
27
|
+
@uri = (uri.nil? ? nil : uri.to_s)
|
28
|
+
@body = body
|
29
|
+
@headers = headers
|
24
30
|
end
|
25
31
|
|
32
|
+
# @todo
|
26
33
|
def size
|
27
34
|
body.size
|
28
35
|
end
|
29
36
|
|
37
|
+
# @todo
|
30
38
|
def mime
|
31
39
|
content_type.split(";").first
|
32
40
|
end
|
33
41
|
|
42
|
+
# @todo
|
34
43
|
def charset
|
35
44
|
content_type.include?("charset=") ? content_type.split("charset=").last : nil
|
36
45
|
end
|
37
46
|
|
47
|
+
# @todo
|
38
48
|
def to_text
|
39
|
-
body
|
49
|
+
Text.new(body)
|
40
50
|
end
|
41
51
|
|
52
|
+
# @todo
|
42
53
|
def to_html
|
43
|
-
HTML.new(
|
54
|
+
HTML.new(self)
|
44
55
|
end
|
45
56
|
|
57
|
+
# @todo
|
46
58
|
def to_xml
|
47
59
|
XML.new(body)
|
48
60
|
end
|
49
61
|
|
62
|
+
# @todo
|
50
63
|
def to_json
|
51
64
|
JSON.new(body)
|
52
65
|
end
|
53
66
|
|
67
|
+
# @todo
|
54
68
|
def to_markdown
|
55
69
|
Markdown.new(to_html)
|
56
70
|
end
|
57
71
|
|
72
|
+
# @todo
|
58
73
|
def to_native
|
59
|
-
|
74
|
+
body
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
to_native
|
60
79
|
end
|
61
80
|
|
62
81
|
private
|
63
82
|
|
83
|
+
# @todo
|
64
84
|
def content_type
|
65
|
-
|
85
|
+
headers['content-type'] || ""
|
66
86
|
end
|
67
87
|
|
68
88
|
end
|