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.
@@ -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
+