smoke 0.0.3 → 0.5.9
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/LICENSE +20 -0
- data/README.markdown +101 -0
- data/Rakefile +53 -4
- data/VERSION.yml +4 -0
- data/examples/web_search_joined.rb +25 -0
- data/examples/yql_web_search.rb +11 -0
- data/lib/core_ext/hash.rb +20 -0
- data/lib/core_ext/string.rb +6 -0
- data/lib/smoke.rb +103 -3
- data/lib/smoke/cache.rb +56 -0
- data/lib/smoke/origin.rb +251 -0
- data/lib/smoke/request.rb +57 -0
- data/lib/smoke/source/data.rb +39 -0
- data/lib/smoke/source/feed.rb +24 -0
- data/lib/smoke/source/join.rb +33 -0
- data/lib/smoke/source/yql.rb +85 -0
- data/rdoc/classes/Smoke.html +260 -0
- data/rdoc/classes/Smoke/Origin.html +340 -0
- data/rdoc/classes/Smoke/Source/Data.html +126 -0
- data/rdoc/classes/Smoke/Source/Feed.html +117 -0
- data/rdoc/classes/Smoke/Source/YQL.html +223 -0
- data/rdoc/created.rid +1 -0
- data/rdoc/files/README_markdown.html +180 -0
- data/rdoc/files/lib/core_ext/hash_rb.html +49 -0
- data/rdoc/files/lib/smoke/origin_rb.html +49 -0
- data/rdoc/files/lib/smoke/request_rb.html +49 -0
- data/rdoc/files/lib/smoke/source/data_rb.html +49 -0
- data/rdoc/files/lib/smoke/source/feed_rb.html +49 -0
- data/rdoc/files/lib/smoke/source/join_rb.html +49 -0
- data/rdoc/files/lib/smoke/source/yql_rb.html +49 -0
- data/rdoc/files/lib/smoke_rb.html +65 -0
- data/rdoc/fr_class_index.html +21 -0
- data/rdoc/fr_file_index.html +28 -0
- data/rdoc/fr_method_index.html +4459 -0
- data/rdoc/index.html +15 -0
- data/rdoc/rdoc-style.css +319 -0
- data/spec/core_ext/hash_spec.rb +25 -0
- data/spec/smoke/cache_spec.rb +75 -0
- data/spec/smoke/origin_spec.rb +220 -0
- data/spec/smoke/request_spec.rb +49 -0
- data/spec/smoke/shared_spec.rb +182 -0
- data/spec/smoke/source/data_spec.rb +65 -0
- data/spec/smoke/source/feed_spec.rb +49 -0
- data/spec/smoke/source/join_spec.rb +53 -0
- data/spec/smoke/source/yql_spec.rb +111 -0
- data/spec/smoke_spec.rb +51 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/supports/amc_pacer.json.yql +10 -0
- data/spec/supports/datatables.yql +12 -0
- data/spec/supports/flickr-photo.json +7 -0
- data/spec/supports/gzip_response.txt +0 -0
- data/spec/supports/search-web.json.yql +12 -0
- data/spec/supports/search-web.xml.yql +83 -0
- data/spec/supports/slashdot.xml +98 -0
- data/spec/supports/test_source.rb +10 -0
- metadata +144 -67
- data/History.txt +0 -13
- data/License.txt +0 -21
- data/Manifest.txt +0 -44
- data/README.txt +0 -3
- data/bin/smoke +0 -41
- data/bin/smoked +0 -43
- data/config/hoe.rb +0 -70
- data/config/requirements.rb +0 -17
- data/contrib/processors/smoke/remote_port_status_check_processor.rb +0 -10
- data/contrib/processors/smoke/system_load_processor.rb +0 -52
- data/contrib/signals/smoke/remote_port_status_check.rb +0 -39
- data/contrib/signals/smoke/system_load.rb +0 -23
- data/lib/smoke/client.rb +0 -50
- data/lib/smoke/client/connection.rb +0 -44
- data/lib/smoke/client/signal_runner.rb +0 -40
- data/lib/smoke/client/version.rb +0 -11
- data/lib/smoke/server.rb +0 -18
- data/lib/smoke/server/signal_handler.rb +0 -64
- data/lib/smoke/server/signal_router.rb +0 -38
- data/lib/smoke/server/version.rb +0 -11
- data/lib/smoke/signal.rb +0 -19
- data/lib/smoke/signal_processor.rb +0 -29
- data/lib/smoke/version.rb +0 -9
- data/log/debug.log +0 -0
- data/log/development.log +0 -0
- data/log/production.log +0 -0
- data/log/server.log +0 -0
- data/log/test.log +0 -0
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -74
- data/setup.rb +0 -1585
- data/tasks/deployment.rake +0 -35
- data/tasks/environment.rake +0 -7
- data/tasks/website.rake +0 -17
- data/test/test_helper.rb +0 -2
- data/test/test_server.rb +0 -11
- data/website/index.html +0 -87
- data/website/index.txt +0 -29
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.rhtml +0 -48
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Ben Schwarz
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# smoke
|
|
2
|
+
|
|
3
|
+
smoke is a Ruby based DSL that allows you to query web services such as YQL, RSS / Atom and JSON or XML in an elegant manner.
|
|
4
|
+
|
|
5
|
+
These "services" can then be re-represented, sorted and filtered. Data can be collected from multiple sources, sorted via a common property, chopped up and refried.
|
|
6
|
+
|
|
7
|
+
Then you can output as a plain ruby object (or JSON)
|
|
8
|
+
|
|
9
|
+
## Examples of use
|
|
10
|
+
|
|
11
|
+
* The `examples` directory has something to get you running straight away
|
|
12
|
+
* I powered [my entire site](http://www.germanforblack.com) [using smoke](http://github.com/benschwarz/benschwarz-site/blob/44de70463c744d821d3ffd2cf940e6d3e415fbdd/lib/stream.rb), until further documentation exists, this is probably a good place to start.
|
|
13
|
+
* Read further details in the [rdoc documentation](http://rdoc.info/projects/benschwarz/smoke) or [the wiki](http://wiki.github.com/benschwarz/smoke)
|
|
14
|
+
|
|
15
|
+
## Media
|
|
16
|
+
|
|
17
|
+
* [Presentation from Melbourne #roro](http://www.slideshare.net/benschwarz/smoke-1371124)
|
|
18
|
+
* Early [screencast](http://vimeo.com/4272804) to get developer / peer feedback
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## The concept
|
|
22
|
+
|
|
23
|
+
The concept comes from using [Yahoo Pipes](http://pipes.yahoo.com) to make web based mash ups: Get a list of tv shows for my torrent client, compile a recipe book or make tools to give me a list of albums that artists in my music library are about to be released.
|
|
24
|
+
|
|
25
|
+
## How or what to contribute
|
|
26
|
+
|
|
27
|
+
* Test everything you do
|
|
28
|
+
* Add a way to output (XML, anyone?)
|
|
29
|
+
* Examples of queries you'd like to be able to do (email / github message them to me)
|
|
30
|
+
|
|
31
|
+
## API Examples
|
|
32
|
+
### YQL
|
|
33
|
+
# This will use yahoo search to get an array of search results about Ruby
|
|
34
|
+
Smoke.yql(:ruby) do
|
|
35
|
+
select :all
|
|
36
|
+
from "search.web"
|
|
37
|
+
where :query, "ruby"
|
|
38
|
+
|
|
39
|
+
discard :title, /tuesday/i
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Smoke.yql(:python) do
|
|
43
|
+
select :all
|
|
44
|
+
from "search.web"
|
|
45
|
+
where :query, "python"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
### Join sources and use them together
|
|
49
|
+
Smoke.join(:ruby, :python)
|
|
50
|
+
|
|
51
|
+
or even
|
|
52
|
+
|
|
53
|
+
Smoke.join(:python, :ruby) do
|
|
54
|
+
emit do
|
|
55
|
+
sort :title
|
|
56
|
+
rename :shit_name => :title
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
### Define a source allowing for variables to be injected later
|
|
61
|
+
|
|
62
|
+
Source definition:
|
|
63
|
+
|
|
64
|
+
Smoke.feed :delicious do
|
|
65
|
+
prepare do
|
|
66
|
+
url "http://feeds.delicious.com/v2/rss/#{username}?count=15"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Execution:
|
|
71
|
+
|
|
72
|
+
Smoke[:delicious].username("bschwarz").output
|
|
73
|
+
|
|
74
|
+
### CI
|
|
75
|
+
|
|
76
|
+
Integrity [is running for smoke](http://integrity.ffolio.net/smoke)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
### TODO (working on, just mental notes)
|
|
80
|
+
#### Later / maybe
|
|
81
|
+
* YQL w/oAuth
|
|
82
|
+
* YQL Subqueries?
|
|
83
|
+
* Implement basic auth for sources
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
#### For wiki pages (docs, later)
|
|
87
|
+
* Document all sources with their irrespective differential methods
|
|
88
|
+
* How to use `path`
|
|
89
|
+
* YQL Definitions
|
|
90
|
+
* Tranformations
|
|
91
|
+
* Insert
|
|
92
|
+
* Joining
|
|
93
|
+
* Variable injection
|
|
94
|
+
* Sort, Reverse
|
|
95
|
+
* Keep, Discard
|
|
96
|
+
* Truncate
|
|
97
|
+
* Manually setting the content type for a url
|
|
98
|
+
|
|
99
|
+
### Copyright
|
|
100
|
+
|
|
101
|
+
Copyright (c) 2009 Ben Schwarz. See LICENSE for details.
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,53 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'spec'
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require 'jeweler'
|
|
7
|
+
Jeweler::Tasks.new do |gem|
|
|
8
|
+
gem.name = "smoke"
|
|
9
|
+
gem.summary = %Q{smoke is a Ruby based DSL that allows you to query web services such as YQL, RSS / Atom and JSON or XML in an elegant manner.}
|
|
10
|
+
gem.email = "ben.schwarz@gmail.com"
|
|
11
|
+
gem.homepage = "http://github.com/benschwarz/smoke"
|
|
12
|
+
gem.authors = ["Ben Schwarz"]
|
|
13
|
+
gem.files = FileList['lib/**/*.rb', 'rdoc/**/*', '[A-Z]*', 'spec/**/*', 'vendor/**/*'].to_a
|
|
14
|
+
gem.add_dependency("simple-rss", "1.2")
|
|
15
|
+
gem.add_dependency("json", "1.1.3")
|
|
16
|
+
gem.add_dependency("fakeweb", "1.2.5")
|
|
17
|
+
gem.add_dependency("crack", "0.1.1")
|
|
18
|
+
gem.add_dependency("moneta", "0.6.0")
|
|
19
|
+
gem.add_dependency("rest-client", "1.0.3")
|
|
20
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
21
|
+
end
|
|
22
|
+
rescue LoadError
|
|
23
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
gem 'mislav-hanna', '>= 0.2.7'
|
|
28
|
+
require 'hanna/rdoctask'
|
|
29
|
+
rescue LoadError
|
|
30
|
+
require 'rake/rdoctask'
|
|
31
|
+
end
|
|
32
|
+
Rake::RDocTask.new do |rdoc|
|
|
33
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
34
|
+
rdoc.title = 'smoke'
|
|
35
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
36
|
+
rdoc.rdoc_files.include('README*')
|
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
require 'spec/rake/spectask'
|
|
41
|
+
Spec::Rake::SpecTask.new(:test) do |t|
|
|
42
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
47
|
+
spec.libs << 'lib' << 'spec'
|
|
48
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
49
|
+
spec.rcov = true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
task :spec => :test
|
|
53
|
+
task :default => :test
|
data/VERSION.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'lib/smoke'
|
|
2
|
+
Smoke.configure do |c|
|
|
3
|
+
c[:enable_logging] = true
|
|
4
|
+
c[:cache][:enabled] = true
|
|
5
|
+
c[:cache][:store] = :memory
|
|
6
|
+
end
|
|
7
|
+
Smoke.yql(:python) do
|
|
8
|
+
select :all
|
|
9
|
+
from "search.web"
|
|
10
|
+
where :query, "python"
|
|
11
|
+
|
|
12
|
+
path :query, :results, :result
|
|
13
|
+
end
|
|
14
|
+
Smoke[:python].output
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Smoke.yql(:ruby) do
|
|
18
|
+
select :all
|
|
19
|
+
from "search.web"
|
|
20
|
+
where :query, "ruby"
|
|
21
|
+
|
|
22
|
+
path :query, :results, :result
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Smoke.join(:ruby, :python)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Hash # :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
|
+
|
|
13
|
+
def rename(candidates)
|
|
14
|
+
candidates.each do |old_key, new_key|
|
|
15
|
+
self[new_key] = self.delete(old_key) if self.has_key?(old_key)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return self
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/smoke.rb
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
|
-
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'digest/md5'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
require 'simple-rss'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'crack'
|
|
7
|
+
require 'moneta'
|
|
8
|
+
require 'restclient'
|
|
9
|
+
|
|
10
|
+
module Smoke
|
|
11
|
+
class << self
|
|
12
|
+
@@active_sources = {}
|
|
13
|
+
@@config = {
|
|
14
|
+
:enable_logging => true,
|
|
15
|
+
:user_agent => "Ruby/#{RUBY_VERSION}/Smoke",
|
|
16
|
+
:cache => {
|
|
17
|
+
:enabled => false,
|
|
18
|
+
:store => :memory,
|
|
19
|
+
:options => {},
|
|
20
|
+
:expiry => 1800
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Access registered smoke source instances
|
|
25
|
+
#
|
|
26
|
+
# Define your source:
|
|
27
|
+
# Smoke.yql(:ruby) do ....
|
|
28
|
+
# Then access it:
|
|
29
|
+
# Smoke[:ruby]
|
|
30
|
+
# => #<Smoke::Source::YQL::0x18428d4...
|
|
31
|
+
def [](source)
|
|
32
|
+
active_sources[source]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Activates new instances of sources
|
|
36
|
+
# Source instances are stored within the
|
|
37
|
+
# @@active_sources class variable for later use
|
|
38
|
+
def activate(name, source)
|
|
39
|
+
if active_sources.has_key?(name)
|
|
40
|
+
Smoke.log.warn "Smoke source activation: Source with idential name already initialized"
|
|
41
|
+
end
|
|
42
|
+
active_sources.update({ name => source })
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns all activated smoke sources
|
|
46
|
+
def active_sources
|
|
47
|
+
@@active_sources
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Rename a source
|
|
51
|
+
def rename(candidates)
|
|
52
|
+
candidates.each do |o, n|
|
|
53
|
+
active_sources[o].name = n.to_s
|
|
54
|
+
active_sources.rename(o => n)
|
|
55
|
+
return active_sources[n]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Log for info, debug, error and warn with:
|
|
60
|
+
#
|
|
61
|
+
# Smoke.log.info "message"
|
|
62
|
+
# Smoke.log.debug "message"
|
|
63
|
+
# Smoke.log.error "message"
|
|
64
|
+
# Smoke.log.warn "message"
|
|
65
|
+
def log
|
|
66
|
+
@@log ||= Logger.new(config[:enable_logging] ? $stdout : "/dev/null")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Set any configurable options
|
|
70
|
+
#
|
|
71
|
+
# Smoke.configure do |c|
|
|
72
|
+
# c[:user_agent] = "Some other site"
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
def configure(&block)
|
|
76
|
+
yield @@config
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Access configuration options
|
|
80
|
+
#
|
|
81
|
+
# Smoke.config[:option_name]
|
|
82
|
+
# => true
|
|
83
|
+
def config
|
|
84
|
+
@@config
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def yql(name, &block); Smoke::Source::YQL.new(name, &block); end
|
|
88
|
+
def data(name, &block); Smoke::Source::Data.new(name, &block); end
|
|
89
|
+
def feed(name, &block); Smoke::Source::Feed.new(name, &block); end
|
|
90
|
+
|
|
91
|
+
# Join multiple sources together into a single feed
|
|
92
|
+
# Usage:
|
|
93
|
+
# Smoke.join(:delicious, :twitter, :flickr) do
|
|
94
|
+
# name :stream
|
|
95
|
+
# path :photos, :photo
|
|
96
|
+
# end
|
|
97
|
+
def join(*names, &block); Smoke::Source::Join.new(names, &block); end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
%w(core_ext/hash core_ext/string smoke/cache smoke/request smoke/origin).each {|r| require File.join(File.dirname(__FILE__), r)}
|
|
102
|
+
|
|
103
|
+
class Object # :nodoc:
|
|
104
|
+
include Smoke
|
|
5
105
|
end
|
data/lib/smoke/cache.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Smoke
|
|
2
|
+
class Cache
|
|
3
|
+
class << self
|
|
4
|
+
def fetch(uri, options)
|
|
5
|
+
output = (enabled?) ? read(uri) : query(uri, options)
|
|
6
|
+
|
|
7
|
+
unless output.keys.any?
|
|
8
|
+
Smoke.log.info "Cache miss"
|
|
9
|
+
output = query(uri, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
output
|
|
13
|
+
rescue
|
|
14
|
+
query(uri, options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def enabled?
|
|
18
|
+
Smoke.config[:cache][:enabled]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
def cache
|
|
23
|
+
Moneta.autoload(klass_name.to_sym, file_name)
|
|
24
|
+
@@cache ||= Moneta.const_get(klass_name).new(Smoke.config[:cache][:options])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def file_name
|
|
28
|
+
"moneta/#{Smoke.config[:cache][:store].to_s}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def klass_name
|
|
32
|
+
Smoke.config[:cache][:store].to_s.camel_case
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def query(uri, options)
|
|
36
|
+
request = RestClient.get(uri, options)
|
|
37
|
+
write(uri, request, request.headers[:content_type]) if enabled?
|
|
38
|
+
{:body => request, :content_type => request.headers[:content_type]}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def read(uri)
|
|
42
|
+
key = generate_key(uri)
|
|
43
|
+
return cache[key]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def write(uri, body, content_type)
|
|
47
|
+
store = {:body => body, :content_type => content_type}
|
|
48
|
+
self.cache.store(generate_key(uri), store, :expire_in => Smoke.config[:cache][:expiry])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def generate_key(key)
|
|
52
|
+
Digest::MD5.hexdigest(key.to_s)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/smoke/origin.rb
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
module Smoke
|
|
2
|
+
class Origin
|
|
3
|
+
attr_reader :items
|
|
4
|
+
attr_accessor :name
|
|
5
|
+
|
|
6
|
+
def initialize(name, &block)
|
|
7
|
+
raise StandardError, "Sources must have a name" unless name
|
|
8
|
+
@name = name
|
|
9
|
+
@items, @prepare, @transformation = [], [], []
|
|
10
|
+
|
|
11
|
+
activate!
|
|
12
|
+
instance_eval(&block) if block_given?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Output your items in a range of formats (:ruby, :json and :yaml currently)
|
|
16
|
+
# Ruby is the default format and will automagically yielded from your source
|
|
17
|
+
#
|
|
18
|
+
# Usage
|
|
19
|
+
#
|
|
20
|
+
# output(:json)
|
|
21
|
+
# => "[{title: \"Ray\"}, {title: \"Peace\"}]"
|
|
22
|
+
def output(type = :ruby)
|
|
23
|
+
prepare!
|
|
24
|
+
dispatch if respond_to? :dispatch
|
|
25
|
+
|
|
26
|
+
case type
|
|
27
|
+
when :json
|
|
28
|
+
return ::JSON.generate(@items)
|
|
29
|
+
when :yaml
|
|
30
|
+
return YAML.dump(@items)
|
|
31
|
+
else
|
|
32
|
+
return @items
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def items=(response) # :nodoc:
|
|
37
|
+
@items = [(@path.nil?) ? response : drill(response, *@path)]
|
|
38
|
+
symbolize_keys!
|
|
39
|
+
transform!
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Path allows you to traverse the tree of a the items returned to
|
|
43
|
+
# only give you access to what you're interested in.
|
|
44
|
+
#
|
|
45
|
+
# Usage:
|
|
46
|
+
# path :down, :to, :the, :data
|
|
47
|
+
#
|
|
48
|
+
# Will traverse through a tree as follows:
|
|
49
|
+
#
|
|
50
|
+
# {
|
|
51
|
+
# :down => {
|
|
52
|
+
# :to => {
|
|
53
|
+
# :the => {
|
|
54
|
+
# :data => []
|
|
55
|
+
# }
|
|
56
|
+
# }
|
|
57
|
+
# }
|
|
58
|
+
# }
|
|
59
|
+
#
|
|
60
|
+
# You will need to help smoke find an array of
|
|
61
|
+
# items that you're interested in.
|
|
62
|
+
def path(*path)
|
|
63
|
+
@path = path
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Transform each item
|
|
67
|
+
#
|
|
68
|
+
# Usage:
|
|
69
|
+
# emit do
|
|
70
|
+
# rename(:href => :link)
|
|
71
|
+
# end
|
|
72
|
+
def emit(&block)
|
|
73
|
+
raise ArgumentError, "requires a block" unless block_given?
|
|
74
|
+
@transformation << block
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Transform must be used inside an `emit` block.
|
|
78
|
+
# It can be used to alter named keys within the item set
|
|
79
|
+
#
|
|
80
|
+
# Usage:
|
|
81
|
+
# emit do
|
|
82
|
+
# transform :name, :description 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 and :description keys in the result set.
|
|
89
|
+
def transform(*keys)
|
|
90
|
+
raise ArgumentError, "requires a block" unless block_given?
|
|
91
|
+
keys.each do |key|
|
|
92
|
+
items.each do |item|
|
|
93
|
+
item[key] = yield(item[key]) || item[key]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Insert must be used inside an `emit` block.
|
|
99
|
+
# It can be used to insert named keys to all items within the result set
|
|
100
|
+
#
|
|
101
|
+
# Usage:
|
|
102
|
+
#
|
|
103
|
+
# emit do
|
|
104
|
+
# insert :source, "twitter"
|
|
105
|
+
# end
|
|
106
|
+
#
|
|
107
|
+
# Once output is called, all items will contain a key of :source with
|
|
108
|
+
# a value of "twitter"
|
|
109
|
+
def insert(key, value)
|
|
110
|
+
@items.each do |item|
|
|
111
|
+
item[key] = value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Prepare is used when you'd like to provision for
|
|
116
|
+
# arguments / variables to be set after the source definition.
|
|
117
|
+
# Eg, create a source definition for twitter, omitting the "username".
|
|
118
|
+
# Set the username using chaining later.
|
|
119
|
+
#
|
|
120
|
+
# Usage:
|
|
121
|
+
# # Definition
|
|
122
|
+
# Smoke.feed :twitter do
|
|
123
|
+
# prepare do
|
|
124
|
+
# url "http://twitter.com/#{username}/rss"
|
|
125
|
+
# end
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# # End use
|
|
129
|
+
# Smoke[:twitter].username(:benschwarz).output
|
|
130
|
+
def prepare(&block)
|
|
131
|
+
raise ArgumentError, "requires a block" unless block_given?
|
|
132
|
+
@prepare << block
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def method_missing(symbol, *args, &block)
|
|
136
|
+
ivar = "@#{symbol}"
|
|
137
|
+
|
|
138
|
+
if args.empty?
|
|
139
|
+
return instance_variable_get(ivar) || super
|
|
140
|
+
else
|
|
141
|
+
instance_variable_set(ivar, args.pop)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
return self
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Re-sort items by a particular key
|
|
148
|
+
# Sort must be used inside an `emit` block.
|
|
149
|
+
def sort(key)
|
|
150
|
+
@items = @items.sort_by{|i| i[key] }
|
|
151
|
+
rescue NoMethodError => e
|
|
152
|
+
Smoke.log.info "You're trying to sort by \"#{key}\" but it does not exist in your item set"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Reverse the order of the items
|
|
156
|
+
#
|
|
157
|
+
# Usage
|
|
158
|
+
# Smoke[:ruby].output
|
|
159
|
+
# Returns [{:header => "Platypus"}, {:header => "Kangaroo"}]
|
|
160
|
+
# Smoke.yql(:ruby) do
|
|
161
|
+
# ... Define your source
|
|
162
|
+
# emit do
|
|
163
|
+
# reverse
|
|
164
|
+
# end
|
|
165
|
+
# end
|
|
166
|
+
# Returns [{:header => "Kangaroo"}, {:header => "Platypus"}]
|
|
167
|
+
# Reverse must be used inside an `emit` block.
|
|
168
|
+
def reverse
|
|
169
|
+
@items.reverse!
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Keep items that match the regex
|
|
173
|
+
#
|
|
174
|
+
# Usage (block, during initialization):
|
|
175
|
+
# Smoke.yql(:ruby) do
|
|
176
|
+
# ...
|
|
177
|
+
# emit do
|
|
178
|
+
# keep(:title, /tuesday/i)
|
|
179
|
+
# end
|
|
180
|
+
# end
|
|
181
|
+
# Keep must be used inside an `emit` block.
|
|
182
|
+
def keep(key, matcher)
|
|
183
|
+
@items.reject! {|i| (i[key] =~ matcher) ? false : true }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Discard items that do not match the regex
|
|
187
|
+
#
|
|
188
|
+
# Usage (block, during initialization):
|
|
189
|
+
# Smoke.yql(:ruby) do
|
|
190
|
+
# ...
|
|
191
|
+
# emit do
|
|
192
|
+
# discard(:title, /tuesday/i)
|
|
193
|
+
# end
|
|
194
|
+
# end
|
|
195
|
+
# Discard must be used inside an `emit` block.
|
|
196
|
+
def discard(key, matcher)
|
|
197
|
+
@items.reject! {|i| (i[key] =~ matcher) ? true : false }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Rename one or many keys at a time
|
|
201
|
+
#
|
|
202
|
+
# Usage
|
|
203
|
+
# # Renames all items with a key of href to link
|
|
204
|
+
# rename(:href => :link)
|
|
205
|
+
# or
|
|
206
|
+
# rename(:href => :link, :description => :excerpt)
|
|
207
|
+
# Rename must be used inside an `emit` block.
|
|
208
|
+
def rename(*args)
|
|
209
|
+
@items.each {|item| item.rename(*args) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Truncate your result set to this many objects
|
|
213
|
+
#
|
|
214
|
+
# Usage
|
|
215
|
+
# Smoke.yql(:ruby) do
|
|
216
|
+
# ...
|
|
217
|
+
# truncate(3)
|
|
218
|
+
# end
|
|
219
|
+
# Smoke[:ruby].output
|
|
220
|
+
# => [{title => "Canon"}, {:title => "Nikon"}, {:title => "Pentax"}]
|
|
221
|
+
# Truncate must be used inside an `emit` block.
|
|
222
|
+
def truncate(length)
|
|
223
|
+
@items = @items[0..(length - 1)]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
private
|
|
227
|
+
def prepare!
|
|
228
|
+
@prepare.each{|p| p.call } unless @prepare.nil?
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def transform!
|
|
232
|
+
@transformation.each{|t| t.call } unless @transformation.nil?
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def symbolize_keys!
|
|
236
|
+
@items = items.flatten.map{|i| i.symbolize_keys! } if items.respond_to? :flatten
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def drill(*path)
|
|
240
|
+
path.inject(nil) do |obj, pointer|
|
|
241
|
+
obj = obj.nil? ? pointer : obj[pointer]
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def activate!
|
|
246
|
+
Smoke.activate(@name, self)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
Dir["#{File.dirname(__FILE__)}/source/*.rb"].each &method(:require)
|