benschwarz-smoke 0.2.3 → 0.2.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
@@ -4,7 +4,11 @@ smoke is a Ruby based DSL that allows you to take data from YQL, RSS / Atom (and
4
4
  This "data" can then be re-represented, sorted and filtered. You can collect data from a multiude of sources, sort them on a common property
5
5
  and return a plain old ruby object or json (You could add in something to output XML too)
6
6
 
7
- In an early attempt to get feedback I created [this screencast](http://vimeo.com/4272804). I will do another once the library becomes closer to 1.0.0.
7
+ ## Media
8
+
9
+ * [Presentation from Melbourne #roro](http://www.slideshare.net/benschwarz/smoke-1371124)
10
+ * Early [screencast](http://vimeo.com/4272804) to get developer / peer feedback
11
+
8
12
 
9
13
  ## The concept
10
14
 
@@ -13,9 +17,8 @@ The concept comes from using [Yahoo Pipes](http://pipes.yahoo.com) to make littl
13
17
  ## How or what to contribute
14
18
 
15
19
  * Test everything you do
16
- * Add a source (I was thinking a source that took simply took a url and parsed whatever it got)
17
20
  * Add a way to output (XML, anyone?)
18
- * Examples of queries you'd like to be able to do
21
+ * Examples of queries you'd like to be able to do (email / github message them to me)
19
22
 
20
23
  ## API Examples
21
24
  ### YQL
@@ -53,13 +56,20 @@ Integrity [is running for smoke](http://integrity.ffolio.net/smoke)
53
56
 
54
57
  ### TODO (working on, just mental notes)
55
58
 
56
- * Look at moving object transformations into the origin class
59
+ * Clean up / simplify source activation process
60
+ * Consider variable injection (eg, allow to query sending a username within some aspect of the request)
61
+ * Implement a disaptch / request method that will actually thread property (event-machine)
62
+ * Checkout experimental fakeweb version to stub out the yql specs
63
+
64
+ #### Later / maybe
57
65
  * Allow for sources to explicitly set the content type being returned for those stupid content providers
58
- * Consider invokation methods (registering of sources, namespacing et al)
59
- * YQL Definitions
60
66
  * YQL w/oAuth
61
67
  * YQL Subqueries?
62
68
 
69
+ #### For wiki pages (docs, later)
70
+ * How to use `path`
71
+ * YQL Definitions
72
+
63
73
  ### Copyright
64
74
 
65
75
  Copyright (c) 2009 Ben Schwarz. See LICENSE for details.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 2
4
- :patch: 3
4
+ :patch: 4
data/lib/smoke/origin.rb CHANGED
@@ -5,10 +5,60 @@ 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 = []
8
+ @items, @transformation = [], []
9
+
9
10
  activate!
10
11
  instance_eval(&block) if block_given?
12
+ end
13
+
14
+ # Output your items in a range of formats (:ruby, :json and :yaml currently)
15
+ # Ruby is the default format and will automagically yielded from your source
16
+ #
17
+ # Usage
18
+ #
19
+ # output(:json)
20
+ # => "[{title: \"Ray\"}, {title: \"Peace\"}]"
21
+ def output(type = :ruby)
11
22
  dispatch if respond_to? :dispatch
23
+ output = (@items.length == 1) ? @items.first : @items
24
+
25
+ case type
26
+ when :ruby
27
+ return output
28
+ when :json
29
+ return ::JSON.generate(output)
30
+ when :yaml
31
+ return YAML.dump(output)
32
+ end
33
+ end
34
+
35
+ def items=(items) # :nodoc:
36
+ @items = items.flatten.map{|i| i.symbolize_keys! }
37
+ invoke_transformation
38
+ end
39
+
40
+ # Path allows you to traverse the tree of a the items returned to
41
+ # only give you access to what you're interested in.
42
+ #
43
+ # Usage:
44
+ # path :down, :to, :the, :data
45
+ #
46
+ # Will traverse through a tree as follows:
47
+ #
48
+ # {
49
+ # :down => {
50
+ # :to => {
51
+ # :the => {
52
+ # :data => []
53
+ # }
54
+ # }
55
+ # }
56
+ # }
57
+ #
58
+ # You will need to help smoke find an array of
59
+ # items that you're interested in.
60
+ def path(*path)
61
+ @path = path
12
62
  end
13
63
 
14
64
  # Transform each item
@@ -18,7 +68,7 @@ module Smoke
18
68
  # rename(:href => :link)
19
69
  # end
20
70
  def emit(&block)
21
- (@transformations ||= []) << DelayedBlock.new(&block)
71
+ @transformation << DelayedBlock.new(&block)
22
72
  end
23
73
 
24
74
  # Re-sort items by a particular key
@@ -43,7 +93,7 @@ module Smoke
43
93
  @items.reject! {|i| (i[key] =~ matcher) ? false : true }
44
94
  return self
45
95
  end
46
-
96
+
47
97
  # Discard items that do not match the regex
48
98
  #
49
99
  # Usage (chained):
@@ -57,7 +107,7 @@ module Smoke
57
107
  @items.reject! {|i| (i[key] =~ matcher) ? true : false }
58
108
  return self
59
109
  end
60
-
110
+
61
111
  # Rename one or many keys at a time
62
112
  # Suggested that you run it within an each block, but it makes no difference
63
113
  # other than readability
@@ -71,25 +121,7 @@ module Smoke
71
121
  @items.each {|item| item.rename(*args) }
72
122
  return self
73
123
  end
74
-
75
- # Output your items in a range of formats (:ruby, :json and :yaml currently)
76
- # Ruby is the default format and will automagically yielded from your source
77
- #
78
- # Usage
79
- #
80
- # output(:json)
81
- # => "[{title: \"Ray\"}, {title: \"Peace\"}]"
82
- def output(type = :ruby)
83
- case type
84
- when :ruby
85
- return @items
86
- when :json
87
- return ::JSON.generate(@items)
88
- when :yaml
89
- return YAML.dump(@items)
90
- end
91
- end
92
-
124
+
93
125
  # Truncate your result set to this many objects
94
126
  #
95
127
  # Usage
@@ -101,14 +133,15 @@ module Smoke
101
133
  return self
102
134
  end
103
135
 
104
- def items=(items)
105
- @items = items.map{|i| i.symbolize_keys! }
106
- invoke_transformations
136
+ private
137
+ def invoke_transformation
138
+ @transformation.each{|t| t.execute! } unless @transformation.nil?
107
139
  end
108
140
 
109
- private
110
- def invoke_transformations
111
- @transformations.each{|t| t.execute! } unless @transformations.nil?
141
+ def drill(*path)
142
+ path.inject(nil) do |obj, pointer|
143
+ obj = obj.nil? ? pointer : obj[pointer]
144
+ end
112
145
  end
113
146
 
114
147
  def activate!
data/lib/smoke/request.rb CHANGED
@@ -5,7 +5,7 @@ module Smoke
5
5
 
6
6
  def initialize(uri, msg)
7
7
  @uri = URI.parse(uri)
8
- Smoke.log.error "Smoke Request: Failed to get from #{@uri.host} via #{@uri.scheme}\n#{msg}"
8
+ Smoke.log.error "Smoke Request: Failed to get from #{@uri} (#{msg})"
9
9
  end
10
10
  end
11
11
 
@@ -32,25 +32,11 @@ module Smoke
32
32
  @url = source_url
33
33
  end
34
34
 
35
- # Path allows you to traverse the tree of a the items returned to
36
- # only give you access to what you're interested in. In the case
37
- # of the comments in this document I traverse to the photos returned.
38
- def path(*path)
39
- @path = path
40
- end
41
-
42
35
  protected
43
36
  def dispatch
44
37
  @request = Smoke::Request.new(@url)
45
38
  self.items = (@path.nil?) ? @request.body : drill(@request.body, *@path)
46
39
  end
47
-
48
- private
49
- def drill(*path)
50
- path.inject(nil) do |obj, pointer|
51
- obj = obj.nil? ? pointer : obj[pointer]
52
- end
53
- end
54
40
  end
55
41
  end
56
42
  end
@@ -16,6 +16,7 @@ module Smoke
16
16
  # where :query, "ruby"
17
17
  # end
18
18
  class YQL < Origin
19
+ API_BASE = "http://query.yahooapis.com/v1/public/yql"
19
20
  attr_reader :request
20
21
 
21
22
  # Select indicates what YQL will be selecting
@@ -26,7 +27,7 @@ module Smoke
26
27
  # => "SELECT title"
27
28
  # select :title, :description
28
29
  # => "SELECT title, description"
29
- def select(what)
30
+ def select(what = :all)
30
31
  @select = what.join(",") and return if what.is_a? Array
31
32
  @select = "*" and return if what == :all
32
33
  @select = what.to_s
@@ -53,20 +54,38 @@ module Smoke
53
54
  @where << "#{column.to_s} = '#{value}'"
54
55
  end
55
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
+
56
66
  protected
67
+ def params
68
+ @params || @params = {}
69
+ end
70
+
57
71
  def dispatch
58
72
  @request = Smoke::Request.new(build_uri)
59
- self.items = @request.body[:query][:results][:result]
73
+ self.items = [(@path.nil?) ? @request.body : drill(@request.body, *@path)]
60
74
  end
61
75
 
62
76
  private
63
-
64
77
  def build_uri
65
- "http://query.yahooapis.com/v1/public/yql?q=#{build_query}&format=json"
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("&"))
66
85
  end
67
86
 
68
87
  def build_query
69
- URI.encode("SELECT #{@select} FROM #{@from} WHERE #{@where.join(" AND ")}")
88
+ "SELECT #{@select} FROM #{@from} WHERE #{@where.join(" AND ")}"
70
89
  end
71
90
  end
72
91
  end
data/lib/smoke.rb CHANGED
@@ -12,7 +12,7 @@ $:<< File.join(File.dirname(__FILE__))
12
12
  require 'core_ext/hash.rb'
13
13
 
14
14
  module Smoke
15
- class << self
15
+ class << self
16
16
 
17
17
  @@active_sources = {}
18
18
  attr_reader :active_sources
@@ -18,6 +18,10 @@ describe Smoke::Origin do
18
18
  it "should be ordered by title" do
19
19
  @origin.output.first[:title].should == "Kangaroo"
20
20
  end
21
+
22
+ it "should output a single hash rather than a hash in an array when there is one item" do
23
+ Smoke[:test].truncate(1).output.should == {:title => "Kangaroo"}
24
+ end
21
25
  end
22
26
 
23
27
  describe "transformations" do
@@ -44,7 +48,7 @@ describe Smoke::Origin do
44
48
  end
45
49
 
46
50
  it "should only contain items that match" do
47
- Smoke[:keep].keep(:head, /^K/).output.should == [{:head => "Kangaroo"}]
51
+ Smoke[:keep].keep(:head, /^K/).output.should == {:head => "Kangaroo"}
48
52
  end
49
53
 
50
54
  it "should discard items" do
@@ -52,25 +56,36 @@ describe Smoke::Origin do
52
56
  end
53
57
 
54
58
  it "should not contain items that match" do
55
- Smoke[:discard].discard(:head, /^K/).output.should == [{:head => "Platypus"}]
59
+ Smoke[:discard].discard(:head, /^K/).output.should == {:head => "Platypus"}
56
60
  end
57
61
  end
58
62
  end
59
63
 
60
- it "should output" do
61
- @origin.output.should be_an_instance_of(Array)
62
- end
63
-
64
- it "should output two items" do
65
- @origin.output.size.should == 2
66
- end
67
-
68
- it "should output json" do
69
- @origin.output(:json).should =~ /^\[\{/
70
- end
71
-
72
- it "should output yml" do
73
- @origin.output(:yaml).should =~ /--- \n- :title:/
64
+ describe "output" do
65
+ it "should output" do
66
+ @origin.output.should be_an_instance_of(Array)
67
+ end
68
+
69
+ it "should output two items" do
70
+ @origin.output.size.should == 2
71
+ end
72
+
73
+ it "should output json" do
74
+ @origin.output(:json).should =~ /^\[\{/
75
+ end
76
+
77
+ it "should output yml" do
78
+ @origin.output(:yaml).should =~ /--- \n- :title:/
79
+ end
80
+
81
+ it "should dispatch when output is called" do
82
+ TestSource.source(:no_dispatch)
83
+ Smoke[:no_dispatch].should_not_receive(:dispatch)
84
+
85
+ TestSource.source(:dispatch)
86
+ Smoke[:dispatch].should_receive(:dispatch)
87
+ Smoke[:dispatch].output
88
+ end
74
89
  end
75
90
 
76
91
  it "method chaining" do
@@ -81,4 +96,10 @@ describe Smoke::Origin do
81
96
  it "should softly error when attempting to sort on a key that doesn't exist" do
82
97
  TestSource.source(:chain).sort(:header).should_not raise_error("NoMethodError")
83
98
  end
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"
104
+ end
84
105
  end
@@ -27,16 +27,22 @@ describe "Feed" do
27
27
  Smoke[:slashdot].should respond_to(:url)
28
28
  end
29
29
 
30
- it "should accept multiple urls" do
31
- Smoke[:slashdot].requests.should be_an_instance_of(Array)
32
- end
33
-
34
- it "should hold the url used to query" do
35
- Smoke[:slashdot].requests.collect{|r| r.uri }.should include("http://slashdot.org/index.rdf")
36
- end
37
-
38
- it "should have renamed url to link" do
39
- Smoke[:slashdot].items.first.should have_key(:url)
40
- Smoke[:slashdot].items.first.should_not have_key(:link)
30
+ describe "after dispatch / query" do
31
+ before do
32
+ Smoke[:slashdot].output
33
+ end
34
+
35
+ it "should accept multiple urls" do
36
+ Smoke[:slashdot].requests.should be_an_instance_of(Array)
37
+ end
38
+
39
+ it "should hold the url used to query" do
40
+ Smoke[:slashdot].requests.collect{|r| r.uri }.should include("http://slashdot.org/index.rdf")
41
+ end
42
+
43
+ it "should have renamed url to link" do
44
+ Smoke[:slashdot].output.first.should have_key(:url)
45
+ Smoke[:slashdot].output.first.should_not have_key(:link)
46
+ end
41
47
  end
42
48
  end
@@ -4,15 +4,13 @@ describe "YQL" do
4
4
  before :all do
5
5
  # Fake web does not yet support regex matched uris
6
6
 
7
- #FakeWeb.register_uri("query.yahooapis.com/*") do |response|
8
- # response.body = File.read(File.join(SPEC_DIR, 'supports', 'search-web.yql'))
9
- # response.content_type "text/json"
10
- #end
7
+ FakeWeb.register_uri("query.yahooapis.com/*", :response => File.join(SPEC_DIR, 'supports', 'search-web.yql'))
11
8
 
12
9
  Smoke.yql(:search) do
13
10
  select :all
14
11
  from "search.web"
15
12
  where :query, "ruby"
13
+ path :query, :results, :result
16
14
 
17
15
  emit do
18
16
  rename(:url => :link)
@@ -28,16 +26,50 @@ describe "YQL" do
28
26
  Smoke[:search].items.should be_an_instance_of(Array)
29
27
  end
30
28
 
31
- it "should hold the url used to query" do
32
- 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"
33
- end
34
-
35
- it "should have renamed url to link" do
36
- Smoke[:search].items.first.should have_key(:link)
37
- Smoke[:search].items.first.should_not have_key(:href)
29
+ describe "after dispatch" do
30
+ before do
31
+ Smoke[:search].output
32
+ end
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"
36
+ end
37
+
38
+ it "should have renamed url to link" do
39
+ Smoke[:search].output.first.should have_key(:link)
40
+ Smoke[:search].output.first.should_not have_key(:url)
41
+ end
42
+
43
+ it "should output a ruby object" do
44
+ Smoke[:search].output.should be_an_instance_of(Array)
45
+ end
38
46
  end
39
47
 
40
- it "should output a ruby object" do
41
- Smoke[:search].output.should be_an_instance_of(Array)
48
+ describe "yql definitions" do
49
+ before do
50
+ Smoke.yql(:smoke) do
51
+ use "http://datatables.org/alltables.env"
52
+
53
+ select :all
54
+ from "github.repo"
55
+ where :id, "benschwarz"
56
+ where :repo, "smoke"
57
+ path :query, :results
58
+ end
59
+
60
+ Smoke[:smoke].output # Force execution
61
+ end
62
+
63
+ it "should be a respository" do
64
+ Smoke[:smoke].output.should have_key(:repository)
65
+ end
66
+
67
+ it "should respond to use" do
68
+ Smoke[:smoke].should respond_to(:use)
69
+ end
70
+
71
+ it "should contain 'env' within the query string" do
72
+ Smoke[:smoke].request.uri.should =~ /env=/
73
+ end
42
74
  end
43
75
  end
data/spec/spec.opts CHANGED
@@ -1 +1 @@
1
- -c --format specdoc
1
+ -c
@@ -1,8 +1,6 @@
1
1
  module TestSource
2
2
  def self.source(name, &block)
3
- source = Smoke::Origin.allocate
4
- source.stub!(:dispatch)
5
- source.send(:initialize, name, &block || Proc.new {})
3
+ source = Smoke::Origin.new(name, &block || Proc.new {})
6
4
  source.items = [
7
5
  {:head => "Platypus"},
8
6
  {:head => "Kangaroo"}
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.3
4
+ version: 0.2.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-04-29 00:00:00 -07:00
12
+ date: 2009-05-31 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency