benschwarz-smoke 0.2.4 → 0.3.4

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 CHANGED
@@ -56,8 +56,8 @@ Integrity [is running for smoke](http://integrity.ffolio.net/smoke)
56
56
 
57
57
  ### TODO (working on, just mental notes)
58
58
 
59
- * Clean up / simplify source activation process
60
- * Consider variable injection (eg, allow to query sending a username within some aspect of the request)
59
+ * Joined sources require a call to dispatch when their parent calls output
60
+ * Sources that are renamed also need their @name's renaming
61
61
  * Implement a disaptch / request method that will actually thread property (event-machine)
62
62
  * Checkout experimental fakeweb version to stub out the yql specs
63
63
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 2
3
+ :minor: 3
4
4
  :patch: 4
data/lib/smoke/origin.rb CHANGED
@@ -5,7 +5,7 @@ module Smoke
5
5
  def initialize(name, &block)
6
6
  raise StandardError, "Sources must have a name" unless name
7
7
  @name = name
8
- @items, @transformation = [], []
8
+ @items, @prepare, @transformation = [], [], []
9
9
 
10
10
  activate!
11
11
  instance_eval(&block) if block_given?
@@ -19,22 +19,23 @@ module Smoke
19
19
  # output(:json)
20
20
  # => "[{title: \"Ray\"}, {title: \"Peace\"}]"
21
21
  def output(type = :ruby)
22
+ prepare!
22
23
  dispatch if respond_to? :dispatch
23
24
  output = (@items.length == 1) ? @items.first : @items
24
25
 
25
26
  case type
26
- when :ruby
27
- return output
28
27
  when :json
29
28
  return ::JSON.generate(output)
30
29
  when :yaml
31
30
  return YAML.dump(output)
31
+ else
32
+ return output
32
33
  end
33
34
  end
34
35
 
35
36
  def items=(items) # :nodoc:
36
37
  @items = items.flatten.map{|i| i.symbolize_keys! }
37
- invoke_transformation
38
+ transform!
38
39
  end
39
40
 
40
41
  # Path allows you to traverse the tree of a the items returned to
@@ -68,7 +69,39 @@ module Smoke
68
69
  # rename(:href => :link)
69
70
  # end
70
71
  def emit(&block)
71
- @transformation << DelayedBlock.new(&block)
72
+ @transformation << block
73
+ end
74
+
75
+ # Prepare is used when you'd like to provision for
76
+ # arguments / variables to be set after the source definition.
77
+ # Eg, create a source definition for twitter, omitting the "username".
78
+ # Set the username using chaining later.
79
+ #
80
+ # Usage:
81
+ # # Definition
82
+ # Smoke.feed :twitter do
83
+ # prepare do
84
+ # url "http://twitter.com/#{username}/rss"
85
+ # end
86
+ # end
87
+ #
88
+ # # End use
89
+ # Smoke[:twitter].username(:benschwarz).output
90
+ def prepare(&block)
91
+ raise ArgumentError, "requires a block" unless block_given?
92
+ @prepare << block
93
+ end
94
+
95
+ def method_missing(symbol, *args, &block)
96
+ ivar = "@#{symbol}"
97
+
98
+ if args.empty?
99
+ return instance_variable_get(ivar) || super
100
+ else
101
+ instance_variable_set(ivar, args.pop)
102
+ end
103
+
104
+ return self
72
105
  end
73
106
 
74
107
  # Re-sort items by a particular key
@@ -134,8 +167,12 @@ module Smoke
134
167
  end
135
168
 
136
169
  private
137
- def invoke_transformation
138
- @transformation.each{|t| t.execute! } unless @transformation.nil?
170
+ def prepare!
171
+ @prepare.each{|p| p.call } unless @prepare.nil?
172
+ end
173
+
174
+ def transform!
175
+ @transformation.each{|t| t.call } unless @transformation.nil?
139
176
  end
140
177
 
141
178
  def drill(*path)
@@ -1,42 +1,34 @@
1
1
  module Smoke
2
- module Source # :nodoc:
3
- module Data # :nodoc:
4
- Smoke.register(Smoke::Source::Data)
5
-
6
- def data(name, &block)
7
- Data.new(name, &block)
2
+ module Source # :nodoc:
3
+ # The "Data" source allows you to query
4
+ # datasources that are "complete" urls
5
+ # and rely on the automagic object parsing
6
+ # that smoke provides.
7
+ #
8
+ # For example, you may use this source
9
+ # to query a complete restful api call
10
+ # unpackage the xml response and get a
11
+ # clean ruby object.
12
+ #
13
+ # Data can take as many urls as you'd like
14
+ # to throw at it.
15
+ #
16
+ # Usage:
17
+ # Smoke.data(:ruby) do
18
+ # url "http://api.flickr.com/services/rest/?user_id=36821533%40N00&tags=benschwarz-site&nojsoncallback=1&method=flickr.photos.search&format=json&api_key=your_api_key_here
19
+ # path :photos, :photo
20
+ # end
21
+ class Data < Origin
22
+ attr_reader :request
23
+
24
+ def url(source_url)
25
+ @url = source_url
8
26
  end
9
27
 
10
- # The "Data" source allows you to query
11
- # datasources that are "complete" urls
12
- # and rely on the automagic object parsing
13
- # that smoke provides.
14
- #
15
- # For example, you may use this source
16
- # to query a complete restful api call
17
- # unpackage the xml response and get a
18
- # clean ruby object.
19
- #
20
- # Data can take as many urls as you'd like
21
- # to throw at it.
22
- #
23
- # Usage:
24
- # Smoke.data(:ruby) do
25
- # url "http://api.flickr.com/services/rest/?user_id=36821533%40N00&tags=benschwarz-site&nojsoncallback=1&method=flickr.photos.search&format=json&api_key=your_api_key_here
26
- # path :photos, :photo
27
- # end
28
- class Data < Origin
29
- attr_reader :request
30
-
31
- def url(source_url)
32
- @url = source_url
33
- end
34
-
35
- protected
36
- def dispatch
37
- @request = Smoke::Request.new(@url)
38
- self.items = (@path.nil?) ? @request.body : drill(@request.body, *@path)
39
- end
28
+ protected
29
+ def dispatch
30
+ @request = Smoke::Request.new(@url)
31
+ self.items = (@path.nil?) ? @request.body : drill(@request.body, *@path)
40
32
  end
41
33
  end
42
34
  end
@@ -1,31 +1,23 @@
1
1
  module Smoke
2
2
  module Source # :nodoc:
3
- module Feed # :nodoc:
4
- Smoke.register(Smoke::Source::Feed)
3
+ # Feed can take multiple rss or atom feeds and munge them up together.
4
+ #
5
+ # Usage:
6
+ # Smoke.feed(:ruby) do
7
+ # url "domain.tld/rss"
8
+ # url "site.tld/atom"
9
+ # end
10
+ class Feed < Origin
11
+ attr_reader :requests
5
12
 
6
- def feed(name, &block)
7
- Feed.new(name, &block)
13
+ def url(feed_uri)
14
+ (@feeds ||= [] ) << feed_uri
8
15
  end
9
16
 
10
- # Feed can take multiple rss or atom feeds and munge them up together.
11
- #
12
- # Usage:
13
- # Smoke.feed(:ruby) do
14
- # url "domain.tld/rss"
15
- # url "site.tld/atom"
16
- # end
17
- class Feed < Origin
18
- attr_reader :requests
19
-
20
- def url(feed_uri)
21
- (@feeds ||= [] ) << feed_uri
22
- end
23
-
24
- protected
25
- def dispatch
26
- @requests = @feeds.map{|f| Smoke::Request.new(f, :raw_response) }
27
- self.items = @requests.map{|r| ::SimpleRSS.parse(r.body).items }.flatten
28
- end
17
+ protected
18
+ def dispatch
19
+ @requests = @feeds.map{|f| Smoke::Request.new(f, :raw_response) }
20
+ self.items = @requests.map{|r| ::SimpleRSS.parse(r.body).items }.flatten
29
21
  end
30
22
  end
31
23
  end
@@ -1,92 +1,84 @@
1
1
  module Smoke
2
- module Source # :nodoc:
3
- module YQL # :nodoc:
4
- Smoke.register(Smoke::Source::YQL)
2
+ module Source # :nodoc:
3
+ # YQL will call to Yahoo YQL services
4
+ #
5
+ # Usage:
6
+ # Smoke.yql(:ruby) do
7
+ # select :all
8
+ # from "search.web"
9
+ # where :query, "ruby"
10
+ # end
11
+ class YQL < Origin
12
+ API_BASE = "http://query.yahooapis.com/v1/public/yql"
13
+ attr_reader :request
5
14
 
6
- def yql(name, &block)
7
- YQL.new(name, &block)
15
+ # Select indicates what YQL will be selecting
16
+ # Usage:
17
+ # select :all
18
+ # => "SELECT *"
19
+ # select :title
20
+ # => "SELECT title"
21
+ # select :title, :description
22
+ # => "SELECT title, description"
23
+ def select(what = :all)
24
+ @select = what.join(",") and return if what.is_a? Array
25
+ @select = "*" and return if what == :all
26
+ @select = what.to_s
27
+ end
28
+
29
+ # from corresponds to the from fragment of the YQL query
30
+ # Usage:
31
+ # from "search.web"
32
+ # or
33
+ # from :html
34
+ def from(source)
35
+ @from = source.join(',') and return if source.is_a? Array
36
+ @from = source.to_s
37
+ end
38
+
39
+ # where is a straight up match, no fancy matchers
40
+ # are currently supported
41
+ # Usage:
42
+ # where :xpath, "//div/div/a"
43
+ # or
44
+ # where :query, "python"
45
+ def where(column, value)
46
+ @where = @where || []
47
+ @where << "#{column.to_s} = '#{value}'"
8
48
  end
9
49
 
10
- # YQL will call to Yahoo YQL services
50
+ # `use` can be used to set the url location of the data-table
51
+ # that you want YQL to search upon
11
52
  #
12
53
  # Usage:
13
- # Smoke.yql(:ruby) do
14
- # select :all
15
- # from "search.web"
16
- # where :query, "ruby"
17
- # end
18
- class YQL < Origin
19
- API_BASE = "http://query.yahooapis.com/v1/public/yql"
20
- attr_reader :request
21
-
22
- # Select indicates what YQL will be selecting
23
- # Usage:
24
- # select :all
25
- # => "SELECT *"
26
- # select :title
27
- # => "SELECT title"
28
- # select :title, :description
29
- # => "SELECT title, description"
30
- def select(what = :all)
31
- @select = what.join(",") and return if what.is_a? Array
32
- @select = "*" and return if what == :all
33
- @select = what.to_s
34
- end
35
-
36
- # from corresponds to the from fragment of the YQL query
37
- # Usage:
38
- # from "search.web"
39
- # or
40
- # from :html
41
- def from(source)
42
- @from = source.join(',') and return if source.is_a? Array
43
- @from = source.to_s
44
- end
45
-
46
- # where is a straight up match, no fancy matchers
47
- # are currently supported
48
- # Usage:
49
- # where :xpath, "//div/div/a"
50
- # or
51
- # where :query, "python"
52
- def where(column, value)
53
- @where = @where || []
54
- @where << "#{column.to_s} = '#{value}'"
55
- end
56
-
57
- # `use` can be used to set the url location of the data-table
58
- # that you want YQL to search upon
59
- #
60
- # Usage:
61
- # use "http://datatables.org/alltables.env"
62
- def use(url)
63
- params.merge!({:env => url})
64
- end
65
-
66
- protected
67
- def params
68
- @params || @params = {}
69
- end
70
-
71
- def dispatch
72
- @request = Smoke::Request.new(build_uri)
73
- self.items = [(@path.nil?) ? @request.body : drill(@request.body, *@path)]
74
- end
75
-
76
- private
77
- def build_uri
78
- chunks = []
79
- default_opts = {
80
- :q => build_query,
81
- :format => "json"
82
- }.merge(params).each {|k,v| chunks << "#{k}=#{v}" }
83
-
84
- return URI.encode(API_BASE + "?" + chunks.join("&"))
85
- end
54
+ # use "http://datatables.org/alltables.env"
55
+ def use(url)
56
+ params.merge!({:env => url})
57
+ end
58
+
59
+ protected
60
+ def params
61
+ @params || @params = {}
62
+ end
63
+
64
+ def dispatch
65
+ @request = Smoke::Request.new(build_uri)
66
+ self.items = [(@path.nil?) ? @request.body : drill(@request.body, *@path)]
67
+ end
68
+
69
+ private
70
+ def build_uri
71
+ chunks = []
72
+ default_opts = {
73
+ :q => build_query,
74
+ :format => "json"
75
+ }.merge(params).each {|k,v| chunks << "#{k}=#{v}" }
86
76
 
87
- def build_query
88
- "SELECT #{@select} FROM #{@from} WHERE #{@where.join(" AND ")}"
89
- end
77
+ return URI.encode(API_BASE + "?" + chunks.join("&"))
78
+ end
79
+
80
+ def build_query
81
+ "SELECT #{@select} FROM #{@from} WHERE #{@where.join(" AND ")}"
90
82
  end
91
83
  end
92
84
  end
data/lib/smoke.rb CHANGED
@@ -5,28 +5,16 @@ require 'json'
5
5
  require 'crack'
6
6
  require 'simple-rss'
7
7
 
8
-
9
8
  $:<< File.join(File.dirname(__FILE__))
10
9
 
11
10
  # Core ext
12
11
  require 'core_ext/hash.rb'
13
12
 
14
13
  module Smoke
15
- class << self
16
-
14
+ class << self
17
15
  @@active_sources = {}
18
16
  attr_reader :active_sources
19
17
 
20
- # Smoke sources can invoke access to themselves
21
- # via the register method:
22
- #
23
- # Smoke.register(Smoke::Source::YQL)
24
- #
25
- # Check the supplied sources for usage
26
- def register(mod)
27
- class_eval { include mod }
28
- end
29
-
30
18
  # Access registered smoke source instances
31
19
  #
32
20
  # Usage:
@@ -34,7 +22,7 @@ module Smoke
34
22
  # Smoke.yql(:ruby) do ....
35
23
  #
36
24
  # Smoke[:ruby]
37
- # => #<Smoke::Source::YQL::YQL:0x18428d4...
25
+ # => #<Smoke::Source::YQL::0x18428d4...
38
26
  def [](source)
39
27
  active_sources[source]
40
28
  end
@@ -49,11 +37,13 @@ module Smoke
49
37
  return source
50
38
  end
51
39
 
40
+ # Activates new instances of sources
41
+ # Source instances are stored within the
42
+ # @@active_sources class variable for later use
52
43
  def activate(name, source)
53
44
  if active_sources.has_key?(name)
54
45
  Smoke.log.warn "Smoke source activation: Source with idential name already initialized"
55
46
  end
56
-
57
47
  active_sources.update({ name => source })
58
48
  end
59
49
 
@@ -77,9 +67,13 @@ module Smoke
77
67
  # Smoke.log.error "message"
78
68
  # Smoke.log.warn "message"
79
69
  def log
80
- @logger ||= Logger.new($stdout)
70
+ @@log ||= Logger.new($stdout)
81
71
  end
82
72
 
73
+ def yql(name, &block); Smoke::Source::YQL.new(name, &block); end
74
+ def data(name, &block); Smoke::Source::Data.new(name, &block); end
75
+ def feed(name, &block); Smoke::Source::Feed.new(name, &block); end
76
+
83
77
  private
84
78
  def get_sources(sources = [])
85
79
  active_sources.find_all{|k, v| sources.include?(k) }
@@ -89,7 +83,6 @@ end
89
83
 
90
84
  require 'smoke/request'
91
85
  require 'smoke/origin'
92
- require 'smoke/delayed_block'
93
86
 
94
87
  class Object # :nodoc:
95
88
  include Smoke
@@ -97,9 +97,64 @@ describe Smoke::Origin do
97
97
  TestSource.source(:chain).sort(:header).should_not raise_error("NoMethodError")
98
98
  end
99
99
 
100
- describe "variable injection" do
101
- it "should allow variables to be left happlessly in your source definitions"
102
- it "should allow setting of variables"
103
- it "should error if output is called before a variable has been set"
100
+ describe "preperation" do
101
+ before :all do
102
+ @source = TestSource.source(:preperation)
103
+ end
104
+
105
+ it "should respond to prepare" do
106
+ @source.should respond_to(:prepare)
107
+ end
108
+
109
+ it "should require a block" do
110
+ lambda { @source.prepare }.should raise_error
111
+ lambda { @source.prepare {} }.should_not raise_error
112
+ end
113
+
114
+ describe "call order" do
115
+ before :all do
116
+ @url = "http://domain.tld/benschwarz/feed"
117
+ FakeWeb.register_uri(@url, :response => File.join(SPEC_DIR, 'supports', 'flickr-photo.json'))
118
+
119
+ Smoke.data :feed_preperation_call_order do
120
+ prepare do
121
+ url "http://domain.tld/#{username}/feed"
122
+ end
123
+
124
+ path :photos, :photo
125
+ end
126
+ end
127
+
128
+ describe "before setting variables" do
129
+ it "should fail" do
130
+ lambda { Smoke[:feed_preperation_call_order].output }.should raise_error(NameError)
131
+ end
132
+ end
133
+
134
+ describe "setting abstract properties" do
135
+ it "should not respond to a property that hasn't been set" do
136
+ lambda { Smoke[:feed_preperation_call_order].abstract }.should raise_error(NoMethodError)
137
+ end
138
+
139
+ it "should allow setting a property" do
140
+ lambda { Smoke[:feed_preperation_call_order].abstract(:value) }.should_not raise_error(NoMethodError)
141
+ Smoke[:feed_preperation_call_order].abstract.should == :value
142
+ end
143
+
144
+ it "should chain the source when setting a property" do
145
+ Smoke[:feed_preperation_call_order].abstract(:value).should be_an_instance_of(Smoke::Source::Data)
146
+ end
147
+ end
148
+
149
+ describe "after setting variables" do
150
+ it "should output successfully" do
151
+ lambda { Smoke[:feed_preperation_call_order].username("benschwarz").output }.should_not raise_error
152
+ end
153
+
154
+ it "should have used the correct url" do
155
+ Smoke[:feed_preperation_call_order].request.uri.should == @url
156
+ end
157
+ end
158
+ end
104
159
  end
105
160
  end
@@ -12,7 +12,7 @@ describe "'Data' source" do
12
12
  end
13
13
 
14
14
  it "should have been activated" do
15
- Smoke[:photos].should(be_an_instance_of(Smoke::Source::Data::Data))
15
+ Smoke[:photos].should(be_an_instance_of(Smoke::Source::Data))
16
16
  end
17
17
 
18
18
  it "should be a list of things" do
@@ -16,7 +16,7 @@ describe "Feed" do
16
16
 
17
17
 
18
18
  it "should have been activated" do
19
- Smoke[:slashdot].should(be_an_instance_of(Smoke::Source::Feed::Feed))
19
+ Smoke[:slashdot].should(be_an_instance_of(Smoke::Source::Feed))
20
20
  end
21
21
 
22
22
  it "should be a list of things" do
@@ -19,7 +19,7 @@ describe "YQL" do
19
19
  end
20
20
 
21
21
  it "should have been activated" do
22
- Smoke[:search].should(be_an_instance_of(Smoke::Source::YQL::YQL))
22
+ Smoke[:search].should(be_an_instance_of(Smoke::Source::YQL))
23
23
  end
24
24
 
25
25
  it "should be a list of things" do
@@ -31,8 +31,18 @@ describe "YQL" do
31
31
  Smoke[:search].output
32
32
  end
33
33
 
34
- it "should hold the url used to query" do
35
- Smoke[:search].request.uri.should == "http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20search.web%20WHERE%20query%20=%20'ruby'&format=json"
34
+ describe "url" do
35
+ it "should contain the base uri for yql" do
36
+ Smoke[:search].request.uri.should =~ /^http:\/\/query.yahooapis.com\/v1\/public\/yql?/
37
+ end
38
+
39
+ it "should be format=json" do
40
+ Smoke[:search].request.uri.should include("format=json")
41
+ end
42
+
43
+ it "should contain the query" do
44
+ Smoke[:search].request.uri.should include("SELECT%20*%20FROM%20search.web%20WHERE%20query%20=%20'ruby'")
45
+ end
36
46
  end
37
47
 
38
48
  it "should have renamed url to link" do
data/spec/smoke_spec.rb CHANGED
@@ -7,20 +7,6 @@ describe Smoke do
7
7
  @source_c = TestSource.source :c
8
8
  end
9
9
 
10
- describe "registration" do
11
- before do
12
- Smoke.register(Smoke::SecretSauce) # defined in supports/mayo.rb
13
- end
14
-
15
- it "should allow sources to register themselves" do
16
- Smoke.included_modules.should include(SecretSauce)
17
- end
18
-
19
- it "should have an instance method of 'mayo'" do
20
- Smoke.instance_methods.should include("mayo")
21
- end
22
- end
23
-
24
10
  describe "joining" do
25
11
  before do
26
12
  @joined = Smoke.join(:a, :b)
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.2.4
4
+ version: 0.3.4
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-05-31 00:00:00 -07:00
12
+ date: 2009-06-08 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -47,7 +47,6 @@ files:
47
47
  - lib/core_ext
48
48
  - lib/core_ext/hash.rb
49
49
  - lib/smoke
50
- - lib/smoke/delayed_block.rb
51
50
  - lib/smoke/origin.rb
52
51
  - lib/smoke/request.rb
53
52
  - lib/smoke/source
@@ -1,9 +0,0 @@
1
- class DelayedBlock # :nodoc:
2
- def initialize(&block)
3
- @block = block
4
- end
5
-
6
- def execute!
7
- @block.call
8
- end
9
- end