lookout-rack-utils 1.2.0

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmNkMzhmMTMyZTI2N2FiODZhMDk0ZDMzYTM1NjA0Yzc4ZDg5MWRlYQ==
5
+ data.tar.gz: !binary |-
6
+ OGYzNThmOTViMWY1NmZjNWEwZjEzMDhjY2MxNTcxYmMyYjFkNDA3Yg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MjU0Y2FlMjBhZTYxOTg2Zjg1M2Y4ODQxZjQ5ODczMzk2ZjVkOTk0NWU0MWRk
10
+ ODFlZmM1OGYwYThmZmRlMDM1N2QyODM0OGVhZWEyOTUxZjBiZmU0OGEzYjEy
11
+ YzllNmY0YzI4OThmODRmZDI3MzQ1MzI4YzQ3MWJjMzIwYWFlNDg=
12
+ data.tar.gz: !binary |-
13
+ OGI4N2E4ZGY5ZTliYTQ1Mzg3Y2I2NWYzZjE0ZGFjZmQ4Yjg5Njg3YTcyNDhk
14
+ N2EwODY4YTllNzFhOThjNjQwZjE1NWNmMzQ4ZmVkM2QyNTI3MmUzYmM1OWNm
15
+ NDk5ZTBkNTE1MTlhZGM1YmNhNGRlMWUwMTRkNzI3YWRjMzVjMWQ=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lookout-rack-utils.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lookout, Inc
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Lookout::Rack::Utils
2
+
3
+ Assorted Rack utils.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'lookout-rack-utils'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install lookout-rack-utils
18
+
19
+ ## Usage
20
+ Require pieces of this in your project as needed; details below.
21
+
22
+ ### Lookout::Rack::Utils::Graphite
23
+ You'll need configatron set up:
24
+
25
+ ```ruby
26
+ configatron.statsd do |s|
27
+ s.prefix = 'app'
28
+ s.host = 'localhost'
29
+ s.port = 8125
30
+ end
31
+ ```
32
+ ### Lookout::Rack::Utils::I18n
33
+ You'll need configatron set up:
34
+
35
+ ```ruby
36
+ configatron.default_locale = :en
37
+ configatron.locales = [:en]
38
+ ```
39
+
40
+ You'll also need to set the load path somewhere in your
41
+ app:
42
+ ```ruby
43
+ I18n.load_path = Dir["./config/locales/**/*.yml"]
44
+ ```
45
+
46
+ Note that we expect `t(*args)` to be called in the context of a request.
47
+
48
+ ### Lookout::Rack::Utils::Log
49
+ You'll need configatron set up. If `Lookout::Rack::Utils::Graphite` is
50
+ present, it will increment those stats whenever a log is written.
51
+
52
+ ```ruby
53
+ configatron.project_name = 'My project name'
54
+ configatron.logging do |l|
55
+ l.enabled = true
56
+ l.level = 'WARN'
57
+ l.file = 'log/some_file.log'
58
+ end
59
+ ```
60
+
61
+ ### Lookout::Rack::Utils::Request
62
+ `Lookout::Rack::Utils::Request` will log errors using
63
+ `Lookout::Rack::Utils::Log` if it has been required elsewhere.
64
+
65
+ ### Lookout::Rack::Utils::Subroute
66
+ `subroute!(relative_path)` will fire off a request to the specified relative
67
+ path; the result can then be used, or just returned immediately to the browser.
68
+
69
+ Examples (Used in a Sinatra application):
70
+ ```ruby
71
+ require 'lookout/rack/utils/subroute'
72
+ include Lookout::Rack::Utils::Subroute
73
+
74
+ # Return the status of a request to /api/public/v1/original_route and throw
75
+ # away the response body
76
+ get '/status' do
77
+ sub_status, sub_data = subroute!('/api/public/v1/original_route')
78
+
79
+ halt sub_status
80
+ end
81
+
82
+ # Assuming we have a method 'current_user', make /api/public/v1/user route
83
+ # to the current user
84
+ get '/api/public/v1/user' do
85
+ subroute!("/api/public/v1/user/#{current_user.id}")
86
+ end
87
+
88
+ # Same as above, but a POST request - the verb and all other parts of the
89
+ # request's env will be preserved
90
+ post '/api/public/v1/user' do
91
+ subroute!("/api/public/v1/user/#{current_user.id}")
92
+ end
93
+ ```
94
+
95
+ ## Contributing
96
+
97
+ 1. Fork it
98
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
99
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
100
+ 4. Push to the branch (`git push origin my-new-feature`)
101
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = '--fail-fast --color --order random'
6
+ end
7
+
8
+ task :test => :spec
9
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ require "lookout/rack/utils/version"
2
+
3
+ module Lookout
4
+ module Rack
5
+ module Utils
6
+ end
7
+ end
8
+ end
9
+
10
+ if RUBY_PLATFORM == 'java'
11
+ # This is required because YAML is messed up and incompatible with our version
12
+ # of Configatron under JRuby
13
+ module Psych
14
+ module Yecht
15
+ MergeKey = nil
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'lookout/rack/utils/graphite'
21
+ require 'lookout/rack/utils/i18n'
22
+ require 'lookout/rack/utils/log'
23
+ require 'lookout/rack/utils/request'
24
+ require 'lookout/rack/utils/subroute'
@@ -0,0 +1,35 @@
1
+ require 'singleton'
2
+ require 'configatron'
3
+ require 'statsd'
4
+
5
+ module Lookout::Rack::Utils
6
+ # Statsd proxy. This class initializes the Statsd client and
7
+ # delegates all stats related calls to it.
8
+ #
9
+ # Use as:
10
+ # Lookout::Rack::Utils::Graphite.increment('device.associated')
11
+ # Lookout::Rack::Utils::Graphite.update_counter('device.associated', 5)
12
+ #
13
+ class Graphite
14
+ include Singleton
15
+
16
+ def initialize
17
+ prefix = configatron.statsd.prefix
18
+ unless ENV['RACK_ENV'] == 'production'
19
+ prefix = "dev.#{ENV['USER']}.#{prefix}"
20
+ end
21
+
22
+ Statsd.create_instance(:host => configatron.statsd.host,
23
+ :port => configatron.statsd.port,
24
+ :prefix => prefix)
25
+ end
26
+
27
+ def self.method_missing(meth, *args, &block)
28
+ self.instance && Statsd.instance.send(meth, *args)
29
+ end
30
+
31
+ def self.respond_to?(method, include_private = false)
32
+ super || (self.instance && Statsd.instance.respond_to?(method, include_private))
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ require 'i18n'
2
+ require 'configatron'
3
+
4
+ module Lookout::Rack::Utils
5
+ module I18n
6
+ ::I18n.default_locale = configatron.default_locale
7
+
8
+ def t(*args)
9
+ ::I18n.t(*args)
10
+ end
11
+
12
+ def current_locale
13
+ return @locale unless @locale.nil?
14
+
15
+ accepted_languages.each do |lang, quality|
16
+ if configatron.locales.include?(lang)
17
+ @locale = lang
18
+ return @locale
19
+ end
20
+ end
21
+
22
+ # Just fallback to what we have set for the default
23
+ @locale = configatron.default_locale
24
+ return @locale
25
+ end
26
+
27
+ # We expect this to be called in a Rack request, but it will default to
28
+ # returning [] if not.
29
+ def accepted_languages
30
+ accepted = defined?(request.env) && request.env['HTTP_ACCEPT_LANGUAGE']
31
+ return [] if accepted.nil?
32
+
33
+ # parse Accept-Language
34
+ accepted = accepted.split(',')
35
+ accepted = accepted.map { |l| l.strip.split(";") }
36
+ accepted = accepted.map { |l|
37
+ # en-US -> :en
38
+ lang = l[0].split('-').first.downcase.to_sym
39
+
40
+ if (l.size == 2)
41
+ # quality present
42
+ [lang, l[1].sub(/^q=/, "").to_f ]
43
+ else
44
+ # no quality specified => quality == 1
45
+ [ lang, 1.0 ]
46
+ end
47
+ }
48
+ # sort by quality
49
+ accepted.sort { |left, right| right[1] <=> left[1] }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+ require 'log4r'
3
+ require 'singleton'
4
+ require 'rack/requestash/log4r'
5
+ require 'time'
6
+ require 'configatron'
7
+
8
+ module Lookout::Rack::Utils
9
+ # Logging. Logs to log/<project_name>.log with the format:
10
+ #
11
+ # [Log Level]: [Timestamp (ISO-8601)]: [File:linenum]: [Log Message]
12
+ #
13
+ # Use through the helper:
14
+ # log.warn 'This is my log message'
15
+ #
16
+ class Log
17
+ include Singleton
18
+ include Log4r
19
+
20
+ # Formatter that include the filename and relative path, and line number in
21
+ # output of the caller.
22
+ #
23
+ # Since all callers go through the methods defined in this class to log, we
24
+ # look at the second line of the tracer output, removing everything but the
25
+ # directories after the project directory.
26
+ #
27
+ class LookoutFormatter < Log4r::Formatter
28
+ # Return the project base directory for filtering to help with
29
+ # identifiying the filename and line number when formatting the log
30
+ # message
31
+ #
32
+ # @return [String] Base directory for the project
33
+ def basedir
34
+ @basedir ||= File.expand_path(File.join(File.dirname(__FILE__), ".."))
35
+ end
36
+
37
+ # Return a trimmed version of the filename from where a LogEvent occurred
38
+ # @param [String] tracer A line from the LogEvent#tracer Array
39
+ # @return [String] Trimmed and parsed version of the file ane line number
40
+ def event_filename(tracer)
41
+ parts = tracer.match(/#{basedir}\/(.*:[0-9]+).*:/)
42
+
43
+ # If we get no matches back, we're probably in a jar file in which case
44
+ # the format of the tracer is going to be abbreviated
45
+ if parts.nil?
46
+ parts = tracer.match(/(.*:[0-9]+).*:/)
47
+ end
48
+ return parts[-1] if parts
49
+ end
50
+
51
+ # Receive the LogEvent and pull out the log message and format it for
52
+ # display in the logs
53
+ #
54
+ # @param [Log4r::LogEvent] event
55
+ # @return [String] Formatted log message
56
+ def format(event)
57
+ filename = event_filename(event.tracer[1])
58
+ time = Time.now.utc.iso8601
59
+ return "#{Log4r::LNAMES[event.level]}: #{time}: #{filename}: #{event.data}\n"
60
+ end
61
+ end
62
+
63
+
64
+ attr_reader :outputter
65
+
66
+ def initialize
67
+ logger_name = configatron.project_name.to_s.gsub(/\s*/, '_')
68
+ if logger_name.nil? || logger_name.empty?
69
+ logger_name = 'no_name_given'
70
+ end
71
+
72
+ @logger = Logger.new(logger_name)
73
+
74
+ if configatron.logging.enabled
75
+ index = Log4r::LNAMES.index(configatron.logging.level)
76
+ # if loggel.level is not in LNAMES an exception will be thrown
77
+ @logger.level = index unless index.nil?
78
+ else
79
+ @logger.level = Log4r::OFF
80
+ end
81
+
82
+ @outputter = FileOutputter.new("#{logger_name.to_s}fileoutput",
83
+ {:filename => configatron.logging.file,
84
+ :trunc => false})
85
+ @logger.trace = true
86
+ @outputter.formatter = LookoutFormatter
87
+
88
+ if ENV['RACK_ENV'] == 'production'
89
+ @outputter.formatter = Rack::Requestash::Log4r::Formatter
90
+ end
91
+ @logger.outputters = @outputter
92
+ end
93
+
94
+
95
+ [:debug, :info, :warn, :error, :level].each do |method|
96
+ define_method(method) do |*args|
97
+ if defined?(Lookout::Rack::Utils::Graphite)
98
+ Lookout::Rack::Utils::Graphite.increment("log.#{method}") unless method == :level
99
+ end
100
+ @logger.send(method, *args)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,35 @@
1
+ module Lookout::Rack::Utils
2
+ module Request
3
+ ILLEGAL_CHARS_REGEX = /[<>]/
4
+
5
+ # Return the raw, unprocessed request body
6
+ #
7
+ # @return [String]
8
+ def raw_body
9
+ # Rewind the StringIO object in case somebody else read it first
10
+ request.body.rewind
11
+ return request.body.read
12
+ end
13
+
14
+ # Process and parse the request body as JSON
15
+ #
16
+ # Will halt and create a a 400 status code if there is something wrong with
17
+ # the body
18
+ #
19
+ def body_as_json
20
+ body = raw_body
21
+
22
+ halt 400, { :error => t('error.body_was_nil') }.to_json if body.nil?
23
+ halt 400, { :error => t('error.body_was_blank') }.to_json if body.blank?
24
+
25
+ begin
26
+ return JSON.parse(body)
27
+ rescue JSON::ParserError
28
+ if defined?(Lookout::Rack::Utils::Log)
29
+ Lookout::Rack::Utils::Log.instance.warn "ParserError encountered parsing the request body (#{body})"
30
+ end
31
+ halt 400, "{}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ module Lookout::Rack::Utils
2
+ module Subroute
3
+ def subroute!(relative_path)
4
+ subcode, subheaders, body = call(env.merge('PATH_INFO' => relative_path))
5
+ return [subcode, body.first]
6
+ end
7
+
8
+ # Returns true if the status given is 20x
9
+ #
10
+ # @param [Integer] status
11
+ def succeeded?(status)
12
+ status.is_a?(Fixnum) && (200..299).include?(status)
13
+ end
14
+
15
+ # Returns false if the status given is 20x
16
+ #
17
+ # @param [Integer] status
18
+ def failed?(status)
19
+ !succeeded?(status)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module Lookout
2
+ module Rack
3
+ module Utils
4
+ VERSION = "1.2.0"
5
+ end
6
+ end
7
+ end
data/log ADDED
File without changes
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lookout/rack/utils/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lookout-rack-utils"
8
+ spec.version = Lookout::Rack::Utils::VERSION
9
+ spec.authors = ["Ian Smith"]
10
+ spec.email = ["ian.smith@lookout.com"]
11
+ spec.description = %q{A collection of rack utils.}
12
+ spec.summary = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rack-test"
25
+ spec.add_development_dependency "sinatra"
26
+ spec.add_development_dependency "timecop"
27
+
28
+ spec.add_runtime_dependency "i18n"
29
+
30
+ spec.add_dependency "rack"
31
+ spec.add_dependency "rack-graphite"
32
+ spec.add_dependency "rack-requestash"
33
+ spec.add_dependency "configatron"
34
+ spec.add_dependency "log4r"
35
+ spec.add_dependency "lookout-statsd", '>= 0.7.0'
36
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'lookout/rack/utils/graphite'
3
+
4
+ describe Lookout::Rack::Utils::Graphite do
5
+ subject(:graphite) { described_class }
6
+
7
+ before :each do
8
+ graphite.instance # Initialize statsd singleton
9
+ configatron.statsd.stub(:prefix).and_return('test')
10
+ end
11
+
12
+ context 'offers statsd methods' do
13
+ it { should respond_to :increment }
14
+ it { should respond_to :decrement }
15
+ it { should respond_to :timing }
16
+ it { should respond_to :update_counter }
17
+ end
18
+
19
+ it 'should delegate to statsd' do
20
+ Statsd.instance.should_receive(:increment).once.with('device.associated')
21
+ Lookout::Rack::Utils::Graphite.increment('device.associated')
22
+ end
23
+ end
data/spec/i18n_spec.rb ADDED
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+ require 'lookout/rack/utils/i18n'
3
+
4
+ class TestHelper
5
+ attr_accessor :locale
6
+ attr_accessor :request
7
+ attr_accessor :configatron
8
+
9
+ include Lookout::Rack::Utils::I18n
10
+
11
+ def initialize
12
+ end
13
+ end
14
+
15
+ describe Lookout::Rack::Utils::I18n do
16
+ let(:helper) { TestHelper.new }
17
+
18
+ describe '.t' do
19
+ let(:args) { double('mock arguments') }
20
+
21
+ before :each do
22
+ ::I18n.should_receive(:t).with(args)
23
+ end
24
+
25
+ it 'should call out to ::I18n.t' do
26
+ helper.t(args)
27
+ end
28
+ end
29
+
30
+ describe 'accepted_languages' do
31
+ subject { helper.accepted_languages }
32
+
33
+ before :each do
34
+ helper.request = Object.new
35
+ helper.request.stub(:env).and_return({'HTTP_ACCEPT_LANGUAGE' => accepted_langs})
36
+ end
37
+
38
+ context 'if HTTP_ACCEPT_LANGUAGE is not set' do
39
+ let(:accepted_langs) { nil }
40
+
41
+ before :each do
42
+ helper.configatron = Object.new
43
+ helper.configatron.stub(:locales).and_return([])
44
+ end
45
+
46
+ it { should be_empty }
47
+ end
48
+
49
+ context 'if HTTP_ACCEPT_LANGUAGE is set' do
50
+ # Example borrowed from http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
51
+ let(:accepted_langs) { 'da, en-gb;q=0.8, en;q=0.7' }
52
+ before :each do
53
+ helper.configatron = Object.new
54
+ helper.configatron.stub(:locales).and_return(['en'])
55
+ end
56
+
57
+ it { should_not be_empty }
58
+ end
59
+ end
60
+
61
+ describe '.current_locale' do
62
+
63
+ let(:target_locale) { 'target locale' }
64
+ let(:default_locale) { 'default locale' }
65
+ let(:accepted_langs) { [target_locale] }
66
+
67
+ before :each do
68
+ helper.stub(:accepted_languages).and_return(accepted_langs)
69
+ end
70
+
71
+ subject (:current_locale) { helper.current_locale }
72
+
73
+ context 'if locale is not nil' do
74
+ before :each do
75
+ helper.locale = target_locale
76
+ end
77
+
78
+ it { should eql target_locale }
79
+ end
80
+
81
+ context 'if locale is nil' do
82
+ context 'if configatron does not contain any of the accepted langs' do
83
+ before :each do
84
+ helper.configatron = Object.new
85
+ helper.configatron.stub(:locales).and_return([])
86
+ helper.configatron.stub(:default_locale).and_return(default_locale)
87
+ end
88
+
89
+ it { should eql default_locale }
90
+ end
91
+
92
+ context 'if configatron.locales contains one of the accepted languages' do
93
+ before :each do
94
+ helper.configatron = Object.new
95
+ helper.configatron.stub(:locales).and_return([target_locale])
96
+ end
97
+
98
+ it { should eql target_locale }
99
+ end
100
+ end
101
+ end
102
+ end
data/spec/log_spec.rb ADDED
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+ require 'lookout/rack/utils/log'
3
+ require 'timecop'
4
+ require 'configatron'
5
+
6
+ describe Lookout::Rack::Utils::Log do
7
+ subject(:log) { described_class.instance }
8
+
9
+ before :all do
10
+ configatron.logging.enabled = false
11
+ configatron.logging.file = "log"
12
+ end
13
+
14
+ describe '.debug' do
15
+ it 'should log a graphite stat' do
16
+ Lookout::Rack::Utils::Graphite.should_receive(:increment).with('log.debug')
17
+ log.debug 'foo'
18
+ end
19
+ end
20
+
21
+ describe '.info' do
22
+ it 'should log a graphite stat' do
23
+ Lookout::Rack::Utils::Graphite.should_receive(:increment).with('log.info')
24
+ log.info 'foo'
25
+ end
26
+ end
27
+
28
+ describe '.warn' do
29
+ it 'should log a graphite stat' do
30
+ Lookout::Rack::Utils::Graphite.should_receive(:increment).with('log.warn')
31
+ log.warn 'foo'
32
+ end
33
+ end
34
+
35
+ describe '.error' do
36
+ it 'should log a graphite stat' do
37
+ Lookout::Rack::Utils::Graphite.should_receive(:increment).with('log.error')
38
+ log.error 'foo'
39
+ end
40
+ end
41
+ end
42
+
43
+ describe Lookout::Rack::Utils::Log::LookoutFormatter do
44
+ subject(:formatter) { described_class.new }
45
+ let(:logger) do
46
+ logger = double('Mock Logger')
47
+ logger.stub(:name).and_return('RSpec Logger')
48
+ logger.stub(:fullname).and_return('RSpec Logger')
49
+ logger
50
+ end
51
+ let(:project_name) { 'some_project' }
52
+ let(:basedir) { "/home/rspec/#{project_name}" }
53
+ let(:tracer) do
54
+ [
55
+ "#{basedir}/log.rb:63:in `warn'",
56
+ "#{basedir}/spec/log_spec.rb:9:in `block (2 levels) in <top (required)>'"
57
+ ]
58
+ end
59
+
60
+ before :all do
61
+ # The root logger creates the log levels, so making sure it's been
62
+ # created
63
+ Log4r::RootLogger.instance
64
+ end
65
+
66
+
67
+ before :each do
68
+ formatter.stub(:basedir).and_return(basedir)
69
+ end
70
+
71
+
72
+ describe '#event_filename' do
73
+ subject(:filename) { formatter.event_filename(tracer[1]) }
74
+
75
+ context 'with a normal MRI LogEvent' do
76
+ it { should eql('spec/log_spec.rb:9') }
77
+ end
78
+
79
+ # We have slightly different log formats under packaged .jar files
80
+ context 'with a LogEvent from a packaged .jar' do
81
+ let(:tracer) { [nil, "backend/metrics.rb:52:in `runloop'"] }
82
+ let(:basedir) { 'file:/home/user/source/projects/stuff.jar!/project' }
83
+
84
+ it { should eql('backend/metrics.rb:52') }
85
+ end
86
+ end
87
+
88
+ describe '#format' do
89
+ before :each do
90
+ Timecop.freeze
91
+ end
92
+
93
+ after :each do
94
+ Timecop.return
95
+ end
96
+
97
+ context 'with a valid LogEvent' do
98
+ # Level 3 is the Log4r "warn" level
99
+ let(:level) { 3 }
100
+ let(:data) { 'rspec' }
101
+ let(:timestamp) { Time.now.utc.iso8601 }
102
+
103
+ let(:event) do
104
+ event = Log4r::LogEvent.new(level, logger, tracer, data)
105
+ end
106
+
107
+ it 'should be properly formatted' do
108
+ expect(formatter.format(event)).to eql("WARN: #{timestamp}: spec/log_spec.rb:9: #{data}\n")
109
+ end
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib/')
2
+
3
+ require 'lookout/rack/utils'
4
+
5
+ require 'rspec'
6
+
7
+ require 'rack/test'
8
+
9
+ Dir["./spec/support/**/*.rb"].sort.each do |f|
10
+ require f
11
+ end
12
+
13
+ RSpec.configure do |c|
14
+ c.include(Rack::Test::Methods, :type => :route)
15
+ c.include(RouteHelpers, :type => :route)
16
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lookout::Rack::Utils::Subroute, :type => :route do
4
+ describe '#subroute' do
5
+ let(:route) { '/test_route' }
6
+ let(:original) { get route }
7
+
8
+ subject(:subrouted) { get "/subrouted" }
9
+
10
+ before :each do
11
+ class RouteHelpers::Server
12
+ include Lookout::Rack::Utils::Subroute
13
+ get '/subrouted' do
14
+ subroute!('/test_route')
15
+ end
16
+ end
17
+ end
18
+
19
+ it 'should return the status code and body of the route' do
20
+ expect([subrouted.status, subrouted.body]).to eql [original.status, original.body]
21
+ end
22
+ end
23
+
24
+ describe '#succeeded?' do
25
+ subject { SubrouteTestHelper.new.succeeded?(status) }
26
+ context 'with status 200' do
27
+ let(:status) { 200 }
28
+ it { should be_true }
29
+ end
30
+
31
+ context 'with status 299' do
32
+ let(:status) { 299 }
33
+ it { should be_true }
34
+ end
35
+
36
+ context 'with a non-20x status' do
37
+ let(:status) { 300 }
38
+ it { should be_false }
39
+ end
40
+ end
41
+
42
+ describe '#failed?' do
43
+ subject { SubrouteTestHelper.new.failed?(status) }
44
+ context 'with status 200' do
45
+ let(:status) { 200 }
46
+ it { should be_false }
47
+ end
48
+
49
+ context 'with status 299' do
50
+ let(:status) { 299 }
51
+ it { should be_false }
52
+ end
53
+
54
+ context 'with a non-20x status' do
55
+ let(:status) { 300 }
56
+ it { should be_true }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ require 'sinatra'
2
+
3
+ module RouteHelpers
4
+ class Server < Sinatra::Base
5
+ get '/test_route' do
6
+ status 200
7
+ end
8
+ end
9
+
10
+ def app
11
+ Server.new
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ require 'lookout/rack/utils/subroute'
2
+
3
+ class SubrouteTestHelper
4
+ include Lookout::Rack::Utils::Subroute
5
+ end
metadata ADDED
@@ -0,0 +1,254 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lookout-rack-utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: i18n
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rack
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rack-graphite
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rack-requestash
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ! '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: configatron
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ! '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: log4r
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ! '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: lookout-statsd
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: 0.7.0
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: 0.7.0
195
+ description: A collection of rack utils.
196
+ email:
197
+ - ian.smith@lookout.com
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - .gitignore
203
+ - Gemfile
204
+ - LICENSE.txt
205
+ - README.md
206
+ - Rakefile
207
+ - lib/lookout/rack/utils.rb
208
+ - lib/lookout/rack/utils/graphite.rb
209
+ - lib/lookout/rack/utils/i18n.rb
210
+ - lib/lookout/rack/utils/log.rb
211
+ - lib/lookout/rack/utils/request.rb
212
+ - lib/lookout/rack/utils/subroute.rb
213
+ - lib/lookout/rack/utils/version.rb
214
+ - log
215
+ - lookout-rack-utils.gemspec
216
+ - spec/graphite_spec.rb
217
+ - spec/i18n_spec.rb
218
+ - spec/log_spec.rb
219
+ - spec/spec_helper.rb
220
+ - spec/subroute_spec.rb
221
+ - spec/support/routehelpers.rb
222
+ - spec/support/subroutehelpers.rb
223
+ homepage: ''
224
+ licenses:
225
+ - MIT
226
+ metadata: {}
227
+ post_install_message:
228
+ rdoc_options: []
229
+ require_paths:
230
+ - lib
231
+ required_ruby_version: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ! '>='
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
236
+ required_rubygems_version: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - ! '>='
239
+ - !ruby/object:Gem::Version
240
+ version: '0'
241
+ requirements: []
242
+ rubyforge_project:
243
+ rubygems_version: 2.0.5
244
+ signing_key:
245
+ specification_version: 4
246
+ summary: ''
247
+ test_files:
248
+ - spec/graphite_spec.rb
249
+ - spec/i18n_spec.rb
250
+ - spec/log_spec.rb
251
+ - spec/spec_helper.rb
252
+ - spec/subroute_spec.rb
253
+ - spec/support/routehelpers.rb
254
+ - spec/support/subroutehelpers.rb