hatt 0.0.1
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.
- checksums.yaml +7 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +138 -0
- data/LICENSE.txt +18 -0
- data/README.rdoc +120 -0
- data/bin/hatt +111 -0
- data/hatt.gemspec +94 -0
- data/lib/hatt/api_clients.rb +40 -0
- data/lib/hatt/base.rb +17 -0
- data/lib/hatt/blankslateproxy.rb +33 -0
- data/lib/hatt/configuration.rb +85 -0
- data/lib/hatt/dsl.rb +62 -0
- data/lib/hatt/hattmixin.rb +21 -0
- data/lib/hatt/http.rb +243 -0
- data/lib/hatt/json_helpers.rb +34 -0
- data/lib/hatt/log.rb +43 -0
- data/lib/hatt/mixin.rb +33 -0
- data/lib/hatt/singleton_mixin.rb +34 -0
- data/lib/hatt.rb +1 -0
- metadata +246 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 38589e6ed1df4b139b3339624ba5eb130548c062
|
4
|
+
data.tar.gz: 6b52ef37b1b0fc458b9ffd88c951e20d0efee1bc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a69fbe934e0a246fc33c7e3de4c00f5ae52fff5af334a408dc86319f6c2213fec4ca3e9963993db03c5180d7f06eaaf4a58e500411bf3141a40e5288858d7abc
|
7
|
+
data.tar.gz: 8e88242846ac574f6f811b5166d307aee6f8234789c3aaa1b9058a8e1e14f3ede903f57c07c726eed1d2600b3ed099619f0f5abd0cea1c0dd4017cfc1f60c473
|
data/Gemfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
# misc things from activesupport are used
|
4
|
+
# notifications are used to implement instrumentation
|
5
|
+
gem 'activesupport'
|
6
|
+
# faraday provides the basis for making http requests
|
7
|
+
gem 'faraday'
|
8
|
+
# when will json gem go into the standard lib anyways?
|
9
|
+
gem 'multi_json'
|
10
|
+
# tcfg provides the basis for configuration (hatt.yml) and environments (qa/production/etc)
|
11
|
+
gem 'tcfg', '~> 0.2.2'
|
12
|
+
# typhoes faraday adapter is the default for making http requests
|
13
|
+
# provides a useful mechanism for making concurrent requests as well
|
14
|
+
gem 'typhoeus'
|
15
|
+
# pry is the basis of the REPL feature of the hatt cmd line tool
|
16
|
+
gem 'pry'
|
17
|
+
# awesome_print makes pry better
|
18
|
+
gem 'awesome_print'
|
19
|
+
|
20
|
+
group :development do
|
21
|
+
gem 'debase'
|
22
|
+
gem 'jeweler'
|
23
|
+
gem 'rspec'
|
24
|
+
gem 'rubocop'
|
25
|
+
gem 'ruby-debug-ide'
|
26
|
+
gem 'simplecov'
|
27
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (5.1.0)
|
5
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
|
+
i18n (~> 0.7)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
tzinfo (~> 1.1)
|
9
|
+
addressable (2.5.1)
|
10
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
11
|
+
ast (2.3.0)
|
12
|
+
awesome_print (1.7.0)
|
13
|
+
builder (3.2.3)
|
14
|
+
coderay (1.1.1)
|
15
|
+
concurrent-ruby (1.0.5)
|
16
|
+
debase (0.2.1)
|
17
|
+
debase-ruby_core_source
|
18
|
+
debase-ruby_core_source (0.9.9)
|
19
|
+
descendants_tracker (0.0.4)
|
20
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
21
|
+
diff-lcs (1.3)
|
22
|
+
docile (1.1.5)
|
23
|
+
ethon (0.10.1)
|
24
|
+
ffi (>= 1.3.0)
|
25
|
+
faraday (0.9.2)
|
26
|
+
multipart-post (>= 1.2, < 3)
|
27
|
+
ffi (1.9.18)
|
28
|
+
git (1.3.0)
|
29
|
+
github_api (0.11.3)
|
30
|
+
addressable (~> 2.3)
|
31
|
+
descendants_tracker (~> 0.0.1)
|
32
|
+
faraday (~> 0.8, < 0.10)
|
33
|
+
hashie (>= 1.2)
|
34
|
+
multi_json (>= 1.7.5, < 2.0)
|
35
|
+
nokogiri (~> 1.6.0)
|
36
|
+
oauth2
|
37
|
+
hashie (3.5.5)
|
38
|
+
highline (1.7.8)
|
39
|
+
i18n (0.8.1)
|
40
|
+
jeweler (2.3.5)
|
41
|
+
builder
|
42
|
+
bundler (>= 1.0)
|
43
|
+
git (>= 1.2.5)
|
44
|
+
github_api (~> 0.11.0)
|
45
|
+
highline (>= 1.6.15)
|
46
|
+
nokogiri (>= 1.5.10)
|
47
|
+
psych (~> 2.2)
|
48
|
+
rake
|
49
|
+
rdoc
|
50
|
+
semver2
|
51
|
+
json (2.1.0)
|
52
|
+
jwt (1.5.6)
|
53
|
+
method_source (0.8.2)
|
54
|
+
mini_portile2 (2.1.0)
|
55
|
+
minitest (5.10.1)
|
56
|
+
multi_json (1.12.1)
|
57
|
+
multi_xml (0.6.0)
|
58
|
+
multipart-post (2.0.0)
|
59
|
+
nokogiri (1.6.8.1)
|
60
|
+
mini_portile2 (~> 2.1.0)
|
61
|
+
oauth2 (1.3.1)
|
62
|
+
faraday (>= 0.8, < 0.12)
|
63
|
+
jwt (~> 1.0)
|
64
|
+
multi_json (~> 1.3)
|
65
|
+
multi_xml (~> 0.5)
|
66
|
+
rack (>= 1.2, < 3)
|
67
|
+
parser (2.4.0.0)
|
68
|
+
ast (~> 2.2)
|
69
|
+
powerpack (0.1.1)
|
70
|
+
pry (0.10.4)
|
71
|
+
coderay (~> 1.1.0)
|
72
|
+
method_source (~> 0.8.1)
|
73
|
+
slop (~> 3.4)
|
74
|
+
psych (2.2.4)
|
75
|
+
public_suffix (2.0.5)
|
76
|
+
rack (2.0.1)
|
77
|
+
rainbow (2.2.2)
|
78
|
+
rake
|
79
|
+
rake (12.0.0)
|
80
|
+
rdoc (5.1.0)
|
81
|
+
rspec (3.5.0)
|
82
|
+
rspec-core (~> 3.5.0)
|
83
|
+
rspec-expectations (~> 3.5.0)
|
84
|
+
rspec-mocks (~> 3.5.0)
|
85
|
+
rspec-core (3.5.4)
|
86
|
+
rspec-support (~> 3.5.0)
|
87
|
+
rspec-expectations (3.5.0)
|
88
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
89
|
+
rspec-support (~> 3.5.0)
|
90
|
+
rspec-mocks (3.5.0)
|
91
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
92
|
+
rspec-support (~> 3.5.0)
|
93
|
+
rspec-support (3.5.0)
|
94
|
+
rubocop (0.48.1)
|
95
|
+
parser (>= 2.3.3.1, < 3.0)
|
96
|
+
powerpack (~> 0.1)
|
97
|
+
rainbow (>= 1.99.1, < 3.0)
|
98
|
+
ruby-progressbar (~> 1.7)
|
99
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
100
|
+
ruby-debug-ide (0.6.0)
|
101
|
+
rake (>= 0.8.1)
|
102
|
+
ruby-progressbar (1.8.1)
|
103
|
+
semver2 (3.4.2)
|
104
|
+
simplecov (0.14.1)
|
105
|
+
docile (~> 1.1.0)
|
106
|
+
json (>= 1.8, < 3)
|
107
|
+
simplecov-html (~> 0.10.0)
|
108
|
+
simplecov-html (0.10.0)
|
109
|
+
slop (3.6.0)
|
110
|
+
tcfg (0.2.2)
|
111
|
+
activesupport
|
112
|
+
thread_safe (0.3.6)
|
113
|
+
typhoeus (1.1.2)
|
114
|
+
ethon (>= 0.9.0)
|
115
|
+
tzinfo (1.2.3)
|
116
|
+
thread_safe (~> 0.1)
|
117
|
+
unicode-display_width (1.2.1)
|
118
|
+
|
119
|
+
PLATFORMS
|
120
|
+
ruby
|
121
|
+
|
122
|
+
DEPENDENCIES
|
123
|
+
activesupport
|
124
|
+
awesome_print
|
125
|
+
debase
|
126
|
+
faraday
|
127
|
+
jeweler
|
128
|
+
multi_json
|
129
|
+
pry
|
130
|
+
rspec
|
131
|
+
rubocop
|
132
|
+
ruby-debug-ide
|
133
|
+
simplecov
|
134
|
+
tcfg (~> 0.2.2)
|
135
|
+
typhoeus
|
136
|
+
|
137
|
+
BUNDLED WITH
|
138
|
+
1.13.6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
2
|
+
a copy of this software and associated documentation files (the
|
3
|
+
"Software"), to deal in the Software without restriction, including
|
4
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
5
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
6
|
+
permit persons to whom the Software is furnished to do so, subject to
|
7
|
+
the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be
|
10
|
+
included in all copies or substantial portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
13
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
14
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
15
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
16
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
17
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
18
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
Hatt
|
2
|
+
=======
|
3
|
+
Do cool stuff with HTTP JSON APIs. Designed for the real world of developing systems based on service-oriented-architecture.
|
4
|
+
|
5
|
+
What is Hatt?
|
6
|
+
----------------
|
7
|
+
Hatt is primarily designed to be used by software teams that build HTTP JSON services.
|
8
|
+
Hatt makes it easy to:
|
9
|
+
* Define "macro" methods that accomplish high level goals through service calls.
|
10
|
+
* Call services or macro methods from the command line, adhoc scripts, or programtically within any Ruby program.
|
11
|
+
* Test services using RSpec or other testing frameworks
|
12
|
+
* "Point" at different environments (eg. QA, Staging, Production)
|
13
|
+
|
14
|
+
Hatt relies heavily on the 'convention over configuration' approach. For very little code, you
|
15
|
+
get a lot of functionality.
|
16
|
+
|
17
|
+
Hatt is based on [Faraday](https://github.com/lostisland/faraday), thus there is basic support for making requests concurrently, and for swapping out the HTTP "adapter".
|
18
|
+
|
19
|
+
Getting Started
|
20
|
+
---------------
|
21
|
+
Lets demo how this works using a simple example of an API that requires no
|
22
|
+
authentication.
|
23
|
+
|
24
|
+
The OpenWeatherMap is a good candidate for a simple API
|
25
|
+
which is accessible without needing any authentication key:
|
26
|
+
[http://api.openweathermap.org/]
|
27
|
+
|
28
|
+
|
29
|
+
### make a project directory
|
30
|
+
|
31
|
+
mkdir weather
|
32
|
+
cd weather
|
33
|
+
|
34
|
+
### setup hatt.yml
|
35
|
+
|
36
|
+
services:
|
37
|
+
owm:
|
38
|
+
hostname: api.openweathermap.org
|
39
|
+
|
40
|
+
### make the hatts directory, and a hatt file
|
41
|
+
|
42
|
+
mkdir owm_hatts
|
43
|
+
touch owm_hatts/weather_hatts.rb
|
44
|
+
|
45
|
+
### add method for checking weather to owm_hatts/weather.rb
|
46
|
+
def weather_for city
|
47
|
+
owm.get "/data/2.5/weather?q=#{URI.encode(city)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
### Call it from the cmd line:
|
51
|
+
$ hatt weather_for 'Pleasantville, NY'
|
52
|
+
|
53
|
+
And that returns something like this:
|
54
|
+
{"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}
|
55
|
+
|
56
|
+
Use the -v command line option and see the full request/response logged to StdOut
|
57
|
+
|
58
|
+
### Script it
|
59
|
+
Make a file called temperatures.rb with this in it:
|
60
|
+
|
61
|
+
[
|
62
|
+
'New York, NY',
|
63
|
+
'Toronto, Canada',
|
64
|
+
'Paris, France',
|
65
|
+
'Tokyo, Japan',
|
66
|
+
'Sydney, Australia',
|
67
|
+
].each do |city|
|
68
|
+
weather = weather_for city
|
69
|
+
kelvin = weather['main']['temp']
|
70
|
+
celcius = (kelvin - 273.15).round
|
71
|
+
puts "#{city}: #{celcius} celcius"
|
72
|
+
end
|
73
|
+
|
74
|
+
And then run the hatt script like so:
|
75
|
+
|
76
|
+
hatt -f temperatures.rb
|
77
|
+
|
78
|
+
And get this nice output:
|
79
|
+
|
80
|
+
I: [04/25/13 17:59:22][hatt] - Running data script 'temperatures.rb'
|
81
|
+
New York, NY: 18 celcius
|
82
|
+
Toronto, Canada: 7 celcius
|
83
|
+
Paris, France: 18 celcius
|
84
|
+
Tokyo, Japan: 16 celcius
|
85
|
+
Sydney, Australia: 14 celcius
|
86
|
+
|
87
|
+
### Test it
|
88
|
+
Using the included helper module, testing an api becomes easy. Lets setup a simple RSpec example. Step one is create the spec folder:
|
89
|
+
|
90
|
+
mkdir spec
|
91
|
+
|
92
|
+
Setup spec/spec_helper.rb with contents like:
|
93
|
+
|
94
|
+
require 'hatt/helpers'
|
95
|
+
|
96
|
+
RSpec.configure do |config|
|
97
|
+
config.include Hatt::Helpers
|
98
|
+
end
|
99
|
+
|
100
|
+
Setup spec/weather_spec.rb with contents like:
|
101
|
+
|
102
|
+
require 'spec_helper'
|
103
|
+
describe "getting weather reports" do
|
104
|
+
it "should know the weather for New York City" do
|
105
|
+
response = weather_for 'New York, NY'
|
106
|
+
expected_items = ['temp', 'temp_min', 'temp_max', 'humidity', 'pressure']
|
107
|
+
response['main'].keys.should include(*expected_items)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
Run it:
|
112
|
+
|
113
|
+
gem install rspec
|
114
|
+
rspec
|
115
|
+
|
116
|
+
And get back:
|
117
|
+
|
118
|
+
1 example, 0 failures
|
119
|
+
|
120
|
+
|
data/bin/hatt
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require_relative '../lib/hatt'
|
5
|
+
|
6
|
+
Log = Hatt::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: 'hatt.yml',
|
15
|
+
authenticate: true,
|
16
|
+
repl: false,
|
17
|
+
# by leaving environment unset, hatt will use HATT_ENVIRONMENT variable if no -e is used on cmd line
|
18
|
+
# environment: nil,
|
19
|
+
}
|
20
|
+
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = %(
|
23
|
+
HATT HTTP API Test Tool command line utility:
|
24
|
+
example, run a data script: bin/hatt -f script.rb
|
25
|
+
example, call a dsl method: bin/hatt <hatt dsl method name>
|
26
|
+
|
27
|
+
)
|
28
|
+
|
29
|
+
opts.on('-f', '--script=FILENAME',
|
30
|
+
'specify a script file to run') { |file| options[:files] << file }
|
31
|
+
|
32
|
+
opts.on('-d', '--script-dir=dir',
|
33
|
+
'specify a directory of hatt scripts, run all of them') { |dir| options[:files] += Dir["#{dir}/**/*.rb"] }
|
34
|
+
|
35
|
+
opts.on('-v', '--verbose',
|
36
|
+
'debug logging, see every request and response in detail') { Log.level = Logger::DEBUG }
|
37
|
+
|
38
|
+
opts.on('-q', '--quiet',
|
39
|
+
'quiet logging, warnings and errors only') { Log.level = Logger::WARN }
|
40
|
+
|
41
|
+
opts.on('-c', '--config=FILENAME',
|
42
|
+
'config filename') { |fn| options[:config_file] = fn }
|
43
|
+
|
44
|
+
opts.on('-e', '--environment=ENVIRONMENT',
|
45
|
+
'environment name') do |e|
|
46
|
+
# a bit unfortunate this has to be passed by env var, but it works
|
47
|
+
ENV['HATT_ENVIRONMENT'] = e
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('-n', '--no-authenticate',
|
51
|
+
'Do not authenticate') { options[:authenticate] = false }
|
52
|
+
|
53
|
+
opts.on('-l', '--list', 'Print each available hatt dsl method') do
|
54
|
+
options[:list_hatt_methods] = true
|
55
|
+
options[:authenticate] = false # no point in authenticating for --list
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on('-r', '--pry-repl', 'Launch an interactice repl with full access to hatt services and dsl') do
|
59
|
+
options[:repl] = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
optparse.parse!
|
63
|
+
options
|
64
|
+
end
|
65
|
+
options = parse_cmd_line
|
66
|
+
|
67
|
+
begin
|
68
|
+
hatt = Hatt.new options
|
69
|
+
rescue TCFG::NoSuchConfigFileError => e
|
70
|
+
Log.error e.message
|
71
|
+
Log.error <<-MSG
|
72
|
+
\n
|
73
|
+
Either use -c to specify the location of your hatt config file,
|
74
|
+
or create a file called hatt.yml in the current directory.
|
75
|
+
|
76
|
+
Review documentation on configuration here: <TODO>
|
77
|
+
MSG
|
78
|
+
Kernel.abort
|
79
|
+
end
|
80
|
+
|
81
|
+
if options[:list_hatt_methods]
|
82
|
+
puts 'HATT method listing'
|
83
|
+
|
84
|
+
hatt.singleton_methods.sort.each do |meth|
|
85
|
+
puts " - #{meth}(#{hatt.method(meth).parameters.join(', ')})"
|
86
|
+
end
|
87
|
+
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
if !ARGV.empty?
|
92
|
+
# a hatt method has been specified on the command line
|
93
|
+
method = ARGV.shift
|
94
|
+
response = hatt.send(method.to_sym, *ARGV)
|
95
|
+
Log.debug "HATT method '#{method}' returned:\n" + response.inspect
|
96
|
+
puts response
|
97
|
+
|
98
|
+
elsif !options[:files].empty?
|
99
|
+
# some hatt files were specified on the command line
|
100
|
+
options[:files].each do |script_file|
|
101
|
+
response = hatt.run_script_file(script_file)
|
102
|
+
Log.debug "HATT script '#{script_file}' returtned: #{response.inspect}"
|
103
|
+
puts response
|
104
|
+
end
|
105
|
+
|
106
|
+
elsif options[:repl]
|
107
|
+
Log.debug 'Launching PRY REPL since --pry-repl was specified....'
|
108
|
+
hatt.launch_pry_repl
|
109
|
+
else
|
110
|
+
Kernel.abort 'Nothing to do? use -f or give a method name on the cmd line'
|
111
|
+
end
|
data/hatt.gemspec
ADDED
@@ -0,0 +1,94 @@
|
|
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
|
+
# stub: hatt 0.0.1 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "hatt"
|
9
|
+
s.version = "0.0.1"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.authors = ["Robert Schultheis"]
|
14
|
+
s.date = "2017-05-12"
|
15
|
+
s.description = "convention based approach to interfacing with an HTTP JSON API."
|
16
|
+
s.email = "robert.schultheis@gmail.com"
|
17
|
+
s.executables = ["hatt"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE.txt",
|
20
|
+
"README.rdoc"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"bin/hatt",
|
27
|
+
"hatt.gemspec",
|
28
|
+
"lib/hatt.rb",
|
29
|
+
"lib/hatt/api_clients.rb",
|
30
|
+
"lib/hatt/base.rb",
|
31
|
+
"lib/hatt/blankslateproxy.rb",
|
32
|
+
"lib/hatt/configuration.rb",
|
33
|
+
"lib/hatt/dsl.rb",
|
34
|
+
"lib/hatt/hattmixin.rb",
|
35
|
+
"lib/hatt/http.rb",
|
36
|
+
"lib/hatt/json_helpers.rb",
|
37
|
+
"lib/hatt/log.rb",
|
38
|
+
"lib/hatt/mixin.rb",
|
39
|
+
"lib/hatt/singleton_mixin.rb"
|
40
|
+
]
|
41
|
+
s.homepage = "http://github.com/rschultheis/hatt"
|
42
|
+
s.licenses = ["MIT"]
|
43
|
+
s.rubygems_version = "2.5.1"
|
44
|
+
s.summary = "Make calls to HTTP JSON APIs with ease and confidence"
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
s.specification_version = 4
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
51
|
+
s.add_runtime_dependency(%q<faraday>, [">= 0"])
|
52
|
+
s.add_runtime_dependency(%q<multi_json>, [">= 0"])
|
53
|
+
s.add_runtime_dependency(%q<tcfg>, ["~> 0.2.2"])
|
54
|
+
s.add_runtime_dependency(%q<typhoeus>, [">= 0"])
|
55
|
+
s.add_runtime_dependency(%q<pry>, [">= 0"])
|
56
|
+
s.add_runtime_dependency(%q<awesome_print>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<debase>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<rubocop>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<ruby-debug-ide>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
65
|
+
s.add_dependency(%q<faraday>, [">= 0"])
|
66
|
+
s.add_dependency(%q<multi_json>, [">= 0"])
|
67
|
+
s.add_dependency(%q<tcfg>, ["~> 0.2.2"])
|
68
|
+
s.add_dependency(%q<typhoeus>, [">= 0"])
|
69
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
70
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
71
|
+
s.add_dependency(%q<debase>, [">= 0"])
|
72
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rubocop>, [">= 0"])
|
75
|
+
s.add_dependency(%q<ruby-debug-ide>, [">= 0"])
|
76
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
80
|
+
s.add_dependency(%q<faraday>, [">= 0"])
|
81
|
+
s.add_dependency(%q<multi_json>, [">= 0"])
|
82
|
+
s.add_dependency(%q<tcfg>, ["~> 0.2.2"])
|
83
|
+
s.add_dependency(%q<typhoeus>, [">= 0"])
|
84
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
85
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
86
|
+
s.add_dependency(%q<debase>, [">= 0"])
|
87
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
88
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rubocop>, [">= 0"])
|
90
|
+
s.add_dependency(%q<ruby-debug-ide>, [">= 0"])
|
91
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'http'
|
2
|
+
require_relative 'configuration'
|
3
|
+
|
4
|
+
module Hatt
|
5
|
+
module ApiClients
|
6
|
+
include Hatt::Configuration
|
7
|
+
|
8
|
+
def hatt_build_client_methods
|
9
|
+
hatt_configuration[:hatt_services].each_pair do |svc, svc_cfg|
|
10
|
+
hatt_add_service svc, svc_cfg
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# add a service to hatt
|
15
|
+
#
|
16
|
+
# @param name [String] the name of the service
|
17
|
+
# @param url [String] an absolute url to the api
|
18
|
+
def hatt_add_service(name, url_or_svc_cfg_hash)
|
19
|
+
svc_cfg = case url_or_svc_cfg_hash
|
20
|
+
when String
|
21
|
+
{ 'url' => url_or_svc_cfg_hash }
|
22
|
+
when Hash
|
23
|
+
url_or_svc_cfg_hash
|
24
|
+
else
|
25
|
+
raise ArgumentError, "'#{url_or_svc_cfg_hash}' is not a url string nor hash with url key"
|
26
|
+
end
|
27
|
+
|
28
|
+
init_config
|
29
|
+
services_config = hatt_configuration['hatt_services']
|
30
|
+
services_config[name] = svc_cfg
|
31
|
+
@hatt_configuration.tcfg_set 'hatt_services', services_config
|
32
|
+
|
33
|
+
@hatt_http_clients ||= {}
|
34
|
+
@hatt_http_clients[name] = Hatt::HTTP.new hatt_configuration['hatt_services'][name]
|
35
|
+
define_singleton_method name.intern do
|
36
|
+
@hatt_http_clients[name]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/hatt/base.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require_relative 'mixin'
|
3
|
+
require_relative 'singleton_mixin'
|
4
|
+
|
5
|
+
module Hatt
|
6
|
+
class << self
|
7
|
+
include Hatt::Mixin
|
8
|
+
|
9
|
+
def new(options = {})
|
10
|
+
options = ActiveSupport::HashWithIndifferentAccess.new options
|
11
|
+
hatt_instance = Class.new { include Hatt::Mixin }.new
|
12
|
+
hatt_instance.hatt_config_file options.fetch(:config_file, 'hatt.yml')
|
13
|
+
hatt_instance.hatt_initialize
|
14
|
+
hatt_instance
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hatt
|
2
|
+
# A simple 'blankslate' class which is used to avoid namespace polution
|
3
|
+
# Provides a proxy to a parent class, in order to allow access to other hatt methods like http service objects
|
4
|
+
#
|
5
|
+
# TODO: Figure out if can/should use a BasicObject here. I know it wont work right now
|
6
|
+
# because the binding method of Object is needed and not in BasicObject
|
7
|
+
#
|
8
|
+
class BlankSlateProxy < Object
|
9
|
+
def initialize(parent)
|
10
|
+
@parent = parent
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_id, *arguments, &block)
|
14
|
+
if parent_has_method?(method_id)
|
15
|
+
@parent.send(method_id, *arguments, &block)
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def parent_has_method?(name, include_private = false)
|
22
|
+
if include_private
|
23
|
+
@parent.methods.include?(name.to_sym)
|
24
|
+
else
|
25
|
+
@parent.public_methods.include?(name.to_sym)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to_missing?(name, include_private = false)
|
30
|
+
parent_has_method?(name, include_private) || super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
require 'tcfg/tcfg_base'
|
4
|
+
|
5
|
+
module Hatt
|
6
|
+
module Configuration
|
7
|
+
DEFAULT_CONFIG_FILE = 'hatt.yml'.freeze
|
8
|
+
DEFAULT_HATT_GLOBS = ['*_hattdsl/*_hattdsl.rb'].freeze
|
9
|
+
|
10
|
+
def hatt_configuration
|
11
|
+
init_config
|
12
|
+
@hatt_configuration.tcfg
|
13
|
+
end
|
14
|
+
|
15
|
+
def hatt_config_file=(filename)
|
16
|
+
@hatt_config_file = filename
|
17
|
+
init_config
|
18
|
+
self
|
19
|
+
end
|
20
|
+
alias hatt_config_file hatt_config_file=
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def hatt_config_file_path
|
25
|
+
if instance_variable_defined? :@hatt_config_file and not @hatt_config_file.nil?
|
26
|
+
File.expand_path @hatt_config_file
|
27
|
+
elsif File.exist? DEFAULT_CONFIG_FILE
|
28
|
+
File.expand_path DEFAULT_CONFIG_FILE
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# ensure the configuration is resolved and ready to use
|
35
|
+
def init_config
|
36
|
+
unless instance_variable_defined? :@hatt_configuration
|
37
|
+
@hatt_configuration = TCFG::Base.new
|
38
|
+
|
39
|
+
# build up some default configuration
|
40
|
+
@hatt_configuration.tcfg_set 'hatt_services', {}
|
41
|
+
@hatt_configuration.tcfg_set 'hatt_globs', DEFAULT_HATT_GLOBS
|
42
|
+
|
43
|
+
@hatt_configuration.tcfg_set_env_var_prefix 'HATT_'
|
44
|
+
end
|
45
|
+
|
46
|
+
if hatt_config_file_path
|
47
|
+
# if a config file was specified, we assume it exists
|
48
|
+
@hatt_configuration.tcfg_config_file hatt_config_file_path
|
49
|
+
else
|
50
|
+
Log.warn 'No configuration file specified or found. Make a hatt.yml file and point hatt at it.'
|
51
|
+
end
|
52
|
+
@hatt_configuration.tcfg_set 'hatt_config_file', hatt_config_file_path
|
53
|
+
|
54
|
+
normalize_services_config @hatt_configuration
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def normalize_services_config(cfg)
|
60
|
+
services = cfg['hatt_services']
|
61
|
+
services.each_pair do |svc, svc_cfg|
|
62
|
+
# add the name
|
63
|
+
svc_cfg['name'] = svc
|
64
|
+
|
65
|
+
unless svc_cfg.key? :url
|
66
|
+
raise ConfigurationError, "url for service '#{svc_cfg['name']}' is not defined"
|
67
|
+
end
|
68
|
+
|
69
|
+
unless svc_cfg['url'] =~ URI::ABS_URI
|
70
|
+
raise ConfigurationError, "url for service '#{svc_cfg['name']}' is not a proper absolute URL: #{svc_cfg['url']}"
|
71
|
+
end
|
72
|
+
|
73
|
+
parsed = URI.parse svc_cfg['url']
|
74
|
+
|
75
|
+
svc_cfg['base_uri'] ||= parsed.path
|
76
|
+
|
77
|
+
svc_cfg['faraday_url'] = "#{parsed.scheme}://#{parsed.host}:#{parsed.port}"
|
78
|
+
end
|
79
|
+
|
80
|
+
cfg.tcfg_set 'hatt_services', services
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class ConfigurationError < StandardError; end
|
85
|
+
end
|