smoke 0.5.17 → 0.5.19

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 (43) hide show
  1. data/README.markdown +5 -5
  2. data/Rakefile +1 -0
  3. data/VERSION.yml +2 -2
  4. data/lib/core_ext/array.rb +12 -0
  5. data/lib/smoke.rb +24 -13
  6. data/lib/smoke/cache.rb +4 -4
  7. data/lib/smoke/origin.rb +6 -11
  8. data/lib/smoke/request.rb +5 -14
  9. data/lib/smoke/transformer.rb +7 -0
  10. data/lib/smoke/transformers/json.rb +15 -0
  11. data/lib/smoke/transformers/ruby.rb +11 -0
  12. data/lib/smoke/{output → transformers}/xml.rb +12 -6
  13. data/lib/smoke/transformers/yaml.rb +15 -0
  14. data/spec/smoke/origin_spec.rb +4 -0
  15. data/spec/smoke/request_spec.rb +7 -0
  16. data/spec/smoke/{output → transformers}/xml_spec.rb +4 -6
  17. data/spec/smoke/transformers_spec.rb +35 -0
  18. data/spec/smoke_spec.rb +18 -10
  19. data/spec/supports/twitter_timeline.json +22 -0
  20. metadata +23 -30
  21. data/rdoc/classes/Smoke.html +0 -260
  22. data/rdoc/classes/Smoke/Origin.html +0 -340
  23. data/rdoc/classes/Smoke/Source/Data.html +0 -126
  24. data/rdoc/classes/Smoke/Source/Feed.html +0 -117
  25. data/rdoc/classes/Smoke/Source/YQL.html +0 -223
  26. data/rdoc/created.rid +0 -1
  27. data/rdoc/files/README_markdown.html +0 -180
  28. data/rdoc/files/lib/core_ext/hash_rb.html +0 -49
  29. data/rdoc/files/lib/smoke/origin_rb.html +0 -49
  30. data/rdoc/files/lib/smoke/request_rb.html +0 -49
  31. data/rdoc/files/lib/smoke/source/data_rb.html +0 -49
  32. data/rdoc/files/lib/smoke/source/feed_rb.html +0 -49
  33. data/rdoc/files/lib/smoke/source/join_rb.html +0 -49
  34. data/rdoc/files/lib/smoke/source/yql_rb.html +0 -49
  35. data/rdoc/files/lib/smoke_rb.html +0 -65
  36. data/rdoc/fr_class_index.html +0 -21
  37. data/rdoc/fr_file_index.html +0 -28
  38. data/rdoc/fr_method_index.html +0 -4459
  39. data/rdoc/index.html +0 -15
  40. data/rdoc/rdoc-style.css +0 -319
  41. data/spec/smoke/input/xls_spec.rb +0 -15
  42. data/spec/smoke/shared_spec.rb +0 -182
  43. data/spec/supports/gov_act_toliets.xls +0 -0
