benschwarz-smoke 0.3.6 → 0.3.7
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.
- data/README.markdown +4 -2
- data/VERSION.yml +1 -1
- data/lib/smoke.rb +25 -4
- data/lib/smoke/origin.rb +25 -2
- data/lib/smoke/request.rb +6 -1
- data/lib/smoke/source/data.rb +1 -1
- data/lib/smoke/source/join.rb +0 -1
- data/lib/smoke/source/yql.rb +1 -1
- data/spec/smoke/origin_spec.rb +79 -42
- data/spec/smoke/request_spec.rb +24 -4
- data/spec/smoke_spec.rb +27 -0
- data/spec/spec_helper.rb +5 -1
- data/spec/supports/test_source.rb +2 -2
- metadata +3 -3
data/README.markdown
CHANGED
|
@@ -70,13 +70,15 @@ Integrity [is running for smoke](http://integrity.ffolio.net/smoke)
|
|
|
70
70
|
|
|
71
71
|
### TODO (working on, just mental notes)
|
|
72
72
|
|
|
73
|
-
*
|
|
73
|
+
* Secrets
|
|
74
74
|
* Checkout experimental fakeweb version to stub out the yql specs
|
|
75
75
|
|
|
76
76
|
#### Later / maybe
|
|
77
|
-
* Allow for sources to explicitly set the content type being returned for those stupid content providers
|
|
78
77
|
* YQL w/oAuth
|
|
79
78
|
* YQL Subqueries?
|
|
79
|
+
* Implement basic auth for sources
|
|
80
|
+
* Allow for sources to explicitly set the content type being returned for those stupid content providers
|
|
81
|
+
|
|
80
82
|
|
|
81
83
|
#### For wiki pages (docs, later)
|
|
82
84
|
* How to use `path`
|
data/VERSION.yml
CHANGED
data/lib/smoke.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'zlib'
|
|
2
2
|
require 'open-uri'
|
|
3
3
|
require 'logger'
|
|
4
|
-
require 'json'
|
|
5
4
|
require 'crack'
|
|
6
5
|
require 'simple-rss'
|
|
6
|
+
require 'json'
|
|
7
7
|
|
|
8
8
|
$:<< File.join(File.dirname(__FILE__))
|
|
9
9
|
|
|
@@ -13,7 +13,10 @@ require 'core_ext/hash.rb'
|
|
|
13
13
|
module Smoke
|
|
14
14
|
class << self
|
|
15
15
|
@@active_sources = {}
|
|
16
|
-
|
|
16
|
+
@@config = {
|
|
17
|
+
:enable_logging => true,
|
|
18
|
+
:user_agent => "Ruby/#{RUBY_VERSION}/Smoke"
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
# Access registered smoke source instances
|
|
19
22
|
#
|
|
@@ -58,7 +61,25 @@ module Smoke
|
|
|
58
61
|
# Smoke.log.error "message"
|
|
59
62
|
# Smoke.log.warn "message"
|
|
60
63
|
def log
|
|
61
|
-
@@log ||= Logger.new($stdout)
|
|
64
|
+
@@log ||= Logger.new(config[:enable_logging] ? $stdout : "/dev/null")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Set any configurable options
|
|
68
|
+
#
|
|
69
|
+
# Smoke.configure do |c|
|
|
70
|
+
# c[:user_agent] = "Some other site"
|
|
71
|
+
# end
|
|
72
|
+
#
|
|
73
|
+
def configure(&block)
|
|
74
|
+
yield @@config
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Access configuration options
|
|
78
|
+
#
|
|
79
|
+
# Smoke.config[:option_name]
|
|
80
|
+
# => true
|
|
81
|
+
def config
|
|
82
|
+
@@config
|
|
62
83
|
end
|
|
63
84
|
|
|
64
85
|
def yql(name, &block); Smoke::Source::YQL.new(name, &block); end
|
data/lib/smoke/origin.rb
CHANGED
|
@@ -34,8 +34,9 @@ module Smoke
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def items=(
|
|
38
|
-
@items =
|
|
37
|
+
def items=(response) # :nodoc:
|
|
38
|
+
@items = [(@path.nil?) ? response : drill(response, *@path)]
|
|
39
|
+
symbolize_keys!
|
|
39
40
|
transform!
|
|
40
41
|
end
|
|
41
42
|
|
|
@@ -73,6 +74,24 @@ module Smoke
|
|
|
73
74
|
@transformation << block
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
# Transform must be used inside an `emit` block.
|
|
78
|
+
# It can be used to alter the a single field at a time
|
|
79
|
+
#
|
|
80
|
+
# Usage:
|
|
81
|
+
# emit do
|
|
82
|
+
# transform :name do |name|
|
|
83
|
+
# name.gsub(/\302/, "")
|
|
84
|
+
# end
|
|
85
|
+
# end
|
|
86
|
+
#
|
|
87
|
+
# In quasi-english: The result of the block is returned and set to each
|
|
88
|
+
# of the :name attributes in the result set.
|
|
89
|
+
def transform(name, &block)
|
|
90
|
+
items.each do |item|
|
|
91
|
+
item[name] = yield(item[name]) || item[name]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
76
95
|
# Prepare is used when you'd like to provision for
|
|
77
96
|
# arguments / variables to be set after the source definition.
|
|
78
97
|
# Eg, create a source definition for twitter, omitting the "username".
|
|
@@ -188,6 +207,10 @@ module Smoke
|
|
|
188
207
|
@transformation.each{|t| t.call } unless @transformation.nil?
|
|
189
208
|
end
|
|
190
209
|
|
|
210
|
+
def symbolize_keys!
|
|
211
|
+
@items = items.flatten.map{|i| i.symbolize_keys! } if items.respond_to? :flatten
|
|
212
|
+
end
|
|
213
|
+
|
|
191
214
|
def drill(*path)
|
|
192
215
|
path.inject(nil) do |obj, pointer|
|
|
193
216
|
obj = obj.nil? ? pointer : obj[pointer]
|
data/lib/smoke/request.rb
CHANGED
|
@@ -20,9 +20,14 @@ module Smoke
|
|
|
20
20
|
|
|
21
21
|
private
|
|
22
22
|
def dispatch
|
|
23
|
+
opts = {
|
|
24
|
+
"User-Agent" => Smoke.config[:user_agent],
|
|
25
|
+
"Accept-Encoding" => "gzip"
|
|
26
|
+
}
|
|
23
27
|
Thread.new {
|
|
24
|
-
open(@uri,
|
|
28
|
+
open(@uri, opts) do |request|
|
|
25
29
|
@content_type = request.content_type
|
|
30
|
+
request = Zlib::GzipReader.new(request) if request.content_encoding.include? "gzip"
|
|
26
31
|
@body = request.read
|
|
27
32
|
end
|
|
28
33
|
}.join
|
data/lib/smoke/source/data.rb
CHANGED
data/lib/smoke/source/join.rb
CHANGED
data/lib/smoke/source/yql.rb
CHANGED
data/spec/smoke/origin_spec.rb
CHANGED
|
@@ -6,6 +6,10 @@ describe Smoke::Origin do
|
|
|
6
6
|
emit do
|
|
7
7
|
rename(:head => :title)
|
|
8
8
|
sort(:title)
|
|
9
|
+
|
|
10
|
+
transform :title do |title|
|
|
11
|
+
title.gsub(/Animal: /, '')
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -43,8 +47,21 @@ describe Smoke::Origin do
|
|
|
43
47
|
|
|
44
48
|
describe "filtering" do
|
|
45
49
|
before do
|
|
46
|
-
TestSource.source(:keep)
|
|
47
|
-
|
|
50
|
+
TestSource.source(:keep) do
|
|
51
|
+
emit do
|
|
52
|
+
transform :head do |head|
|
|
53
|
+
head.gsub(/Animal: /, '')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
TestSource.source(:discard) do
|
|
59
|
+
emit do
|
|
60
|
+
transform :head do |head|
|
|
61
|
+
head.gsub(/Animal: /, '')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
48
65
|
end
|
|
49
66
|
|
|
50
67
|
it "should keep items" do
|
|
@@ -93,12 +110,18 @@ describe Smoke::Origin do
|
|
|
93
110
|
end
|
|
94
111
|
|
|
95
112
|
it "method chaining" do
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
TestSource.source(:chain) do
|
|
114
|
+
emit do
|
|
115
|
+
transform :head do |head|
|
|
116
|
+
head.gsub(/Animal: /, '')
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
Smoke[:chain].rename(:head => :header).sort(:header).output.should == [{:header => "Kangaroo"}, {:header => "Platypus"}]
|
|
98
121
|
end
|
|
99
122
|
|
|
100
123
|
it "should softly error when attempting to sort on a key that doesn't exist" do
|
|
101
|
-
|
|
124
|
+
Smoke[:chain].sort(:header).should_not raise_error("NoMethodError")
|
|
102
125
|
end
|
|
103
126
|
|
|
104
127
|
describe "preperation" do
|
|
@@ -114,51 +137,65 @@ describe Smoke::Origin do
|
|
|
114
137
|
lambda { @source.prepare }.should raise_error
|
|
115
138
|
lambda { @source.prepare {} }.should_not raise_error
|
|
116
139
|
end
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
path :photos, :photo
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe "call order" do
|
|
143
|
+
before :all do
|
|
144
|
+
@url = "http://domain.tld/benschwarz/feed"
|
|
145
|
+
FakeWeb.register_uri(@url, :response => File.join(SPEC_DIR, 'supports', 'flickr-photo.json'))
|
|
146
|
+
|
|
147
|
+
Smoke.data :feed_preperation_call_order do
|
|
148
|
+
prepare do
|
|
149
|
+
url "http://domain.tld/#{username}/feed"
|
|
129
150
|
end
|
|
151
|
+
|
|
152
|
+
path :photos, :photo
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe "before setting variables" do
|
|
157
|
+
it "should fail" do
|
|
158
|
+
lambda { Smoke[:feed_preperation_call_order].output }.should raise_error(NameError)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
describe "setting abstract properties" do
|
|
163
|
+
it "should not respond to a property that hasn't been set" do
|
|
164
|
+
lambda { Smoke[:feed_preperation_call_order].abstract }.should raise_error(NoMethodError)
|
|
130
165
|
end
|
|
131
166
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
end
|
|
167
|
+
it "should allow setting a property" do
|
|
168
|
+
lambda { Smoke[:feed_preperation_call_order].abstract(:value) }.should_not raise_error(NoMethodError)
|
|
169
|
+
Smoke[:feed_preperation_call_order].abstract.should == :value
|
|
136
170
|
end
|
|
137
171
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "should chain the source when setting a property" do
|
|
149
|
-
Smoke[:feed_preperation_call_order].abstract(:value).should be_an_instance_of(Smoke::Source::Data)
|
|
150
|
-
end
|
|
172
|
+
it "should chain the source when setting a property" do
|
|
173
|
+
Smoke[:feed_preperation_call_order].abstract(:value).should be_an_instance_of(Smoke::Source::Data)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe "after setting variables" do
|
|
178
|
+
it "should output successfully" do
|
|
179
|
+
lambda { Smoke[:feed_preperation_call_order].username("benschwarz").output }.should_not raise_error
|
|
151
180
|
end
|
|
152
181
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
lambda { Smoke[:feed_preperation_call_order].username("benschwarz").output }.should_not raise_error
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it "should have used the correct url" do
|
|
159
|
-
Smoke[:feed_preperation_call_order].request.uri.should == @url
|
|
160
|
-
end
|
|
182
|
+
it "should have used the correct url" do
|
|
183
|
+
Smoke[:feed_preperation_call_order].request.uri.should == @url
|
|
161
184
|
end
|
|
162
185
|
end
|
|
163
186
|
end
|
|
187
|
+
|
|
188
|
+
describe "trasformations" do
|
|
189
|
+
it "should respond to emit" do
|
|
190
|
+
Smoke[:test].should respond_to(:emit)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "should respond to transform" do
|
|
194
|
+
Smoke[:test].should respond_to(:transform)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "should have at least one transformation" do
|
|
198
|
+
Smoke[:test].transformation.size.should == 1
|
|
199
|
+
end
|
|
200
|
+
end
|
|
164
201
|
end
|
data/spec/smoke/request_spec.rb
CHANGED
|
@@ -22,13 +22,33 @@ describe Smoke::Request do
|
|
|
22
22
|
|
|
23
23
|
it "should be a pure ruby array response" do
|
|
24
24
|
# Temporary real request, fakeweb isn't allowing content_type setting as of writing
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
request = Smoke::Request.new("http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20search.web%20WHERE%20query%20=%20'ruby'&format=xml")
|
|
26
|
+
request.body.should be_an_instance_of(Hash)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it "should be a raw string response" do
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
request = Smoke::Request.new(@url, :raw_response)
|
|
31
|
+
request.body.should be_an_instance_of(String)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "gzipped responses" do
|
|
35
|
+
before do
|
|
36
|
+
# class Stream < StringIO
|
|
37
|
+
# def close; rewind; end
|
|
38
|
+
# end
|
|
39
|
+
# output = Stream.new
|
|
40
|
+
# gz = Zlib::GzipWriter.new(output)
|
|
41
|
+
# gz.write(File.read(@gzip_response))
|
|
42
|
+
# gz.close
|
|
43
|
+
#@gzip_response = File.join(SPEC_DIR, 'supports', 'gzip.response')
|
|
44
|
+
#FakeWeb.register_uri(@url, :file => @gzip_response)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should transparently handle a gzipped response" do
|
|
48
|
+
pending
|
|
49
|
+
request = Smoke::Request.new(@url)
|
|
50
|
+
request.body.should == "gzip_response"
|
|
51
|
+
end
|
|
32
52
|
end
|
|
33
53
|
|
|
34
54
|
describe "http redirects" do
|
data/spec/smoke_spec.rb
CHANGED
|
@@ -17,4 +17,31 @@ describe Smoke do
|
|
|
17
17
|
Smoke[:b].should be_an_instance_of(Smoke::Origin)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
describe "configuration" do
|
|
22
|
+
it "should be configurable" do
|
|
23
|
+
Smoke.should respond_to(:configure)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should be accessible" do
|
|
27
|
+
Smoke.should respond_to(:config)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should take a block and be retrievable" do
|
|
31
|
+
Smoke.configure {|c| c[:spec] = true }
|
|
32
|
+
Smoke.config[:spec].should be_true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "default configurations" do
|
|
36
|
+
it "should have at least one default configuration" do
|
|
37
|
+
Smoke.config.keys.should_not be_empty
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should allow overwriting default configurations" do
|
|
41
|
+
key = Smoke.config.keys.first
|
|
42
|
+
Smoke.configure {|c| c[key] = true }
|
|
43
|
+
Smoke.config[key].should be_true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
20
47
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -2,8 +2,8 @@ module TestSource
|
|
|
2
2
|
def self.source(name, &block)
|
|
3
3
|
source = Smoke::Origin.new(name, &block || Proc.new {})
|
|
4
4
|
source.items = [
|
|
5
|
-
{:head => "Platypus"},
|
|
6
|
-
{:head => "Kangaroo"}
|
|
5
|
+
{:head => "Animal: Platypus"},
|
|
6
|
+
{:head => "Animal: Kangaroo"}
|
|
7
7
|
]
|
|
8
8
|
return source
|
|
9
9
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: benschwarz-smoke
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Schwarz
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-06-
|
|
12
|
+
date: 2009-06-15 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
@@ -100,7 +100,7 @@ requirements: []
|
|
|
100
100
|
rubyforge_project:
|
|
101
101
|
rubygems_version: 1.2.0
|
|
102
102
|
signing_key:
|
|
103
|
-
specification_version:
|
|
103
|
+
specification_version: 2
|
|
104
104
|
summary: smoke is a DSL that allows you to take data from YQL, RSS / Atom
|
|
105
105
|
test_files: []
|
|
106
106
|
|