rory 0.6.1 → 0.7.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: df765583fa4f315b7f217e8324523d7dd4b3c2ba
4
- data.tar.gz: f977ad97a93566d606319301ea03a06447c2a260
3
+ metadata.gz: 87f7aad3ae233e7193f17c7766db1cc3c551793b
4
+ data.tar.gz: 8c5ac24faba7ab853b087af9dbea31100d966fc8
5
5
  SHA512:
6
- metadata.gz: 3c2dd88f139db2931ebe7fdb9153c6ca4180d65f36622fdc51e18859ae55f6661636fe5c5b525564b9ac6973f9980a2e6266b8508317579f87bce4218b68bf83
7
- data.tar.gz: 152c81f7ab8498553a4d4e1dce0d583c25d29bd882c8c776178e8b73815b3a3f9a08e2ab3e2300b8c95f50d0b2efc034348522125a0089efac00574ebc5c8a4f
6
+ metadata.gz: df8251d088994de0b6333c6ac67daa1c260aa69e7f8aee39f2b59a8234b68cfb096b301664d0c6440e0cf32173160cd5ba4ecd5ee7482d80a3613190148c21c1
7
+ data.tar.gz: 3146ba610b2b6e80178f417bbde883acfdb9a560f57aa85157a2db819d8140137fa8431299bc0e421ae05cf498788896f49d4738a0578de1677c5fe3f09f5c8d
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Gem Version](https://badge.fury.io/rb/rory.svg)](http://badge.fury.io/rb/rory)
1
2
  rory
2
3
  ====
3
4
 
data/lib/rory.rb CHANGED
@@ -2,6 +2,7 @@ ENV['RORY_ENV'] ||= ENV['RACK_ENV'] || 'development'
2
2
 
3
3
  require 'yaml'
4
4
  require 'sequel'
5
+ require 'thread/inheritable_attributes'
5
6
  require 'rack/contrib'
6
7
  require 'rory/application'
7
8
  require 'rory/dispatcher'
@@ -1,5 +1,6 @@
1
1
  require 'pathname'
2
- require 'logger'
2
+ require 'rory/logger'
3
+ require 'rory/request_id'
3
4
  require 'rory/route_mapper'
4
5
  require 'rack/commonlogger'
5
6
  require_relative 'request_parameter_logger'
@@ -39,7 +40,7 @@ module Rory
39
40
  end
40
41
 
41
42
  def root=(root_path)
42
- @root = Pathname.new(root_path).expand_path
43
+ $:.unshift @root = Pathname.new(root_path).realpath
43
44
  end
44
45
  end
45
46
 
@@ -137,6 +138,7 @@ module Rory
137
138
 
138
139
  def use_default_middleware
139
140
  if request_logging_on?
141
+ use_middleware Rory::RequestId, :uuid_prefix => self.class.name
140
142
  use_middleware Rack::PostBodyContentTypeParser
141
143
  use_middleware Rack::CommonLogger, logger
142
144
  use_middleware Rory::RequestParameterLogger, logger, :filters => parameters_to_filter
@@ -11,7 +11,7 @@ module Rory
11
11
 
12
12
  def self.rack_app(app)
13
13
  Proc.new { |env|
14
- new(Rack::Request.new(env), app).dispatch
14
+ new(Rory::Request.new(env), app).dispatch
15
15
  }
16
16
  end
17
17
 
@@ -0,0 +1,46 @@
1
+ require "rory/request"
2
+ require "logger"
3
+
4
+ module Rory
5
+ class Logger < ::Logger
6
+ def initialize(io, options={})
7
+ super(io)
8
+ @default_formatter = Formatter.new
9
+ @tagged = options.fetch(:tagged, [:request_id])
10
+ end
11
+
12
+ def <<(msg)
13
+ super([tagged, msg].reject(&:empty?).join(" "))
14
+ end
15
+
16
+ alias_method :write, :<<
17
+
18
+ def request_id
19
+ Thread.current.get_inheritable_attribute(:rory_request_id)
20
+ end
21
+
22
+ def tagged
23
+ @tagged.map do |key|
24
+ "#{key}=#{quoted_string(public_send(key))}"
25
+ end.join(" ").rstrip
26
+ end
27
+
28
+ private
29
+
30
+ def quoted_string(str)
31
+ str =~ /\s/ ? %["#{str}"] : str
32
+ end
33
+
34
+ def format_message(severity, datetime, progname, msg)
35
+ (@formatter || @default_formatter).call(severity, datetime, progname, msg, tagged)
36
+ end
37
+
38
+ class Formatter < ::Logger::Formatter
39
+ FORMAT = "%s, [%s - %s#%d] %5s -- %s: %s\n"
40
+
41
+ def call(severity, time, progname, msg, tagged)
42
+ FORMAT % [severity[0..0], tagged, format_datetime(time), $$, severity, progname, msg2str(msg)]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Rory
2
+ class Request < ::Rack::Request
3
+ # Returns the unique request id, which is based off either the X-Request-Id header that can
4
+ # be generated by a firewall, load balancer, or web server or by the RequestId middleware
5
+ # (which sets the rory.request_id environment variable).
6
+ #
7
+ # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
8
+ # This relies on the rack variable set by the Rory::RequestId middleware.
9
+ def uuid
10
+ @uuid ||= env["rory.request_id"]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ module Rory
2
+ # Makes a unique request id available to the rory.request_id env variable (which is then accessible through
3
+ # Rory::Request#uuid) and sends the same id to the client via the X-Request-Id header.
4
+ #
5
+ # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
6
+ # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
7
+ # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
8
+ #
9
+ # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
10
+ # from multiple pieces of the stack.
11
+ class RequestId
12
+ def initialize(app, options={})
13
+ @app = app
14
+ @uuid_creator = options.fetch(:uuid_creator, SecureRandom)
15
+ @uuid_prefix = options[:uuid_prefix]
16
+ end
17
+
18
+ def call(env)
19
+ env["rory.request_id"] = external_request_id(env) || internal_request_id
20
+ Thread.current.set_inheritable_attribute(:rory_request_id, env["rory.request_id"])
21
+ @app.call(env).tap { |_status, headers, _body| headers["X-Request-Id"] = env["rory.request_id"] }
22
+ end
23
+
24
+ private
25
+
26
+ def external_request_id(env)
27
+ if (request_id = env["HTTP_X_REQUEST_ID"])
28
+ request_id.gsub(/[^\w\-]/, "")[0..254]
29
+ end
30
+ end
31
+
32
+ def internal_request_id
33
+ "#{uuid_prefix}#{@uuid_creator.uuid}"
34
+ end
35
+
36
+ def uuid_prefix
37
+ @uuid_prefix ? "#{Support.tokenize(@uuid_prefix)}-" : ""
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,4 @@
1
1
  require_relative 'parameter_filter'
2
-
3
2
  module Rory
4
3
  class RequestParameterLogger
5
4
 
data/lib/rory/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rory
2
- VERSION = '0.6.1'
2
+ VERSION = '0.7.0'
3
3
  end
data/rory.gemspec CHANGED
@@ -32,14 +32,16 @@ EOF
32
32
  s.add_runtime_dependency 'rack-contrib', '~> 1.2'
33
33
  s.add_runtime_dependency 'sequel', '~> 4.0'
34
34
  s.add_runtime_dependency 'thin', '~> 1.0'
35
+ s.add_runtime_dependency 'thread-inheritable_attributes', '~> 0.1'
35
36
 
36
37
  s.add_development_dependency 'rake', '~> 10.4'
37
38
  s.add_development_dependency 'rspec', '~> 3'
39
+ s.add_development_dependency 'mime-types', '~> 2.6'
38
40
  s.add_development_dependency 'capybara', '~> 2.4'
39
41
  s.add_development_dependency 'yard', '~> 0.8'
40
42
  s.add_development_dependency 'reek', '~> 2.2'
41
43
  s.add_development_dependency 'simplecov', '~> 0.10'
42
- s.add_development_dependency 'bundler', '~> 1.0'
44
+ s.add_development_dependency 'bundler', '~> 1.10'
43
45
  s.add_development_dependency 'pry', '~> 0.10'
44
46
  end
45
47
 
@@ -1,11 +1,38 @@
1
1
  describe Rory::Application do
2
2
  let(:subject) {
3
- Class.new(Rory::Application).tap { |app|
4
- app.root = "whatever"
3
+ Object.const_set(test_rory_app_name, Class.new(Rory::Application).tap { |app|
4
+ app.root = root
5
5
  app.turn_off_request_logging!
6
- }
6
+ })
7
+ Object.const_get(test_rory_app_name)
7
8
  }
8
9
 
10
+ let(:test_rory_app_name){
11
+ "TestRory#{('a'..'z').to_a.sample(5).join}"
12
+ }
13
+ let(:root){"spec/fixture_app"}
14
+
15
+ describe ".root=" do
16
+ let(:root) { "current_app" }
17
+ before { `ln -s spec/fixture_app current_app` }
18
+ after { `rm current_app` }
19
+
20
+ it 'appends to the load path' do
21
+ expect($:).to receive(:unshift).with(Pathname("current_app").realpath)
22
+ subject
23
+ end
24
+ end
25
+
26
+ describe ".root" do
27
+ let(:root) { "current_app" }
28
+ before { `ln -s spec/fixture_app current_app` }
29
+ after { `rm current_app` }
30
+
31
+ it "returns the realpath" do
32
+ expect(subject.root.to_s).to match(/spec\/fixture_app/)
33
+ end
34
+ end
35
+
9
36
  describe ".configure" do
10
37
  it 'yields the given block to self' do
11
38
  subject.configure do |c|
@@ -156,6 +183,7 @@ describe Rory::Application do
156
183
  allow(subject.instance).to receive(:request_logging_on?).and_return(true)
157
184
  allow(subject.instance).to receive(:parameters_to_filter).and_return([:horses])
158
185
  allow(subject.instance).to receive(:logger).and_return(:the_logger)
186
+ expect(subject.instance).to receive(:use_middleware).with(Rory::RequestId, :uuid_prefix => test_rory_app_name)
159
187
  expect(subject.instance).to receive(:use_middleware).with(Rack::PostBodyContentTypeParser)
160
188
  expect(subject.instance).to receive(:use_middleware).with(Rack::CommonLogger, :the_logger)
161
189
  expect(subject.instance).to receive(:use_middleware).with(Rory::RequestParameterLogger, :the_logger, :filters => [:horses])
@@ -263,4 +291,4 @@ describe Rory::Application do
263
291
  end
264
292
  end
265
293
  end
266
- end
294
+ end
@@ -0,0 +1,90 @@
1
+ require "logger"
2
+ require "thread/inheritable_attributes"
3
+ require "rory/logger"
4
+
5
+ describe Rory::Logger do
6
+ subject { described_class.new(string_io) }
7
+ let(:string_io) { StringIO.new }
8
+ let(:result) { string_io.tap(&:rewind).read }
9
+
10
+ let(:simple_format) {
11
+ subject.formatter = Proc.new do |severity, _, _, msg, tagged|
12
+ "#{severity} #{tagged} - #{msg}"
13
+ end
14
+ }
15
+
16
+ let(:rory_request_id) { "1111-2222" }
17
+
18
+ before { Thread.current[:inheritable_attributes] = {:rory_request_id => rory_request_id} }
19
+ after { Thread.current[:inheritable_attributes] = nil }
20
+
21
+ context "when tagged is empty" do
22
+ subject { described_class.new(string_io, tagged: []) }
23
+ it "does not tag anything" do
24
+ simple_format
25
+ subject.<< "Hello"
26
+ expect(result).to eq "Hello"
27
+ end
28
+ end
29
+
30
+ context "creating custom tags" do
31
+ subject { described_class.new(string_io, tagged: [:custom_tag, :request_id]) }
32
+ it "needs an instance method go along with new tag" do
33
+ def subject.custom_tag
34
+ "Words.."
35
+ end
36
+ simple_format
37
+ subject.<< "Hello"
38
+ expect(result).to eq "custom_tag=Words.. request_id=1111-2222 Hello"
39
+ end
40
+ end
41
+
42
+ describe "#<<" do
43
+ it "tags are present with this form" do
44
+ simple_format
45
+ subject.<< "Hello"
46
+ expect(result).to eq "request_id=1111-2222 Hello"
47
+ end
48
+ end
49
+
50
+ context "when a tagged values has spaces" do
51
+ let(:rory_request_id) { "1111 2222" }
52
+ it "is quoted" do
53
+ simple_format
54
+ subject.<< "Good Morning"
55
+ expect(result).to eq 'request_id="1111 2222" Good Morning'
56
+ end
57
+ end
58
+
59
+ describe "#info" do
60
+ it "severity level is set to INFO" do
61
+ simple_format
62
+ subject.info "Hello"
63
+ expect(result).to eq "INFO request_id=1111-2222 - Hello"
64
+ end
65
+ end
66
+
67
+ describe "#formatter" do
68
+ it "define a custom formatting" do
69
+ subject.formatter = Proc.new do |_, _, _, msg, tagged|
70
+ "This is formatted: #{tagged} - #{msg}"
71
+ end
72
+ subject.info "Hello"
73
+ expect(result).to eq "This is formatted: request_id=1111-2222 - Hello"
74
+ end
75
+
76
+ it "has default formatting" do
77
+ subject.info "Goodbye"
78
+ expect(result).to match /request_id=1111-2222.*INFO -- : Goodbye\n/
79
+ end
80
+ end
81
+
82
+ describe "integration with Rack::CommonLogger" do
83
+ it "only prepends tags" do
84
+ [200, { "REMOTE_ADDR" => "127.0.0.1", "HTTP_VERSION" => "1.1" }, ""]
85
+ Rack::CommonLogger.new(Proc.new { |a| a }, subject).send(:log, { "REMOTE_ADDR" => "127.0.0.1", "HTTP_VERSION" => "1.1", Rack::QUERY_STRING => "abc" }, 200, {}, 1)
86
+ "I, [1111-2222 - 2016-01-20T16:30:52.193516 #5341] INFO -- : 127.0.0.1 - - [20/Jan/2016:16:30:52 -0800] \" ?abc 1.1\" 200 - 1453336251.1934\n\n"
87
+ expect(result).to match /request_id=1111-2222 127.0.0.1 - - /
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,56 @@
1
+ RSpec.describe Rory::RequestId do
2
+ subject { described_class.new(Proc.new {|env|[200, headers, ""] },
3
+ uuid_prefix: uuid_prefix,
4
+ uuid_creator: class_double(SecureRandom, uuid: "1234")) }
5
+ after { Thread.current[:inheritable_attributes] = nil }
6
+ let(:headers) { {} }
7
+ let(:env) { {} }
8
+ let(:uuid_prefix) { nil }
9
+
10
+ context "when no external_request_id is set" do
11
+ before { subject.call(env) }
12
+
13
+ it "sets env['rory.request_id']" do
14
+ expect(env["rory.request_id"]).to eq "1234"
15
+ end
16
+
17
+ it "sets header['X-Request-Id']" do
18
+ expect(headers["X-Request-Id"]).to eq "1234"
19
+ end
20
+
21
+ it "sets Thread.current[:rory_request_id]" do
22
+ expect(Thread.current.get_inheritable_attribute(:rory_request_id)).to eq "1234"
23
+ end
24
+
25
+ context "the uuid can be given a prefixed to know where it was created" do
26
+ let(:uuid_prefix) { "AppName" }
27
+ it { expect(Thread.current.get_inheritable_attribute(:rory_request_id)).to eq "app_name-1234" }
28
+ end
29
+ end
30
+
31
+ context "when external_request_id is set" do
32
+ before { subject.call(env) }
33
+ let(:env) { { "HTTP_X_REQUEST_ID" => "4321" } }
34
+
35
+ it "sets env['rory.request_id']" do
36
+ expect(env["rory.request_id"]).to eq "4321"
37
+ end
38
+
39
+ it "sets header['X-Request-Id']" do
40
+ expect(headers["X-Request-Id"]).to eq "4321"
41
+ end
42
+
43
+ it "sets Thread.current[:rory_request_id]" do
44
+ expect(Thread.current.get_inheritable_attribute(:rory_request_id)).to eq "4321"
45
+ end
46
+ end
47
+
48
+ context "use default SecureRandom" do
49
+ subject { described_class.new(Proc.new {|env|[200, headers, ""] },
50
+ uuid_prefix: uuid_prefix).call({}) }
51
+ it "call uuid on SecureRandom" do
52
+ expect(SecureRandom).to receive(:uuid).once
53
+ subject
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe Rory::Request do
2
+ describe "#uuid" do
3
+ it "returns the value set by env['rory.request_id']" do
4
+ env = { "rory.request_id" => "uuid-from_rory_request" }
5
+ expect(described_class.new(env).uuid).to eq "uuid-from_rory_request"
6
+ end
7
+
8
+ context "when no key exists" do
9
+ it "returns nil" do
10
+ expect(described_class.new({}).uuid).to eq nil
11
+ end
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ravi Gadad
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-06-18 00:00:00.000000000 Z
13
+ date: 2016-01-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -68,6 +68,20 @@ dependencies:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
70
  version: '1.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: thread-inheritable_attributes
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '0.1'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '0.1'
71
85
  - !ruby/object:Gem::Dependency
72
86
  name: rake
73
87
  requirement: !ruby/object:Gem::Requirement
@@ -96,6 +110,20 @@ dependencies:
96
110
  - - "~>"
97
111
  - !ruby/object:Gem::Version
98
112
  version: '3'
113
+ - !ruby/object:Gem::Dependency
114
+ name: mime-types
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '2.6'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '2.6'
99
127
  - !ruby/object:Gem::Dependency
100
128
  name: capybara
101
129
  requirement: !ruby/object:Gem::Requirement
@@ -158,14 +186,14 @@ dependencies:
158
186
  requirements:
159
187
  - - "~>"
160
188
  - !ruby/object:Gem::Version
161
- version: '1.0'
189
+ version: '1.10'
162
190
  type: :development
163
191
  prerelease: false
164
192
  version_requirements: !ruby/object:Gem::Requirement
165
193
  requirements:
166
194
  - - "~>"
167
195
  - !ruby/object:Gem::Version
168
- version: '1.0'
196
+ version: '1.10'
169
197
  - !ruby/object:Gem::Dependency
170
198
  name: pry
171
199
  requirement: !ruby/object:Gem::Requirement
@@ -201,10 +229,13 @@ files:
201
229
  - lib/rory/application.rb
202
230
  - lib/rory/controller.rb
203
231
  - lib/rory/dispatcher.rb
232
+ - lib/rory/logger.rb
204
233
  - lib/rory/parameter_filter.rb
205
234
  - lib/rory/path_generation.rb
206
235
  - lib/rory/renderer.rb
207
236
  - lib/rory/renderer/context.rb
237
+ - lib/rory/request.rb
238
+ - lib/rory/request_id.rb
208
239
  - lib/rory/request_parameter_logger.rb
209
240
  - lib/rory/route.rb
210
241
  - lib/rory/route_mapper.rb
@@ -237,10 +268,13 @@ files:
237
268
  - spec/lib/rory/application_spec.rb
238
269
  - spec/lib/rory/controller_spec.rb
239
270
  - spec/lib/rory/dispatcher_spec.rb
271
+ - spec/lib/rory/logger_spec.rb
240
272
  - spec/lib/rory/parameter_filter_spec.rb
241
273
  - spec/lib/rory/renderer/context_spec.rb
242
274
  - spec/lib/rory/renderer_spec.rb
275
+ - spec/lib/rory/request_id_spec.rb
243
276
  - spec/lib/rory/request_parameter_logger_spec.rb
277
+ - spec/lib/rory/request_spec.rb
244
278
  - spec/lib/rory/route_spec.rb
245
279
  - spec/lib/rory/support_spec.rb
246
280
  - spec/lib/rory_spec.rb
@@ -267,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
301
  version: 1.3.6
268
302
  requirements: []
269
303
  rubyforge_project:
270
- rubygems_version: 2.2.2
304
+ rubygems_version: 2.2.5
271
305
  signing_key:
272
306
  specification_version: 4
273
307
  summary: Another Ruby web framework. Just what the world needs.