grifter 0.0.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/Gemfile +12 -0
- data/LICENSE.txt +20 -0
- data/Rakefile +41 -0
- data/Readme.md +119 -0
- data/VERSION +1 -0
- data/bin/grift +62 -0
- data/example/grifter.yml +7 -0
- data/example/owm_grifts/weather_grifts.rb +3 -0
- data/example/temperatures.rb +12 -0
- data/grifter.gemspec +80 -0
- data/lib/grifter.rb +77 -0
- data/lib/grifter/blankslate.rb +126 -0
- data/lib/grifter/configuration.rb +89 -0
- data/lib/grifter/helpers.rb +24 -0
- data/lib/grifter/http_service.rb +142 -0
- data/lib/grifter/json_helpers.rb +38 -0
- data/lib/grifter/log.rb +35 -0
- data/spec/configuration_spec.rb +127 -0
- data/spec/grifter_spec.rb +52 -0
- data/spec/http_service_spec.rb +81 -0
- data/spec/json_helpers_spec.rb +53 -0
- data/spec/resources/example_config.yml +19 -0
- data/spec/resources/grifter.yml +11 -0
- data/spec/resources/syntax_error_grifts/eval_error_grifts.rb +3 -0
- data/spec/resources/twitter_grifts/oauth_grifts.rb +23 -0
- data/spec/resources/twitter_grifts/timeline_grifts.rb +9 -0
- data/spec/rspec_helper_spec.rb +30 -0
- data/spec/support/require_all_lib_files.rb +0 -0
- data/spec/support/spec_helper.rb +19 -0
- metadata +159 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Robert Schultheis
|
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/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "grifter"
|
18
|
+
gem.homepage = "http://github.com/knewton/grifter"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Make calls to HTTP JSON APIs with ease and confidence}
|
21
|
+
gem.description = %Q{convention based approach to interfacing with an HTTP JSON API.}
|
22
|
+
gem.email = "rob@knewton.com"
|
23
|
+
gem.authors = ["Robert Schultheis"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core/rake_task'
|
29
|
+
RSpec::Core::RakeTask.new(:spec)
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
require 'rdoc/task'
|
34
|
+
Rake::RDocTask.new do |rdoc|
|
35
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
36
|
+
|
37
|
+
rdoc.rdoc_dir = 'rdoc'
|
38
|
+
rdoc.title = "grifter #{version}"
|
39
|
+
rdoc.rdoc_files.include('README*')
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
41
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
Grifter
|
2
|
+
=======
|
3
|
+
Grifter makes it smooth to work with JSON HTTP APIs with confidence
|
4
|
+
|
5
|
+
Features
|
6
|
+
--------
|
7
|
+
- Work with multiple APIs
|
8
|
+
- Work with multiple deployment environments (Staging, Production, etc.)
|
9
|
+
- Script calls to API(s)
|
10
|
+
- Command line calls to API(s)
|
11
|
+
- Craft clean API tests using the included RSPec helper
|
12
|
+
- Unified approach to handling request errors
|
13
|
+
- Convention over configuration approach to defining the API interface
|
14
|
+
|
15
|
+
Getting Started
|
16
|
+
---------------
|
17
|
+
Lets demo how this works using a simple example of an API that requires no
|
18
|
+
authentication.
|
19
|
+
|
20
|
+
The OpenWeatherMap is a good candidate for a simple API
|
21
|
+
which is accessible without needing any authentication key:
|
22
|
+
[http://api.openweathermap.org/]
|
23
|
+
|
24
|
+
|
25
|
+
### make a project directory
|
26
|
+
|
27
|
+
mkdir weather
|
28
|
+
cd weather
|
29
|
+
|
30
|
+
### setup grifter.yml
|
31
|
+
|
32
|
+
services:
|
33
|
+
owm:
|
34
|
+
hostname: api.openweathermap.org
|
35
|
+
|
36
|
+
### make the grifts directory, and a grift file
|
37
|
+
|
38
|
+
mkdir owm_grifts
|
39
|
+
touch owm_grifts/weather_grifts.rb
|
40
|
+
|
41
|
+
### add method for checking weather to owm/weather.rb
|
42
|
+
def weather_for city
|
43
|
+
owm.get "/data/2.5/weather?q=#{URI.encode(city)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
### Call it from the cmd line:
|
47
|
+
$ grift weather_for 'Pleasantville, NY'
|
48
|
+
|
49
|
+
And that returns something like this:
|
50
|
+
{"coord"=>{"lon"=>-73.79169, "lat"=>41.13436}, "sys"=>{"country"=>"United States of America", "sunrise"=>1366883974, "sunset"=>1366933583}, "weather"=>[{"id"=>501, "main"=>"Rain", "description"=>"moderate rain", "icon"=>"10d"}], "base"=>"global stations", "main"=>{"temp"=>290.46, "humidity"=>26, "pressure"=>1020, "temp_min"=>289.15, "temp_max"=>292.59}, "wind"=>{"speed"=>2.06, "gust"=>4.11, "deg"=>265}, "rain"=>{"1h"=>2.32}, "clouds"=>{"all"=>0}, "dt"=>1366926419, "id"=>5131757, "name"=>"Pleasantville", "cod"=>200}
|
51
|
+
|
52
|
+
Use the -v command line option and see the full request/response logged to StdOut
|
53
|
+
|
54
|
+
### Script it
|
55
|
+
Make a file called temperatures.rb with this in it:
|
56
|
+
|
57
|
+
[
|
58
|
+
'New York, NY',
|
59
|
+
'Toronto, Canada',
|
60
|
+
'Paris, France',
|
61
|
+
'Tokyo, Japan',
|
62
|
+
'Sydney, Australia',
|
63
|
+
].each do |city|
|
64
|
+
weather = weather_for city
|
65
|
+
kelvin = weather['main']['temp']
|
66
|
+
celcius = (kelvin - 273.15).round
|
67
|
+
puts "#{city}: #{celcius} celcius"
|
68
|
+
end
|
69
|
+
|
70
|
+
And then run the grift script like so:
|
71
|
+
|
72
|
+
grift -f temperatures.rb
|
73
|
+
|
74
|
+
And get this nice output:
|
75
|
+
|
76
|
+
I: [04/25/13 17:59:22][grifter] - Running data script 'temperatures.rb'
|
77
|
+
New York, NY: 18 celcius
|
78
|
+
Toronto, Canada: 7 celcius
|
79
|
+
Paris, France: 18 celcius
|
80
|
+
Tokyo, Japan: 16 celcius
|
81
|
+
Sydney, Australia: 14 celcius
|
82
|
+
|
83
|
+
### Test it
|
84
|
+
Using the included helper module, testing an api becomes easy. Lets setup a simple RSpec example. Step one is create the spec folder:
|
85
|
+
|
86
|
+
mkdir spec
|
87
|
+
|
88
|
+
Setup spec/spec_helper.rb with contents like:
|
89
|
+
|
90
|
+
require 'grifter/helpers'
|
91
|
+
|
92
|
+
RSpec.configure do |config|
|
93
|
+
config.include Grifter::Helpers
|
94
|
+
end
|
95
|
+
|
96
|
+
Setup spec/weather_spec.rb with contents like:
|
97
|
+
|
98
|
+
require 'spec_helper'
|
99
|
+
describe "getting weather reports" do
|
100
|
+
it "should know the weather for New York City" do
|
101
|
+
response = weather_for 'New York, NY'
|
102
|
+
response['main'].keys.should =~ ['temp', 'temp_min', 'temp_max', 'humidity', 'pressure']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Run it:
|
107
|
+
|
108
|
+
gem install rspec
|
109
|
+
rspec
|
110
|
+
|
111
|
+
And get back:
|
112
|
+
|
113
|
+
1 example, 0 failures
|
114
|
+
|
115
|
+
|
116
|
+
Copyright
|
117
|
+
---------
|
118
|
+
Copyright (c) 2013 Knewton. See LICENSE.txt for
|
119
|
+
further details.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
data/bin/grift
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require_relative '../lib/grifter'
|
5
|
+
|
6
|
+
Log = Grifter::Log
|
7
|
+
|
8
|
+
Log.level = Logger::INFO
|
9
|
+
|
10
|
+
#Deal with the cmd line
|
11
|
+
def parse_cmd_line
|
12
|
+
options = {
|
13
|
+
files: [],
|
14
|
+
config_file: 'grifter.yml',
|
15
|
+
authenticate: true,
|
16
|
+
environment: nil,
|
17
|
+
}
|
18
|
+
|
19
|
+
optparse = OptionParser.new do |opts|
|
20
|
+
opts.banner = %Q|
|
21
|
+
Grifter HTTP JSON APIs Client
|
22
|
+
example, run a data script: bin/grifter -f script.rb
|
23
|
+
example, call a method: bin/grifter <grift method name>
|
24
|
+
|
25
|
+
|
|
26
|
+
|
27
|
+
opts.on("-f", "--script=FILENAME",
|
28
|
+
"specify a script file to run") { |file| options[:files] << file }
|
29
|
+
|
30
|
+
opts.on("-v", "--verbose",
|
31
|
+
"enable debug logging") { Log.level = Logger::DEBUG }
|
32
|
+
|
33
|
+
opts.on("-c", "--config=FILENAME",
|
34
|
+
"config filename") { |fn| options[:config_file] = fn }
|
35
|
+
|
36
|
+
opts.on('-e', '--environment=ENVIRONMENT',
|
37
|
+
"environment name") { |e| options[:environment] = e.to_sym }
|
38
|
+
|
39
|
+
opts.on("-d", "--no_authenticate",
|
40
|
+
"Do not authenticate") { options[:authenticate] = false }
|
41
|
+
|
42
|
+
end
|
43
|
+
optparse.parse!
|
44
|
+
options
|
45
|
+
end
|
46
|
+
options = parse_cmd_line
|
47
|
+
|
48
|
+
grifter = Grifter.new options
|
49
|
+
|
50
|
+
if not(ARGV.empty?)
|
51
|
+
method = ARGV.shift
|
52
|
+
response = grifter.send(method.to_sym, *ARGV)
|
53
|
+
puts response.inspect
|
54
|
+
|
55
|
+
elsif not(options[:files].empty?)
|
56
|
+
options[:files].each do |script_file|
|
57
|
+
grifter.run_script_file(script_file)
|
58
|
+
end
|
59
|
+
|
60
|
+
else
|
61
|
+
Kernel.abort "Nothing to do? use -f or give a method name on the cmd line"
|
62
|
+
end
|
data/example/grifter.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
[
|
2
|
+
'New York, NY',
|
3
|
+
'Toronto, Canada',
|
4
|
+
'Paris, France',
|
5
|
+
'Tokyo, Japan',
|
6
|
+
'Sydney, Australia',
|
7
|
+
].each do |city|
|
8
|
+
weather = weather_for city
|
9
|
+
kelvin = weather['main']['temp']
|
10
|
+
celcius = (kelvin - 273.15).round
|
11
|
+
puts "#{city}: #{celcius} celcius"
|
12
|
+
end
|
data/grifter.gemspec
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "grifter"
|
8
|
+
s.version = "0.0.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Robert Schultheis"]
|
12
|
+
s.date = "2013-05-09"
|
13
|
+
s.description = "convention based approach to interfacing with an HTTP JSON API."
|
14
|
+
s.email = "rob@knewton.com"
|
15
|
+
s.executables = ["grift"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Gemfile",
|
21
|
+
"LICENSE.txt",
|
22
|
+
"Rakefile",
|
23
|
+
"Readme.md",
|
24
|
+
"VERSION",
|
25
|
+
"bin/grift",
|
26
|
+
"example/grifter.yml",
|
27
|
+
"example/owm_grifts/weather_grifts.rb",
|
28
|
+
"example/temperatures.rb",
|
29
|
+
"grifter.gemspec",
|
30
|
+
"lib/grifter.rb",
|
31
|
+
"lib/grifter/blankslate.rb",
|
32
|
+
"lib/grifter/configuration.rb",
|
33
|
+
"lib/grifter/helpers.rb",
|
34
|
+
"lib/grifter/http_service.rb",
|
35
|
+
"lib/grifter/json_helpers.rb",
|
36
|
+
"lib/grifter/log.rb",
|
37
|
+
"spec/configuration_spec.rb",
|
38
|
+
"spec/grifter_spec.rb",
|
39
|
+
"spec/http_service_spec.rb",
|
40
|
+
"spec/json_helpers_spec.rb",
|
41
|
+
"spec/resources/example_config.yml",
|
42
|
+
"spec/resources/grifter.yml",
|
43
|
+
"spec/resources/syntax_error_grifts/eval_error_grifts.rb",
|
44
|
+
"spec/resources/twitter_grifts/oauth_grifts.rb",
|
45
|
+
"spec/resources/twitter_grifts/timeline_grifts.rb",
|
46
|
+
"spec/rspec_helper_spec.rb",
|
47
|
+
"spec/support/require_all_lib_files.rb",
|
48
|
+
"spec/support/spec_helper.rb"
|
49
|
+
]
|
50
|
+
s.homepage = "http://github.com/knewton/grifter"
|
51
|
+
s.licenses = ["MIT"]
|
52
|
+
s.require_paths = ["lib"]
|
53
|
+
s.rubygems_version = "1.8.24"
|
54
|
+
s.summary = "Make calls to HTTP JSON APIs with ease and confidence"
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
s.specification_version = 3
|
58
|
+
|
59
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
60
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<pry>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<json>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
68
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
69
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
70
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
71
|
+
end
|
72
|
+
else
|
73
|
+
s.add_dependency(%q<json>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
75
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
76
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
77
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
data/lib/grifter.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'grifter/http_service'
|
2
|
+
require_relative 'grifter/configuration'
|
3
|
+
require_relative 'grifter/log'
|
4
|
+
require_relative 'grifter/blankslate'
|
5
|
+
|
6
|
+
class Grifter
|
7
|
+
include Grifter::Configuration
|
8
|
+
|
9
|
+
DefaultConfigOptions = {
|
10
|
+
#TODO: service_config: nil,
|
11
|
+
grift_globs: ['*_grifts/**/*_grifts.rb'],
|
12
|
+
authenticate: false,
|
13
|
+
load_from_config_file: true,
|
14
|
+
services: {},
|
15
|
+
}
|
16
|
+
def initialize options={}
|
17
|
+
options = DefaultConfigOptions.merge(options)
|
18
|
+
@config = if options[:load_from_config_file]
|
19
|
+
options.merge load_config_file(options)
|
20
|
+
else
|
21
|
+
options
|
22
|
+
end
|
23
|
+
|
24
|
+
#setup the services
|
25
|
+
@services = []
|
26
|
+
@config[:services].each_pair do |service_name, service_config|
|
27
|
+
service = HTTPService.new(service_config)
|
28
|
+
define_singleton_method service_name.intern do
|
29
|
+
service
|
30
|
+
end
|
31
|
+
@services << service
|
32
|
+
end
|
33
|
+
|
34
|
+
#setup the grifter methods if any
|
35
|
+
if @config[:grift_globs]
|
36
|
+
@config[:grift_globs].each do |glob|
|
37
|
+
Dir[glob].each do |grifter_file|
|
38
|
+
load_grifter_file grifter_file
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if @config[:authenticate]
|
44
|
+
self.grifter_authenticate_do
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :services
|
49
|
+
|
50
|
+
def load_grifter_file filename
|
51
|
+
Log.debug "Loading extension file '#{filename}'"
|
52
|
+
anon_mod = Module.new
|
53
|
+
#by evaling in a anonymous module, we protect this class's namespace
|
54
|
+
load_dir = File.dirname(filename)
|
55
|
+
$: << load_dir
|
56
|
+
anon_mod.class_eval(IO.read(filename), filename, 1)
|
57
|
+
$:.pop
|
58
|
+
self.extend anon_mod
|
59
|
+
end
|
60
|
+
|
61
|
+
def run_script_file filename
|
62
|
+
Log.info "Running data script '#{filename}'"
|
63
|
+
raise "No such file '#{filename}'" unless File.exist? filename
|
64
|
+
#by running in a anonymous class, we protect this class's namespace
|
65
|
+
anon_class = BlankSlate.new(self)
|
66
|
+
anon_class.instance_eval(IO.read(filename), filename, 1)
|
67
|
+
end
|
68
|
+
|
69
|
+
#calls all methods that end with grifter_authenticate
|
70
|
+
def grifter_authenticate_do
|
71
|
+
auth_methods = self.singleton_methods.select { |m| m =~ /grifter_authenticate$/ }
|
72
|
+
auth_methods.each do |m|
|
73
|
+
Log.debug "Executing a grifter_authentication on method: #{m}"
|
74
|
+
self.send(m)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|