@@ -14,6 +14,7 @@ Then you can output as a plain ruby object or one of your other favourites (JSON
14
14
 
15
15
  ## Media
16
16
 
17
+ * [Presentation from Webjam11 in Perth](http://www.slideshare.net/benschwarz/how-to-reinterpret-the-web-in-180-seconds)
17
18
  * [Presentation from Melbourne #roro](http://www.slideshare.net/benschwarz/smoke-1371124)
18
19
  * Early [screencast](http://vimeo.com/4272804) to get developer / peer feedback
19
20
 
@@ -77,11 +78,10 @@ Execution:
77
78
 
78
79
 
79
80
  ### TODO (working on, just mental notes)
80
- #### Later / maybe
81
- * YQL w/oAuth
82
- * YQL Subqueries?
83
- * Implement basic auth for sources
84
-
81
+ * Items returned from smoke to be Hashie "rich" objects
82
+ * Output as a web feed (atom, rss)
83
+ * How to push through values like "author"?
84
+ * Passing modified time headers through to the atom formatter
85
85
 
86
86
  #### For wiki pages (docs, later)
87
87
  * Document all sources with their irrespective differential methods
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ begin
18
18
  gem.add_dependency("moneta", "0.6.0")
19
19
  gem.add_dependency("rest-client", "1.0.3")
20
20
  gem.add_dependency("nokogiri", "1.3.2")
21
+ gem.add_dependency("registry", ">=0.1.2")
21
22
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
23
  end
23
24
  rescue LoadError
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 17
2
+ :minor: 5
3
+ :patch: 19
3
4
  :major: 0
4
5
  :build:
5
- :minor: 5
@@ -0,0 +1,12 @@
1
+ class Array # :nodoc:
2
+
3
+ # Thanks merb!
4
+ def symbolize_keys!
5
+ each do |k,v|
6
+ sym = k.respond_to?(:to_sym) ? k.to_sym : k
7
+ self[sym] = Hash === v ? v.symbolize_keys! : v
8
+ delete(k) unless k == sym
9
+ end
10
+ self
11
+ end
12
+ end
@@ -1,5 +1,6 @@
1
1
  require 'logger'
2
2
  require 'digest/md5'
3
+ require 'yaml'
3
4
 
4
5
  require 'simple-rss'
5
6
  require 'json'
@@ -7,8 +8,11 @@ require 'crack'
7
8
  require 'moneta'
8
9
  require 'restclient'
9
10
  require 'nokogiri'
11
+ require 'registry'
10
12
 
11
- module Smoke
13
+ module Smoke
14
+ class NotRegistered < StandardError; end
15
+
12
16
  class << self
13
17
  @@active_sources = {}
14
18
  @@config = {
@@ -22,6 +26,15 @@ module Smoke
22
26
  }
23
27
  }
24
28
 
29
+ def root
30
+ File.join(File.dirname(__FILE__))
31
+ end
32
+
33
+ def version
34
+ @@version ||= YAML::load(File.read("#{root}/../VERSION.YML"))
35
+ "#{@@version[:major]}.#{@@version[:minor]}.#{@@version[:patch]}"
36
+ end
37
+
25
38
  # Access registered smoke source instances
26
39
  #
27
40
  # Define your source:
@@ -39,6 +52,8 @@ module Smoke
39
52
  # Usage:
40
53
  # Smoke.twitter(:username => "benschwarz")
41
54
  def method_missing(sym, args = {})
55
+ raise NotRegistered, "Smoke source not registered" if self[sym].nil?
56
+
42
57
  args.each_pair {|k, v| self[sym].send(k, v) }
43
58
  self[sym]
44
59
  end
@@ -47,8 +62,8 @@ module Smoke
47
62
  # Source instances are stored within the
48
63
  # @@active_sources class variable for later use
49
64
  def activate(name, source)
50
- if active_sources.has_key?(name)
51
- Smoke.log.warn "Smoke source activation: Source with idential name already initialized"
65
+ if active_sources.key?(name)
66
+ Smoke.log.warn "Smoke source activation: Source with identical name already initialized"
52
67
  end
53
68
  active_sources.update({ name => source })
54
69
  end
@@ -115,15 +130,11 @@ module Smoke
115
130
  # end
116
131
  def join(*names, &block); Smoke::Join.new(names, &block); end
117
132
  end
133
+
134
+ autoload :YQL, "smoke/source/yql"
135
+ autoload :Data, "smoke/source/data"
136
+ autoload :Feed, "smoke/source/feed"
137
+ autoload :Join, "smoke/source/join"
118
138
  end
119
139
 
120
- %w(core_ext/hash core_ext/string smoke/cache smoke/request smoke/origin smoke/output/xml).each {|r| require File.join(File.dirname(__FILE__), r)}
121
-
122
- # Autoload the source classes
123
- %w(YQL Data Feed Join).each do |r|
124
- Smoke.autoload(r.to_sym, File.join(File.dirname(__FILE__), "smoke", "source", r.downcase))
125
- end
126
-
127
- class Object # :nodoc:
128
- include Smoke
129
- end
140
+ Dir["#{File.dirname(__FILE__)}/{core_ext,smoke,smoke/transformers}/*.rb"].each {|r| require r}
@@ -39,8 +39,8 @@ module Smoke
39
39
 
40
40
  def query(uri, options)
41
41
  request = RestClient.get(uri, options)
42
- write(uri, request, request.headers[:content_type]) if enabled?
43
- {:body => request, :content_type => request.headers[:content_type]}
42
+ write(uri, request, request.headers) if enabled?
43
+ {:body => request, :headers => request.headers}
44
44
  end
45
45
 
46
46
  def read(uri)
@@ -48,8 +48,8 @@ module Smoke
48
48
  return cache[key]
49
49
  end
50
50
 
51
- def write(uri, body, content_type)
52
- store = {:body => body, :content_type => content_type}
51
+ def write(uri, body, headers)
52
+ store = {:body => body, :headers => headers}
53
53
  self.cache.store(generate_key(uri), store, :expire_in => Smoke.config[:cache][:expiry])
54
54
  end
55
55
 
@@ -1,5 +1,7 @@
1
1
  module Smoke
2
2
  class Origin
3
+ class UnavailableFormat < StandardError; end
4
+
3
5
  attr_reader :items, :requirements, :exposed
4
6
  attr_accessor :name
5
7
 
@@ -13,7 +15,7 @@ module Smoke
13
15
  instance_eval(&block) if block_given?
14
16
  end
15
17
 
16
- # Output your items in a range of formats (:ruby, :json and :yaml currently)
18
+ # Output your items in a range of formats (:ruby, :json, :xml and :yaml currently)
17
19
  # Ruby is the default format and will automagically yielded from your source
18
20
  #
19
21
  # Usage
@@ -24,16 +26,9 @@ module Smoke
24
26
  prepare!
25
27
  dispatch if respond_to? :dispatch
26
28
 
27
- case type
28
- when :json
29
- return ::JSON.generate(@items)
30
- when :yaml
31
- return YAML.dump(@items)
32
- when :xml
33
- return Smoke::Output::XML.generate(@name, @items)
34
- else
35
- return @items
36
- end
29
+ Transformer.for(type).generate(name, items)
30
+ rescue Registry::NotRegistered => e
31
+ raise UnavailableFormat, e.message
37
32
  end
38
33
 
39
34
  def items=(response) # :nodoc:
@@ -9,7 +9,7 @@ module Smoke
9
9
  end
10
10
  end
11
11
 
12
- SUPPORTED_TYPES = %w(json xml javascript excel)
12
+ SUPPORTED_TYPES = %w(json xml javascript)
13
13
  @@request_options = {
14
14
  :user_agent => Smoke.config[:user_agent],
15
15
  :accept_encoding => "gzip, deflate"
@@ -30,7 +30,7 @@ module Smoke
30
30
  def dispatch
31
31
  get = Smoke::Cache.fetch @uri, @@request_options
32
32
  @body = get[:body]
33
- @content_type = get[:content_type]
33
+ @content_type = get[:headers][:content_type]
34
34
 
35
35
  parse! unless @options[:raw_response]
36
36
 
@@ -39,18 +39,9 @@ module Smoke
39
39
  end
40
40
 
41
41
  def parse!
42
- case type
43
- when :json, :javascript
44
- @body = ::Crack::JSON.parse(@body).symbolize_keys!
45
- when :xml
46
- @body = ::Crack::XML.parse(@body).symbolize_keys!
47
- when :excel
48
- # Convert the excel document into an XML document
49
- doc = @body
50
- ::Crack::XML.parse().symbolize_keys!
51
- when :unknown
52
- Smoke.log.warn "Smoke Request: Format unknown for #{@uri} (#{@content_type})"
53
- end
42
+ @body = Transformer.for(type).parse(body).symbolize_keys!
43
+ rescue Registry::NotRegistered
44
+ Smoke.log.warn "Smoke Request: Format unknown for #{@uri} (#{@content_type})"
54
45
  end
55
46
  end
56
47
  end
@@ -0,0 +1,7 @@
1
+ module Smoke
2
+ # A class that will either transfer objects from Ruby to another format
3
+ # or it will parse objects into Ruby. (JSON, XML & Yaml)
4
+ class Transformer
5
+ extend Registry
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module Smoke
2
+ module Transformers
3
+ class Json < Transformer
4
+ identifier :json
5
+
6
+ def self.generate(tree_name, objects)
7
+ ::JSON.generate(objects)
8
+ end
9
+
10
+ def self.parse(string)
11
+ ::Crack::JSON.parse(string)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Smoke
2
+ module Transformers
3
+ class Ruby < Transformer
4
+ identifier :ruby
5
+
6
+ def self.generate(tree_name, objects)
7
+ objects
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,23 +1,29 @@
1
1
  module Smoke
2
- module Output
3
- class XML
2
+ module Transformers
3
+ class XML < Transformer
4
+ identifier :xml
5
+
4
6
  def self.generate(tree_name, items)
5
7
  builder = Nokogiri::XML::Builder.new do |xml|
6
8
  xml.items {
7
9
  items.each do |item|
8
10
  xml.item {
9
- %w(id type class).each{|m| item["#{m}_".to_sym] = item.delete(m.to_sym) }
10
-
11
+ %w(id type class fork).each{|m| item["#{m}_".to_sym] = item.delete(m.to_sym) }
12
+
11
13
  item.each do |k, v|
12
- xml.send(k, v)
14
+ xml.__send__(k, v)
13
15
  end
14
16
  }
15
17
  end
16
18
  }
17
19
  end
18
-
20
+
19
21
  builder.to_xml
20
22
  end
23
+
24
+ def self.parse(string)
25
+ ::Crack::XML.parse(string)
26
+ end
21
27
  end
22
28
  end
23
29
  end
@@ -0,0 +1,15 @@
1
+ module Smoke
2
+ module Transformers
3
+ class Yaml < Transformer
4
+ identifier :yml, :yaml
5
+
6
+ def self.generate(tree_name, objects)
7
+ YAML.dump(objects)
8
+ end
9
+
10
+ def self.parse(string)
11
+ YAML.load(string)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -49,6 +49,10 @@ describe Smoke::Origin do
49
49
  Smoke.test.output(:xml).should include "<?xml version=\"1.0\"?>"
50
50
  end
51
51
 
52
+ it "should raise a UnavailableFormat error" do
53
+ lambda { Smoke.test.output(:xyz) }.should raise_error(Smoke::Origin::UnavailableFormat)
54
+ end
55
+
52
56
  describe "filtering" do
53
57
  before :all do
54
58
  TestSource.source(:keep) do
@@ -35,6 +35,13 @@ describe Smoke::Request do
35
35
  end
36
36
  end
37
37
 
38
+ it "should handle direct array responses" do
39
+ url = "http://fake.tld/canned/twitter.json"
40
+ FakeWeb.register_uri(:get, url, :response => File.join(SPEC_DIR, 'supports', 'twitter_timeline.json'))
41
+
42
+ lambda { Smoke::Request.new(url) }.should_not raise_error(NoMethodError)
43
+ end
44
+
38
45
  describe "format returns" do
39
46
  it "should have a content type of :manual" do
40
47
  request = Smoke::Request.new(@url, :type => :manual)
@@ -1,18 +1,18 @@
1
1
  require File.join(File.dirname(__FILE__), "..", "..", "spec_helper.rb")
2
2
 
3
- describe Smoke::Output::XML do
3
+ describe Smoke::Transformers::XML do
4
4
  before do
5
5
  @tree = :tree
6
6
  @items = [
7
7
  {:id => 1, :class => "first", :type => "mammal", :animal => "monkey"},
8
8
  {:id => 2, :class => "second", :type => "mammal", :animal => "elephant"}
9
9
  ]
10
- @xml = Smoke::Output::XML.generate(@tree, @items)
10
+ @xml = Smoke::Transformers::XML.generate(@tree, @items)
11
11
  @document = Nokogiri::XML(@xml)
12
12
  end
13
13
 
14
14
  it "should respond to generate" do
15
- Smoke::Output::XML.should respond_to(:generate)
15
+ Smoke::Transformers::XML.should respond_to(:generate)
16
16
  end
17
17
 
18
18
  it "should start the tree off with a named key" do
@@ -20,9 +20,7 @@ describe Smoke::Output::XML do
20
20
  end
21
21
 
22
22
  it "should contain items" do
23
- @document.css("items").each do |item|
24
- item.content.should =~ /monkey/
25
- end
23
+ @document.css("item").size.should == 2
26
24
  end
27
25
  end
28
26
 
@@ -0,0 +1,35 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe Smoke::Transformer do
4
+ describe "parsers" do
5
+ it "should respond to parse" do
6
+ Smoke::Transformers::Json.should respond_to(:parse)
7
+ end
8
+
9
+ it "should respond to parse" do
10
+ Smoke::Transformers::XML.should respond_to(:parse)
11
+ end
12
+
13
+ it "should respond to parse" do
14
+ Smoke::Transformers::Yaml.should respond_to(:parse)
15
+ end
16
+ end
17
+
18
+ describe "generators" do
19
+ it "should respond to generate" do
20
+ Smoke::Transformers::Json.should respond_to(:generate)
21
+ end
22
+
23
+ it "should respond to generate" do
24
+ Smoke::Transformers::XML.should respond_to(:generate)
25
+ end
26
+
27
+ it "should respond to generate" do
28
+ Smoke::Transformers::Yaml.should respond_to(:generate)
29
+ end
30
+
31
+ it "should respond to generate" do
32
+ Smoke::Transformers::Ruby.should respond_to(:generate)
33
+ end
34
+ end
35
+ end
@@ -6,6 +6,16 @@ describe Smoke do
6
6
  @source_b = TestSource.source :b
7
7
  end
8
8
 
9
+ it "should have a root" do
10
+ Smoke.should respond_to(:root)
11
+ Smoke.root.should be_an_instance_of(String)
12
+ end
13
+
14
+ it "should have a version string" do
15
+ Smoke.should respond_to :version
16
+ Smoke.version.should be_an_instance_of(String)
17
+ end
18
+
9
19
  describe "active sources" do
10
20
  it "should allow access to sources via an array accessor" do
11
21
  Smoke[:a].should == @source_a
@@ -17,17 +27,15 @@ describe Smoke do
17
27
 
18
28
  it "should have its name as the hash key" do
19
29
  key = Smoke.active_sources.keys.first
20
- Smoke.active_sources[key].name.should == key
30
+ Smoke.active_sources[key].name.to_s.should == key.to_s
21
31
  end
22
32
 
23
- describe "accessing via method call" do
24
- it "should allow access to the sources via a method call" do
25
- Smoke.a.should == @source_a
26
- end
27
-
28
- it "should throw an argument error when missing" do
29
- Smoke.b.should raise_error(NoMethodError)
30
- end
33
+ it "should allow access to the sources via a method call" do
34
+ Smoke.a.should == @source_a
35
+ end
36
+
37
+ it "should throw an error when missing" do
38
+ lambda { Smoke.frank }.should raise_error(Smoke::NotRegistered)
31
39
  end
32
40
 
33
41
  it "should be able to be renamed" do
@@ -40,7 +48,7 @@ describe Smoke do
40
48
  Smoke[:b].name.should == "b"
41
49
  end
42
50
  end
43
-
51
+
44
52
  describe "configuration" do
45
53
  it "should be configurable" do
46
54
  Smoke.should respond_to(:configure)