api_hammer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30f42f8b22545e005c29603af5d98d6874e8027b
4
- data.tar.gz: 7257d0076928ecc3c328596243c3b16d465dba6b
3
+ metadata.gz: d674265eaf92948690ef36086f4e6858103e0d92
4
+ data.tar.gz: 00af75f0635b6e4fcd1a373936d2524e74852afb
5
5
  SHA512:
6
- metadata.gz: a3ea4254d06322b584a4b78ec25d3334280c5118406913500cfaf8d2fbc84a9c5478fd6c0d2bfdb15ead42f795b58a9abc61bfa7daff88696195df93cd21f2d3
7
- data.tar.gz: a275cccb8fba41c50c2781bc2423477bec6a688b0d831145ee044889193ab9d70105bf17a138d88cf3380ee4fbbd2216ad1ea42265609bef407729e90680468d
6
+ metadata.gz: 91be2d6f0a923086524397f7aed0c00029db8c049f59029cbf9e9c3d0f4754009b76702af132a2797484a80111a24d13eb7b3017367f668389172dabbd882038
7
+ data.tar.gz: 6d46171d830d5c41794569e2aa73762fa448b72a831e01d2b146cb6e4850998845eb69048a770bb0c75b80048a895b4aa95e98e1e62ce47f40dacb559d03eb49
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.2.0
2
+ - Rails#unmunged_request_params
3
+ - hc --pretty
4
+ - hc default User-Agent set
5
+ - hc doc improved, middlewares moved lib
6
+ - RequestLogger improved
7
+ - ApiHammer::RailsRequestLogging railtie
8
+ - README improved marginally
9
+
1
10
  # 0.1.0
2
11
  - Object#public_instance_exec
3
12
  - Obect#public_instance_eval
data/README.md CHANGED
@@ -1,3 +1,30 @@
1
1
  # ApiHammer
2
2
 
