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 +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +101 -0
- data/Rakefile +9 -0
- data/lib/lookout/rack/utils.rb +24 -0
- data/lib/lookout/rack/utils/graphite.rb +35 -0
- data/lib/lookout/rack/utils/i18n.rb +52 -0
- data/lib/lookout/rack/utils/log.rb +104 -0
- data/lib/lookout/rack/utils/request.rb +35 -0
- data/lib/lookout/rack/utils/subroute.rb +22 -0
- data/lib/lookout/rack/utils/version.rb +7 -0
- data/log +0 -0
- data/lookout-rack-utils.gemspec +36 -0
- data/spec/graphite_spec.rb +23 -0
- data/spec/i18n_spec.rb +102 -0
- data/spec/log_spec.rb +113 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/subroute_spec.rb +59 -0
- data/spec/support/routehelpers.rb +13 -0
- data/spec/support/subroutehelpers.rb +5 -0
- metadata +254 -0
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
data/Gemfile
ADDED
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,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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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
|