grifter 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Robert Schultheis (Knewton)
4
+ # This is a slightly modified BlankSlate, as taken from https://github.com/masover/blankslate
5
+ # Included is some stuff allowing us to send methods back to a parent
6
+ # Which makes our api script DSL very easy to use
7
+ #
8
+ # Original file comments:
9
+ #
10
+ #--
11
+ # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
12
+ # All rights reserved.
13
+
14
+ # Permission is granted for use, copying, modification, distribution,
15
+ # and distribution of modified versions of this work as long as the
16
+ # above copyright notice is included.
17
+ #++
18
+
19
+ ######################################################################
20
+ # BlankSlate provides an abstract base class with no predefined
21
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
22
+ # BlankSlate is useful as a base class when writing classes that
23
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
24
+ #
25
+ class BlankSlate
26
+ class << self
27
+
28
+ # Hide the method named +name+ in the BlankSlate class. Don't
29
+ # hide +instance_eval+ or any method beginning with "__".
30
+ def hide(name)
31
+ methods = instance_methods.map(&:to_sym)
32
+ if methods.include?(name.to_sym) and
33
+ name !~ /^(__|instance_eval)/
34
+ @hidden_methods ||= {}
35
+ @hidden_methods[name.to_sym] = instance_method(name)
36
+ undef_method name
37
+ end
38
+ end
39
+
40
+ def find_hidden_method(name)
41
+ @hidden_methods ||= {}
42
+ @hidden_methods[name] || superclass.find_hidden_method(name)
43
+ end
44
+
45
+ # Redefine a previously hidden method so that it may be called on a blank
46
+ # slate object.
47
+ def reveal(name)
48
+ hidden_method = find_hidden_method(name)
49
+ fail "Don't know how to reveal method '#{name}'" unless hidden_method
50
+ define_method(name, hidden_method)
51
+ end
52
+ end
53
+
54
+ (instance_methods - [:object_id]).each { |m| hide(m) }
55
+
56
+ def initialize(parent)
57
+ @parent = parent
58
+ end
59
+
60
+ def method_missing(method_id, *arguments, &block)
61
+ @parent.send(method_id, *arguments, &block)
62
+ end
63
+ end
64
+
65
+ ######################################################################
66
+ # Since Ruby is very dynamic, methods added to the ancestors of
67
+ # BlankSlate <em>after BlankSlate is defined</em> will show up in the
68
+ # list of available BlankSlate methods. We handle this by defining a
69
+ # hook in the Object and Kernel classes that will hide any method
70
+ # defined after BlankSlate has been loaded.
71
+ #
72
+ module Kernel
73
+ class << self
74
+ alias_method :blank_slate_method_added, :method_added
75
+
76
+ # Detect method additions to Kernel and remove them in the
77
+ # BlankSlate class.
78
+ def method_added(name)
79
+ result = blank_slate_method_added(name)
80
+ return result if self != Kernel
81
+ BlankSlate.hide(name)
82
+ result
83
+ end
84
+ end
85
+ end
86
+
87
+ ######################################################################
88
+ # Same as above, except in Object.
89
+ #
90
+ class Object
91
+ class << self
92
+ alias_method :blank_slate_method_added, :method_added
93
+
94
+ # Detect method additions to Object and remove them in the
95
+ # BlankSlate class.
96
+ def method_added(name)
97
+ result = blank_slate_method_added(name)
98
+ return result if self != Object
99
+ BlankSlate.hide(name)
100
+ result
101
+ end
102
+
103
+ def find_hidden_method(name)
104
+ nil
105
+ end
106
+ end
107
+ end
108
+
109
+ ######################################################################
110
+ # Also, modules included into Object need to be scanned and have their
111
+ # instance methods removed from blank slate. In theory, modules
112
+ # included into Kernel would have to be removed as well, but a
113
+ # "feature" of Ruby prevents late includes into modules from being
114
+ # exposed in the first place.
115
+ #
116
+ class Module
117
+ alias blankslate_original_append_features append_features
118
+ def append_features(mod)
119
+ result = blankslate_original_append_features(mod)
120
+ return result if mod != Object
121
+ instance_methods.each do |name|
122
+ BlankSlate.hide(name)
123
+ end
124
+ result
125
+ end
126
+ end
@@ -0,0 +1,89 @@
1
+ require 'yaml'
2
+
3
+ require_relative 'log'
4
+
5
+ class Grifter
6
+ module Configuration
7
+ def recursive_symbolize hash
8
+ hash.inject({}) do |h, (k,v)|
9
+ h[k.intern] = case v
10
+ when Hash
11
+ recursive_symbolize v
12
+ else
13
+ v
14
+ end
15
+ h
16
+ end
17
+ end
18
+
19
+ def load_config_file options={}
20
+ options = {
21
+ config_file: ENV['GRIFTER_CONFIG_FILE'] ? ENV['GRIFTER_CONFIG_FILE'] : 'grifter.yml',
22
+ environment: ENV['GRIFTER_ENVIRONMENT'],
23
+ }.merge(options)
24
+ Log.debug "Loading config file '#{options[:config_file]}'"
25
+ unless File.exist?(options[:config_file])
26
+ raise "No such config file: '#{options[:config_file]}'"
27
+ end
28
+ hash = YAML.load_file(options[:config_file])
29
+ symbolized = recursive_symbolize(hash)
30
+ normalize_config(symbolized, options)
31
+ end
32
+
33
+ #this method ensure the config hash has everything we need to instantiate the service objects
34
+ def normalize_config config, options={}
35
+ unless ((config.is_a? Hash) &&
36
+ (config.has_key? :services) &&
37
+ (config[:services].is_a? Hash) &&
38
+ (config[:services].length > 0))
39
+ raise GrifterConfigurationError.new ":services block not found in configuration"
40
+ end
41
+
42
+ #fill out services block entirely for each service
43
+ config[:services].each_pair do |service_name, service_config|
44
+ service_config[:name] = service_name.to_s
45
+ #setup port config
46
+ unless service_config[:port]
47
+ if service_config[:ssl]
48
+ service_config[:port] = 443
49
+ else
50
+ service_config[:port] = 80
51
+ end
52
+ end
53
+
54
+ #ssl config
55
+ unless service_config[:ssl]
56
+ service_config[:ssl] = false
57
+ end
58
+
59
+ #ignore_ssl_certificate
60
+ unless service_config[:ignore_ssl_cert]
61
+ service_config[:ignore_ssl_cert] = false
62
+ end
63
+
64
+ unless service_config[:base_uri]
65
+ service_config[:base_uri] = ''
66
+ end
67
+
68
+ end
69
+
70
+ #merge any environment overrides into the service block
71
+ if options[:environment]
72
+ options[:environment] = options[:environment].to_sym
73
+ unless config[:environments] && config[:environments][options[:environment]]
74
+ raise GrifterConfigurationError.new "No such environment specified in config: '#{options[:environment]}'"
75
+ end
76
+
77
+ config[:environments][options[:environment]].each_pair do |service_name, service_overrides|
78
+ config[:services][service_name].merge! service_overrides
79
+ end
80
+ end
81
+
82
+
83
+ return config
84
+ end
85
+ end
86
+ end
87
+
88
+ class GrifterConfigurationError < Exception
89
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../grifter'
2
+
3
+ class Grifter
4
+ module Helpers
5
+
6
+ def grifter_instance
7
+ @@grifter_instance ||= ::Grifter.new
8
+ end
9
+ module_function :grifter_instance
10
+
11
+ def self.included(mod)
12
+ def grifter
13
+ grifter_instance
14
+ end
15
+
16
+ grifter_instance.singleton_methods.each do |meth|
17
+ define_method meth do |*args, &block|
18
+ grifter_instance.send(meth, *args, &block)
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,142 @@
1
+ require 'net/http'
2
+
3
+ require_relative 'json_helpers'
4
+ require_relative 'log'
5
+
6
+ class Grifter
7
+ class HTTPService
8
+
9
+ include Grifter::JsonHelpers
10
+
11
+ def initialize config
12
+
13
+ @config = config
14
+ @name = config[:name]
15
+ @base_uri = config[:base_uri]
16
+
17
+ Log.debug "Configuring service '#{@name}' with:\n\t#{@config.inspect}"
18
+
19
+ @http = Net::HTTP.new(@config[:hostname], @config[:port])
20
+ @http.use_ssl = @config[:ssl]
21
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @config[:ignore_ssl_cert]
22
+
23
+ @headers = {
24
+ 'accept' => 'application/json',
25
+ 'content-type' => 'application/json',
26
+ }
27
+ if @config[:default_headers]
28
+ Log.debug "Default headers configured: " + @config[:default_headers].inspect
29
+ @config[:default_headers].each_pair do |k, v|
30
+ @headers[k.to_s] = v.to_s
31
+ end
32
+ end
33
+ end
34
+
35
+ #allow stubbing http if we are testing
36
+ attr_reader :http if defined?(RSpec)
37
+
38
+ attr_reader :headers, :name, :config
39
+
40
+ #this is useful for testing apis, and other times
41
+ #you want to interrogate the http details of a response
42
+ attr_reader :last_request, :last_response
43
+
44
+ RequestLogSeperator = '-'*40
45
+ def do_request req
46
+ Log.debug RequestLogSeperator
47
+ Log.debug "#{req.class} #{req.path}"
48
+ Log.debug "HEADERS: #{req.to_hash}"
49
+ Log.debug "BODY:\n#{req.body}" if req.request_body_permitted?
50
+ response = @http.request(req)
51
+ Log.debug "RESPONSE CODE: #{response.code}"
52
+ Log.debug "RESPONSE HEADERS: #{response.to_hash}"
53
+ Log.debug "RESPONSE BODY:\n#{jsonify response.body}\n"
54
+
55
+ @last_request = req
56
+ @last_response = response
57
+
58
+ raise RequestException.new(req, response) unless response.kind_of? Net::HTTPSuccess
59
+
60
+ objectify response.body
61
+ end
62
+
63
+ #add base uri to request
64
+ def make_path path_suffix, base_uri=nil
65
+ base_uri_to_use = base_uri ? base_uri : @base_uri
66
+ if base_uri_to_use
67
+ base_uri_to_use + path_suffix
68
+ else
69
+ path_suffix
70
+ end
71
+ end
72
+
73
+ def make_headers options
74
+ if options[:additional_headers]
75
+ @headers.merge options[:additional_headers]
76
+ elsif options[:headers]
77
+ options[:headers]
78
+ else
79
+ @headers
80
+ end
81
+ end
82
+
83
+ def req_args path, options
84
+ [make_path(path, options[:base_uri]), make_headers(options)]
85
+ end
86
+
87
+ def get path, options={}
88
+ req = Net::HTTP::Get.new(*req_args(path, options))
89
+ do_request req
90
+ end
91
+
92
+ def head path, options={}
93
+ req = Net::HTTP::Head.new(*req_args(path, options))
94
+ do_request req
95
+ end
96
+
97
+ def delete path, options={}
98
+ req = Net::HTTP::Delete.new(*req_args(path, options))
99
+ do_request req
100
+ end
101
+
102
+ def post path, obj, options={}
103
+ req = Net::HTTP::Post.new(*req_args(path, options))
104
+ req.body = jsonify(obj)
105
+ do_request req
106
+ end
107
+
108
+ def put path, obj, options={}
109
+ req = Net::HTTP::Put.new(*req_args(path, options))
110
+ req.body = jsonify(obj)
111
+ do_request req
112
+ end
113
+
114
+ def post_form path, params, options={}
115
+ request_obj = Net::HTTP::Post.new(*req_args(path, options))
116
+ request_obj.set_form_data params
117
+ request_obj.basic_auth(options[:username], options[:password]) if options[:username]
118
+ do_request request_obj
119
+ end
120
+ end
121
+
122
+ class RequestException < Exception
123
+ def initialize(request, response)
124
+ @request, @response = request, response
125
+ end
126
+
127
+ #this makes good info show up in rspec reports
128
+ def to_s
129
+ "#{self.class}\nResponseCode: #{@response.code}\nResponseBody:\n#{@response.body}"
130
+ end
131
+
132
+ #shortcut methods
133
+ def code
134
+ @response.code
135
+ end
136
+
137
+ def body
138
+ @response.body
139
+ end
140
+ end
141
+ end
142
+
@@ -0,0 +1,38 @@
1
+ require 'json'
2
+
3
+ class Grifter
4
+ module JsonHelpers
5
+
6
+ #always returns a string, intended for request bodies
7
+ #every attempt is made to ensure string is valid json
8
+ #but if that is not possible, then its returned as is
9
+ def jsonify obj
10
+ case obj
11
+ when String
12
+ JSON.pretty_generate(JSON.parse(obj))
13
+ when Hash, Array
14
+ JSON.pretty_generate(obj)
15
+ else
16
+ obj.to_s
17
+ end
18
+ rescue Exception
19
+ obj.to_s
20
+ end
21
+ #module_function :jsonify
22
+
23
+ #attempts to parse json strings into native ruby objects
24
+ def objectify json_string
25
+ case json_string
26
+ when Hash, Array
27
+ return json_string
28
+ else
29
+ JSON.parse(json_string.to_s)
30
+ end
31
+ rescue Exception => e
32
+ Log.debug "Unable to parse non-json object: #{e.to_s}"
33
+ json_string
34
+ end
35
+ #module_function :objectify
36
+
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'logger'
2
+
3
+ class Grifter
4
+ class Log
5
+ GrifterFormatter = proc do |severity, datetime, progname, msg|
6
+ "#{severity[0]}: [#{datetime.strftime('%m/%d/%y %H:%M:%S')}][#{progname}] - #{msg}\n"
7
+ end
8
+
9
+ @@loggers = []
10
+ def self.add_logger handle
11
+ new_logger = Logger.new handle
12
+ new_logger.progname = 'grifter'
13
+ new_logger.formatter = GrifterFormatter
14
+ @@loggers << new_logger
15
+ end
16
+
17
+ self.add_logger(STDOUT)
18
+
19
+ def self.level= log_level
20
+ @@loggers.each { |logger| logger.level = log_level}
21
+ end
22
+
23
+ def self.log level, msg
24
+ @@loggers.each {|logger| logger.send(level, msg)}
25
+ end
26
+
27
+ [:fatal, :error, :warn, :info,:debug].each do |log_method|
28
+ define_singleton_method log_method do |msg|
29
+ log(log_method, msg)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+