3
- An API Tool
3
+ a collection of little tools I have used when creating APIs. these are generally too small to have their own
4
+ library, so have been collected here.
5
+
6
+ ## ApiHammer::Rails
7
+
8
+ A module for inclusion in a Rails application controller inheriting from ActionController::Base (Rocketpants
9
+ should also work, but no guarantees).
10
+
11
+ ### #halt
12
+
13
+ You know that pattern in rails, `render(some_stuff) and return`?
14
+
15
+ Don't use it. It's wrong. `and` is used to short-circuit, but you don't actually want your `return` to be conditional on the `render`. Since it is conditional on the return value of `render`, if the return value of `render` is false-ish (`false` or `nil`), you may end end up failing to return. The actual return value of `render` is undocumented. If you follow all the control paths that can affect the return value, last time I looked there were 37 different paths. None of them returns false, but if any control path changes to return false then `render and return` will break.
16
+
17
+ What you really mean is `render(some_stuff); return`, but semicolons are (rightly) frowned upon
18
+ in ruby. Really you should just put the `return` on the next line.
19
+
20
+ Or, use `#halt`, kindly provided by ApiHammer.
21
+
22
+ `ApiHammer#halt` is based on the Sinatra pattern of `throw(:halt, response)`. It uses exceptions because Rails has baked-in exception rescuing (but not, at least as far as I have found, throw catching), but that is hidden away.
23
+
24
+ ### #check_required_params
25
+
26
+ Pass it an parameters which are required, and if they are missing or incorrect it will halt with 422 (not 400 - 400 is wrong).
27
+
28
+ ## Other
29
+
30
+ Various other things. This readme is incomplete and will be updated soon.
data/bin/hc CHANGED
@@ -1,10 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ lib = File.expand_path('../lib', File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.any? { |lp| File.expand_path(lp) == File.expand_path(lib) }
5
+
3
6
  require 'rubygems'
4
7
  require 'faraday'
5
8
  require 'logger'
6
9
  require 'yaml'
7
10
 
11
+ require 'api_hammer/faraday/outputter'
12
+ require 'api_hammer/version'
13
+
8
14
  # OPTION PARSER
9
15
 
10
16
  require 'optparse'
@@ -13,6 +19,7 @@ require 'optparse'
13
19
  $options = {
14
20
  :verbose => true,
15
21
  :color => nil,
22
+ :pretty => nil,
16
23
  :no_ssl_verify => false,
17
24
  :headers => {},
18
25
  }
@@ -31,29 +38,32 @@ opt_parser = OptionParser.new do |opts|
31
38
  opts.on("--[no-]color", "Color the output (defaults to color if the output device is a TTY)") do |v|
32
39
  $options[:color] = v
33
40
  end
34
- opts.on("-t", "--content-type CONTENT-TYPE", "Sets the Content-Type header of the request") do |v|
41
+ opts.on("--[no-]pretty", "Format the output prettily if supported. (JSON is supported.) defaults to true.") do |v|
42
+ $options[:pretty] = v
43
+ end
44
+ opts.on("-t", "--content-type CONTENT-TYPE", "Sets the Content-Type header of the request. This defaults to application/json if a body is included.") do |v|
35
45
  $options[:headers]['Content-Type'.downcase] = v
36
46
  end
37
- opts.on("--oauth-token TOKEN") do |token|
47
+ opts.on("--oauth-token TOKEN", "OAuth 1.0 token") do |token|
38
48
  $oauth[:token] = token
39
49
  end
40
- opts.on("--oauth-token-secret TOKEN_SECRET") do |token_secret|
50
+ opts.on("--oauth-token-secret TOKEN_SECRET", "OAuth 1.0 token secret") do |token_secret|
41
51
  $oauth[:token_secret] = token_secret
42
52
  end
43
- opts.on("--oauth-consumer-key CONSUMER_KEY") do |consumer_key|
53
+ opts.on("--oauth-consumer-key CONSUMER_KEY", "OAuth 1.0 consumer key") do |consumer_key|
44
54
  $oauth[:consumer_key] = consumer_key
45
55
  end
46
- opts.on("--oauth-consumer-secret CONSUMER_SECRET") do |consumer_secret|
56
+ opts.on("--oauth-consumer-secret CONSUMER_SECRET", "OAuth 1.0 consumer secret") do |consumer_secret|
47
57
  $oauth[:consumer_secret] = consumer_secret
48
58
  end
49
- opts.on("--oauth-signature-method SIGNATURE_METHOD") do |signature_method|
59
+ opts.on("--oauth-signature-method SIGNATURE_METHOD", "OAuth 1.0 signature method - defaults to HMAC-SHA1") do |signature_method|
50
60
  $oauth[:signature_method] = signature_method
51
61
  end
52
62
 
53
63
  opts.on("--no-ssl-verify", "Disables SSL verification - use cautiously!") do
54
64
  $options[:no_ssl_verify] = true
55
65
  end
56
- opts.on("-H", "--header HEADER", "Set a header") do |header|
66
+ opts.on("-H", "--header HEADER", %q(Set a header, e.g. -H "User-Agent: mycoolapp")) do |header|
57
67
  if header =~ /\A([^:]+):\s*(.*)\z/m # this could be more strictly conformant to rfc, but whatever
58
68
  field_name = $1
59
69
  field_value = $2
@@ -66,126 +76,6 @@ end
66
76
  opt_parser.parse!
67
77
  abort(opt_parser.help) unless (2..3).include?(ARGV.size)
68
78
 
69
- # OUTPUTTERS FOR FARADAY THAT SHOULD MOVE TO A LIB SOMEWHERE
70
-
71
- # outputs the response body to the given output device (defaulting to STDOUT)
72
- class FaradayOutputter < Faraday::Middleware
73
- def initialize(app, outdev=STDOUT)
74
- @app=app
75
- @outdev = outdev
76
- end
77
-
78
- def call(request_env)
79
- @app.call(request_env).on_complete do |response_env|
80
- @outdev.puts(response_env[:body] || '')
81
- end
82
- end
83
- end
84
-
85
- # this is to approximate `curl -v`s output. but it's all faked, whereas curl gives you
86
- # the real text written and read for request and response. whatever, close enough.
87
- class FaradayCurlVOutputter < FaradayOutputter
88
-
89
- # defines a method with the given name, applying coloring defined by any additional arguments.
90
- # if $options[:color] is set, respects that; otherwise, applies color if the output device is a tty.
91
- def self.color(name, *color_args)
92
- define_method(name) do |arg|
93
- if color?
94
- require 'term/ansicolor'
95
- color_args.inject(arg) do |result, color_arg|
96
- Term::ANSIColor.send(color_arg, result)
97
- end
98
- else
99
- arg
100
- end
101
- end
102
- end
103
-
104
- color :info, :intense_yellow
105
- color :info_body, :yellow
106
- color :protocol
107
-
108
- color :request, :intense_cyan
109
- color :request_verb, :bold
110
- color :request_header
111
- color :request_blankline, :intense_cyan, :bold
112
-
113
- color :response, :intense_green
114
- color :response_status, :bold, :green
115
- color :response_header
116
- color :response_blankline, :intense_green, :bold
117
-
118
- def call(request_env)
119
- @outdev.puts "#{info('*')} #{info_body("connect to #{request_env[:url].host} on port #{request_env[:url].port}")}"
120
- @outdev.puts "#{info('*')} #{info_body("getting our SSL on")}" if request_env[:url].scheme=='https'
121
- @outdev.puts "#{request('>')} #{request_verb(request_env[:method].to_s.upcase)} #{request_env[:url].request_uri} #{protocol('HTTP/1.1' || 'or something - TODO')}"
122
- request_env[:request_headers].each do |k, v|
123
- @outdev.puts "#{request('>')} #{request_header(k)}#{request(':')} #{v}"
124
- end
125
- @outdev.puts "#{request_blankline('>')} "
126
- request_body = color_body_by_content_type(request_env[:body], request_env[:request_headers]['Content-Type'])
127
- (request_body || '').split("\n", -1).each do |line|
128
- @outdev.puts "#{request('>')} #{line}"
129
- end
130
- @app.call(request_env).on_complete do |response_env|
131
- @outdev.puts "#{response('<')} #{protocol('HTTP/1.1' || 'or something - TODO')} #{response_status(response_env[:status].to_s)}"
132
- request_env[:response_headers].each do |k, v|
133
- @outdev.puts "#{response('<')} #{response_header(k)}#{response(':')} #{v}"
134
- end
135
- @outdev.puts "#{response_blankline ('<')} "
136
- response_body = color_body_by_content_type(response_env[:body], response_env[:response_headers]['Content-Type'])
137
- (response_body || '').split("\n", -1).each do |line|
138
- @outdev.puts "#{response('<')} #{line}"
139
- end
140
- end
141
- end
142
-
143
- # whether to use color
144
- def color?
145
- $options[:color].nil? ? @outdev.tty? : $options[:color]
146
- end
147
-
148
- # a mapping for each registered CodeRay scanner to the Media Types which represent
149
- # that language. extremely incomplete!
150
- CodeRayForMediaTypes = {
151
- :c => [],
152
- :cpp => [],
153
- :clojure => [],
154
- :css => ['text/css', 'application/css-stylesheet'],
155
- :delphi => [],
156
- :diff => [],
157
- :erb => [],
158
- :groovy => [],
159
- :haml => [],
160
- :html => ['text/html'],
161
- :java => [],
162
- :java_script => ['application/javascript', 'text/javascript', 'application/x-javascript'],
163
- :json => ['application/json'],
164
- :php => [],
165
- :python => ['text/x-python'],
166
- :ruby => [],
167
- :sql => [],
168
- :xml => ['text/xml', 'application/xml', %r(\Aapplication/.*\+xml\z)],
169
- :yaml => [],
170
- }
171
-
172
- # takes a body and a content type; returns the body, with coloring (ansi colors for terminals)
173
- # possibly added, if it's a recognized content type and #color? is true
174
- def color_body_by_content_type(body, content_type)
175
- if body && color?
176
- # kinda hacky way to get the media_type. faraday should supply this ...
177
- require 'rack'
178
- media_type = ::Rack::Request.new({'CONTENT_TYPE' => content_type}).media_type
179
- coderay_scanner = CodeRayForMediaTypes.reject{|k,v| !v.any?{|type| type === media_type} }.keys.first
180
- if coderay_scanner
181
- require 'coderay'
182
- body = CodeRay.scan(body, coderay_scanner).encode(:terminal)
183
- end
184
- end
185
- body
186
- end
187
- end
188
-
189
79
  # CONFIGURE THE FARADAY CONNECTION
190
80
  faraday_options = {}
191
81
  if $options[:no_ssl_verify]
@@ -198,7 +88,7 @@ connection = Faraday.new(faraday_options) do |builder|
198
88
  OAuthenticator::FaradaySigner
199
89
  builder.use OAuthenticator::FaradaySigner, $oauth
200
90
  end
201
- builder.use $options[:verbose] ? FaradayCurlVOutputter : FaradayOutputter
91
+ builder.use($options[:verbose] ? FaradayCurlVOutputter : FaradayOutputter, $options)
202
92
  builder.adapter Faraday.default_adapter
203
93
  end
204
94
 
@@ -215,6 +105,10 @@ if body && !headers['Content-Type'.downcase]
215
105
  headers['Content-Type'.downcase] = 'application/json'
216
106
  end
217
107
 
108
+ unless headers.keys.any? { |key| key.downcase == 'user-agent' }
109
+ headers['User-Agent'] = "ApiHammer #{ApiHammer::VERSION} hc"
110
+ end
111
+
218
112
  # OH LOOK IT'S FINALLY ACTUALLY CONNECTING TO SOMETHING
219
113
 
220
114
  response = connection.run_request(httpmethod.downcase.to_sym, url, body, headers)
@@ -0,0 +1,137 @@
1
+ require 'faraday'
2
+ require 'rack'
3
+
4
+ # outputs the response body to the given output device (defaulting to STDOUT)
5
+ class FaradayOutputter < Faraday::Middleware
6
+ def initialize(app, options={})
7
+ @app=app
8
+ @options = options
9
+ @outdev = @options[:outdev] || STDOUT
10
+ end
11
+
12
+ def call(request_env)
13
+ @app.call(request_env).on_complete do |response_env|
14
+ @outdev.puts(response_env[:body] || '')
15
+ end
16
+ end
17
+ end
18
+
19
+ # this is to approximate `curl -v`s output. but it's all faked, whereas curl gives you
20
+ # the real text written and read for request and response. whatever, close enough.
21
+ class FaradayCurlVOutputter < FaradayOutputter
22
+
23
+ # defines a method with the given name, applying coloring defined by any additional arguments.
24
+ # if @options[:color] is set, respects that; otherwise, applies color if the output device is a tty.
25
+ def self.color(name, *color_args)
26
+ define_method(name) do |arg|
27
+ if color?
28
+ require 'term/ansicolor'
29
+ color_args.inject(arg) do |result, color_arg|
30
+ Term::ANSIColor.send(color_arg, result)
31
+ end
32
+ else
33
+ arg
34
+ end
35
+ end
36
+ end
37
+
38
+ color :info, :intense_yellow
39
+ color :info_body, :yellow
40
+ color :protocol
41
+
42
+ color :request, :intense_cyan
43
+ color :request_verb, :bold
44
+ color :request_header
45
+ color :request_blankline, :intense_cyan, :bold
46
+
47
+ color :response, :intense_green
48
+ color :response_status, :bold, :green
49
+ color :response_header
50
+ color :response_blankline, :intense_green, :bold
51
+
52
+ def call(request_env)
53
+ @outdev.puts "#{info('*')} #{info_body("connect to #{request_env[:url].host} on port #{request_env[:url].port}")}"
54
+ @outdev.puts "#{info('*')} #{info_body("getting our SSL on")}" if request_env[:url].scheme=='https'
55
+ @outdev.puts "#{request('>')} #{request_verb(request_env[:method].to_s.upcase)} #{request_env[:url].request_uri} #{protocol("HTTP/#{Net::HTTP::HTTPVersion}")}"
56
+ request_env[:request_headers].each do |k, v|
57
+ @outdev.puts "#{request('>')} #{request_header(k)}#{request(':')} #{v}"
58
+ end
59
+ @outdev.puts "#{request_blankline('>')} "
60
+ request_body = alter_body_by_content_type(request_env[:body], request_env[:request_headers]['Content-Type'])
61
+ (request_body || '').split("\n", -1).each do |line|
62
+ @outdev.puts "#{request('>')} #{line}"
63
+ end
64
+ @app.call(request_env).on_complete do |response_env|
65
+ @outdev.puts "#{response('<')} #{protocol('HTTP/1.1' || 'or something - TODO')} #{response_status(response_env[:status].to_s)}"
66
+ request_env[:response_headers].each do |k, v|
67
+ @outdev.puts "#{response('<')} #{response_header(k)}#{response(':')} #{v}"
68
+ end
69
+ @outdev.puts "#{response_blankline ('<')} "
70
+ response_body = alter_body_by_content_type(response_env[:body], response_env[:response_headers]['Content-Type'])
71
+ (response_body || '').split("\n", -1).each do |line|
72
+ @outdev.puts "#{response('<')} #{line}"
73
+ end
74
+ end
75
+ end
76
+
77
+ def pretty?
78
+ @options[:pretty].nil? ? true : @options[:pretty]
79
+ end
80
+
81
+ # whether to use color
82
+ def color?
83
+ @options[:color].nil? ? @outdev.tty? : @options[:color]
84
+ end
85
+
86
+ # a mapping for each registered CodeRay scanner to the Media Types which represent
87
+ # that language. extremely incomplete!
88
+ CodeRayForMediaTypes = {
89
+ :c => [],
90
+ :cpp => [],
91
+ :clojure => [],
92
+ :css => ['text/css', 'application/css-stylesheet'],
93
+ :delphi => [],
94
+ :diff => [],
95
+ :erb => [],
96
+ :groovy => [],
97
+ :haml => [],
98
+ :html => ['text/html'],
99
+ :java => [],
100
+ :java_script => ['application/javascript', 'text/javascript', 'application/x-javascript'],
101
+ :json => ['application/json'],
102
+ :php => [],
103
+ :python => ['text/x-python'],
104
+ :ruby => [],
105
+ :sql => [],
106
+ :xml => ['text/xml', 'application/xml', %r(\Aapplication/.*\+xml\z)],
107
+ :yaml => [],
108
+ }
109
+
110
+ # takes a body and a content type; returns the body, altered according to options.
111
+ #
112
+ # - with coloring (ansi colors for terminals) possibly added, if it's a recognized content type and
113
+ # #color? is true
114
+ # - formatted prettily if #pretty? is true
115
+ def alter_body_by_content_type(body, content_type)
116
+ return body unless body.is_a?(String)
117
+ media_type = ::Rack::Request.new({'CONTENT_TYPE' => content_type}).media_type
118
+ if pretty?
119
+ case media_type
120
+ when 'application/json'
121
+ require 'json'
122
+ begin
123
+ body = JSON.pretty_generate(JSON.parse(body))
124
+ rescue JSON::ParserError
125
+ end
126
+ end
127
+ end
128
+ if color?
129
+ coderay_scanner = CodeRayForMediaTypes.reject{|k,v| !v.any?{|type| type === media_type} }.keys.first
130
+ if coderay_scanner
131
+ require 'coderay'
132
+ body = CodeRay.scan(body, coderay_scanner).encode(:terminal)
133
+ end
134
+ end
135
+ body
136
+ end
137
+ end
@@ -1,5 +1,6 @@
1
1
  require 'api_hammer/halt'
2
2
  require 'api_hammer/check_required_params'
3
+ require 'api_hammer/unmunged_request_params'
3
4
 
4
5
  module ApiHammer::Rails
5
6
  def self.included(klass)
@@ -0,0 +1,59 @@
1
+ require 'api_hammer'
2
+ require 'active_support/log_subscriber'
3
+
4
+ require 'rails/rack/log_tailer'
5
+ # fix up this class to tail the log when the body is closed rather than when its own #call is done.
6
+ module Rails
7
+ module Rack
8
+ class LogTailer
9
+ def call(env)
10
+ status, headers, body = @app.call(env)
11
+ body_proxy = ::Rack::BodyProxy.new(body) { tail! }
12
+ [status, headers, body_proxy]
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module ApiHammer
19
+ class RequestLogSubscriber < ActiveSupport::LogSubscriber
20
+ def process_action(event)
21
+ if event.payload[:request_env]
22
+ info = (event.payload[:request_env]['request_logger.info'] ||= {})
23
+ else
24
+ # if an exception occurs in the action, append_info_to_payload isn't called and
25
+ # event.payload[:request_env] doesn't get set. fall back to use Thread.current.
26
+ info = (Thread.current['request_logger.info'] ||= {})
27
+ end
28
+ info.update(event.payload.slice(:controller, :action, :exception, :format, :formats, :view_runtime, :db_runtime))
29
+ info.update(:transaction_id => event.transaction_id)
30
+ info.update(event.payload['request_logger.info']) if event.payload['request_logger.info']
31
+ end
32
+ end
33
+ end
34
+
35
+ module AddRequestToPayload
36
+ def append_info_to_payload(payload)
37
+ super
38
+ payload[:request_env] = request.env
39
+ end
40
+ end
41
+
42
+ module ApiHammer
43
+ class RailsRequestLogging < ::Rails::Railtie
44
+ initializer :api_hammer_request_logging do |app|
45
+ # use the bits we want from Lograge.setup, disabling existing active* log things.
46
+ # but don't actually enable lograge.
47
+ require 'lograge'
48
+ require 'lograge/rails_ext/rack/logger'
49
+ app.config.action_dispatch.rack_cache[:verbose] = false if app.config.action_dispatch.rack_cache
50
+ Lograge.remove_existing_log_subscriptions
51
+
52
+ ApiHammer::RequestLogSubscriber.attach_to :action_controller
53
+
54
+ app.config.middleware.insert_after(::Rails::Rack::Logger, ApiHammer::RequestLogger, ::Rails.logger)
55
+
56
+ ActionController::Base.send(:include, AddRequestToPayload)
57
+ end
58
+ end
59
+ end
@@ -23,10 +23,13 @@ module ApiHammer
23
23
  @request_body = env["rack.input"].read
24
24
  env["rack.input"].rewind
25
25
 
26
- status, header, body = @app.call(env)
27
- header = ::Rack::Utils::HeaderHash.new(header)
28
- body_proxy = ::Rack::BodyProxy.new(body) { log(env, status, header, began_at, body) }
29
- [status, header, body_proxy]
26
+ log_tags = Thread.current[:activesupport_tagged_logging_tags]
27
+ @log_tags = log_tags.dup if log_tags
28
+
29
+ status, headers, body = @app.call(env)
30
+ headers = ::Rack::Utils::HeaderHash.new(headers)
31
+ body_proxy = ::Rack::BodyProxy.new(body) { log(env, status, headers, began_at, body) }
32
+ [status, headers, body_proxy]
30
33
  end
31
34
 
32
35
  def log(env, status, headers, began_at, body)
@@ -53,7 +56,6 @@ module ApiHammer
53
56
  :white
54
57
  end
55
58
  status_s = bold(send(status_color, status.to_s))
56
- @logger.info "#{status_s} : #{bold(intense_cyan(request.request_method))} #{intense_cyan(request_uri.normalize)}"
57
59
  data = {
58
60
  'request' => {
59
61
  'method' => request.request_method,
@@ -63,6 +65,12 @@ module ApiHammer
63
65
  'remote_addr' => env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"],
64
66
  'User-Agent' => request.user_agent,
65
67
  'body' => @request_body,
68
+ # these come from the OAuthenticator gem/middleware
69
+ 'oauth.authenticated' => env['oauth.authenticated'],
70
+ 'oauth.consumer_key' => env['oauth.consumer_key'],
71
+ 'oauth.token' => env['oauth.token'],
72
+ # airbrake
73
+ 'airbrake.error_id' => env['airbrake.error_id'],
66
74
  }.reject{|k,v| v.nil? },
67
75
  'response' => {
68
76
  'status' => status,
@@ -70,12 +78,24 @@ module ApiHammer
70
78
  'Location' => response.location,
71
79
  'Content-Type' => response.content_type,
72
80
  }.reject{|k,v| v.nil? },
73
- 'began_at' => began_at.utc.to_i,
74
- 'duration' => now - began_at,
81
+ 'processing' => {
82
+ 'began_at' => began_at.utc.to_i,
83
+ 'duration' => now - began_at,
84
+ 'activesupport_tagged_logging_tags' => @log_tags,
85
+ }.merge(env['request_logger.info'] || {}).merge(Thread.current['request_logger.info'] || {}).reject{|k,v| v.nil? },
75
86
  }
87
+ Thread.current['request_logger.info'] = nil
76
88
  json_data = JSON.dump(data)
77
- @logger.debug json_data
78
- $ZMQ_LOGGER.log json_data if defined?($ZMQ_LOGGER)
89
+ dolog = proc do
90
+ @logger.info "#{status_s} : #{bold(intense_cyan(request.request_method))} #{intense_cyan(request_uri.normalize)}"
91
+ @logger.info json_data
92
+ end
93
+ # do the logging with tags that applied to the request if appropriate
94
+ if @logger.respond_to?(:tagged) && @log_tags
95
+ @logger.tagged(@log_tags, &dolog)
96
+ else
97
+ dolog.call
98
+ end
79
99
  end
80
100
  end
81
101
  end
@@ -0,0 +1,26 @@
1
+ module ApiHammer::Rails
2
+ # request parameters (not query parameters) without the nil/empty array munging that rails does
3
+ def unmunged_request_params
4
+ # Thread.exclusive is not optimal but we need to ensure that any other params parsing occurring in other
5
+ # threads is not affected by disabling munging
6
+ #
7
+ # TODO when we are on a rails which has ActionDispatch::Request::Utils.perform_deep_munge, use that instead
8
+ # of clobbering methods
9
+ @unmunged_params ||= Thread.exclusive do
10
+ unless ActionDispatch::Request.method_defined?(:real_deep_munge)
11
+ ActionDispatch::Request.send(:alias_method, :real_deep_munge, :deep_munge)
12
+ end
13
+ ActionDispatch::Request.send(:define_method, :deep_munge) { |hash| hash }
14
+ begin
15
+ unmunged_params = nil
16
+ newenv = request.env.merge('action_dispatch.request.request_parameters' => nil)
17
+ ActionDispatch::ParamsParser.new(proc do |env|
18
+ unmunged_params = env['action_dispatch.request.request_parameters']
19
+ end).call(newenv)
20
+ unmunged_params || ActionDispatch::Request.new(newenv).request_parameters
21
+ ensure
22
+ ActionDispatch::Request.send(:alias_method, :deep_munge, :real_deep_munge)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module ApiHammer
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_hammer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-08 00:00:00.000000000 Z
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: term-ansicolor
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -168,15 +182,18 @@ files:
168
182
  - bin/hc
169
183
  - lib/api_hammer.rb
170
184
  - lib/api_hammer/check_required_params.rb
185
+ - lib/api_hammer/faraday/outputter.rb
171
186
  - lib/api_hammer/halt.rb
172
187
  - lib/api_hammer/public_instance_exec.rb
173
188
  - lib/api_hammer/rails.rb
189
+ - lib/api_hammer/rails_request_logging.rb
174
190
  - lib/api_hammer/request_logger.rb
175
191
  - lib/api_hammer/show_text_exceptions.rb
176
192
  - lib/api_hammer/tasks.rb
177
193
  - lib/api_hammer/tasks/cucumber_pretty.rb
178
194
  - lib/api_hammer/tasks/gem_available_updates.rb
179
195
  - lib/api_hammer/trailing_newline.rb
196
+ - lib/api_hammer/unmunged_request_params.rb
180
197
  - lib/api_hammer/version.rb
181
198
  - lib/api_hammer/weblink.rb
182
199
  - test/check_required_params_test.rb