rapporteur 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDgwNmQ2Yjc2Yzc3Y2NhMTk5YmMwMmFjNWNhZTQ3YWY2NGM0OTFjNw==
5
+ data.tar.gz: !binary |-
6
+ NDg0Njg3NTBiZmYyZmM0ODVjOGY0ZTIzYWE5MTcyMmI0M2U4ZjJjOQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NTE4NWFlODVmMjcxZjg3YTExZGIxMDQyNmI1OWM3ZmQxMGU5MzgyMmVjNDlk
10
+ MjMyYzE2NGYzMTQ0YzdjM2Q5ZTNhNmMzNGEyOTUwZDM5NjAyYzU2Y2ExNzY5
11
+ ZjQ5YTlmZWRiNWZhODQ4YjcxNzJkOTlmNWMxOWExODNiMTdiYzQ=
12
+ data.tar.gz: !binary |-
13
+ YjM5OTIzNjM0MDI5ODU1YjA3ZTc2ZDYwM2RlYWIwYTIzODljMmViY2YwNzg5
14
+ YThmMThkMzExMzAxOGZkMWU1YmQxMmE0NTMzNTg4N2E3M2ZlOTMxYTU3OGY5
15
+ MWIwNjUwNTJlODQ2OTAyZGU5NWNjYjBlNjZkMmM1ODMyMDU4Nzg=
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ .rvmrc
2
+ .ruby-version
3
+ .ruby-gemset
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ *.gemfile.lock
11
+ InstalledFiles
12
+ _yardoc
13
+ coverage
14
+ doc/
15
+ lib/bundler/man
16
+ pkg
17
+ rdoc
18
+ spec/reports
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
22
+ *.sqlite
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ gemfile:
6
+ - gemfiles/rails3.1.gemfile
7
+ - gemfiles/rails3.2.gemfile
8
+ notifications:
9
+ email: false
10
+ campfire:
11
+ on_success: change
12
+ on_failure: always
13
+ rooms:
14
+ - secure: "gxEt3SeVn1kup6PfB6hiQu6eWsefmMEdd8U1qPSS6vlRjsM7Xy2IrXdz9gdl\n0nrrRgESvOxT3sqMh4/opH6M1kbtCyl3M0yvjF2QUtjWQ+4BStJGhNyXlDTp\nNAas19fuEUPBNxNqoy7aTVBiFpQRs0NisEZTS3+N3eA2tz757Qk="
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "rails3.1" do
2
+ gem "railties", "~> 3.1.0"
3
+ end
4
+
5
+ appraise "rails3.2" do
6
+ gem "railties", "~> 3.2.0"
7
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Rapporteur
2
+
3
+ ## [HEAD][unreleased] / unreleased
4
+
5
+ * No significant changes.
6
+
7
+ ## 1.0.0 / 2013-05-19
8
+
9
+ * Initial public release.
10
+
11
+
12
+ [unreleased]: https://github.com/codeschool/rapporteur/compare/v1.0.0...master
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Envy Labs LLC and Code School LLC
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,219 @@
1
+ # Rapporteur
2
+
3
+ This gem provides a singular, status-checking endpoint to your application. The
4
+ endpoint provides a JSON response with either an HTTP 200 or an HTTP 500
5
+ response, depending on the current application environment.
6
+
7
+ When the environment tests successfully, an HTTP 200 response is returned with
8
+ the current application Git revision and server time:
9
+
10
+ ```json
11
+ {
12
+ "revision": "906731e6467ea381ba5bc70f103b85ed4178fee7",
13
+ "time": "2013-05-19T05:38:46Z"
14
+ }
15
+ ```
16
+
17
+ When an application validation fails, an HTTP 500 response is returned with a
18
+ collection of error messages, similar to the default Rails responders for model
19
+ validations:
20
+
21
+ ```json
22
+ {
23
+ "errors": {
24
+ "base": ["The application database is inaccessible or unavailable"]
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Installation
30
+
31
+ Supported Ruby versions:
32
+
33
+ * MRI 1.9.2
34
+ * MRI 1.9.3
35
+
36
+ Supported Rails versions:
37
+
38
+ * Rails 3.1.x.
39
+ * Rails 3.2.x.
40
+
41
+ To install, add this line to your application's Gemfile:
42
+
43
+ gem 'rapporteur'
44
+
45
+ And then execute:
46
+
47
+ $ bundle
48
+
49
+ ## Usage
50
+
51
+ Simply adding the gem requirement to your Gemfile is enough to install and
52
+ automatically load and configure the gem. Technically speaking, the gem is a
53
+ Rails Engine, so it auto-initializes with Rails starts up. There is no further
54
+ configuration necessary.
55
+
56
+ By default, there are no application checks that run and the status endpoint
57
+ simply reports the current application revision and time. This is useful for a
58
+ basic connectivity check to be watched by a third party service like Pingdom
59
+ for a very simple, non-critical application.
60
+
61
+ You may optionally use any of the pre-defined checks (such as the ActiveRecord
62
+ connection check) to expand the robustness of the status checks. Adding a check
63
+ will execute that check each time the status endpoint is requested, so be
64
+ somewhat wary of doing _too_ much. See more in the [Adding checks
65
+ section](#adding-checks), below.
66
+
67
+ Further, you can define your own checks which could be custom to your
68
+ application or environment and report their own, unique errors. The only
69
+ requirement is that the check objects are callable (respond to `#call`, like a
70
+ Proc). See more in the [Creating custom checks
71
+ section](#creating-custom-checks), below.
72
+
73
+ ### The endpoint
74
+
75
+ This gem provides a new, single endpoint in your application. Specifically, it
76
+ creates a named `/status.json` route, with the "status" name. It does not match
77
+ on any other format or variation, which isolates the pollution of your
78
+ application routes.
79
+
80
+ If you'd like to link to the status endpoint from within your application (why,
81
+ I couldn't guess), you can use a standard Rails URL helper:
82
+
83
+ ```ruby
84
+ link_to status_path
85
+ ```
86
+
87
+ Were you already using the `/status.json` endpoint or the "status" route name?
88
+ Hmm. Well... you just broke it.
89
+
90
+ ## Customization
91
+
92
+ ### Adding checks
93
+
94
+ This gem ships with the following checks tested and packaged:
95
+
96
+ * **Rapporteur::Checks::ActiveRecordCheck** - Performs a trivial test
97
+ of the current `ActiveRecord::Base.connection` to ensure basic database
98
+ connectivity.
99
+
100
+ To add checks to your application, define the checks you'd like to run in your
101
+ environment or application configuration files or initializers, such as:
102
+
103
+ ```ruby
104
+ # config/initializers/rapporteur.rb
105
+ Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
106
+ ```
107
+
108
+ Or, make an environment specific check with:
109
+
110
+ ```ruby
111
+ # config/environments/production.rb
112
+ MyApplication.configure do
113
+ config.to_prepare do
114
+ Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
115
+ end
116
+ end
117
+ ```
118
+
119
+ ### Creating custom checks
120
+
121
+ It is simple to add a custom check to the status endpoint. All that is required
122
+ is that you give the checker an object that is callable. In your object, simply
123
+ check for the state of the world that you're interested in, and if you're not
124
+ happy with it, add an error to the given `checker` instance:
125
+
126
+ ```ruby
127
+ # config/initializers/rapporteur.rb
128
+
129
+ my_proc_check = lambda { |checker|
130
+ checker.add_error("You have bad luck!") if rand(10) > 5
131
+ }
132
+
133
+ Rapporteur::Checker.add_check(my_proc_check)
134
+
135
+ class MyClassCheck
136
+ def self.call(checker)
137
+ @@counter ||= 0
138
+ checker.add_error("Stop calling me!!") if @@counter > 50
139
+ end
140
+ end
141
+
142
+ Rapporteur::Checker.add_check(MyClassCheck)
143
+ ```
144
+
145
+ Certainly, the definition and registration of the checks do not need to occur
146
+ within the same file, but you get the idea. Also: Please make your checks more
147
+ useful than those defined above. ;)
148
+
149
+ You could create a checker for your active Redis connection, Memcached
150
+ connections, disk usage percentage, process count, memory usage, or really
151
+ anything you like. Again, because these checks get executed every time the
152
+ status endpoint is called, **be mindful of the tradeoffs when making a check that
153
+ may be resource intensive**.
154
+
155
+ ### Customizing the revision
156
+
157
+ If you need to customize the way in which the current application revision is
158
+ calculated (by default it runs a `git rev-parse HEAD`), you may do so by
159
+ modifying the necessary environment file or creating an initializer in your
160
+ Rails application:
161
+
162
+ ```ruby
163
+ # config/initializers/rapporteur.rb
164
+ Rapporteur::Revision.current = "revision123"
165
+ ```
166
+
167
+ ```ruby
168
+ # config/environments/production.rb
169
+ MyApplication.configure do
170
+ config.to_prepare do
171
+ Rapporteur::Revision.current = "revision123"
172
+ end
173
+ end
174
+ ```
175
+
176
+ You may pass a String or a callable object (Proc) to `.current=` and it will be
177
+ executed and memoized. Useful examples of this are:
178
+
179
+ ```ruby
180
+ # Read a Capistrano REVISION file
181
+ Rapporteur::Revision.current = Rails.root.join("REVISION").read.strip
182
+
183
+ # Force a particular directory and use Git
184
+ Rapporteur::Revision.current = `cd "#{Rails.root}" && git rev-parse HEAD`.strip
185
+
186
+ # Use an ENV variable (Heroku)
187
+ Rapporteur::Revision.current = ENV["REVISION"]
188
+
189
+ # Do some crazy calculation
190
+ Rapporteur::Revision.current = lambda { MyRevisionCalculator.execute! }
191
+ ```
192
+
193
+ ### Customizing the error messages
194
+
195
+ The error messages displayed in the event that application validations fail are
196
+ all collected through I18n. There are default localization strings provided
197
+ with the gem, but you may override them as necessary, simply by redefining them
198
+ in a locales file within your local application.
199
+
200
+ For example, to override the database check failure message:
201
+
202
+ ```yaml
203
+ en:
204
+ activemodel:
205
+ errors:
206
+ models:
207
+ rapporteur/checker:
208
+ attributes:
209
+ base:
210
+ database_unavailable: "Something went wrong"
211
+ ```
212
+
213
+ ## Contributing
214
+
215
+ 1. Fork it
216
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
217
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
218
+ 4. Push to the branch (`git push origin my-new-feature`)
219
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'appraisal'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
@@ -0,0 +1,10 @@
1
+ class StatusesController < ActionController::Base
2
+ self.responder = Rapporteur::Responder
3
+ respond_to :json
4
+
5
+ def show
6
+ respond_with(Rapporteur::Checker.run, {
7
+ serializer: Rapporteur::Serializer
8
+ })
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ en:
2
+ activemodel:
3
+ errors:
4
+ models:
5
+ rapporteur/checker:
6
+ attributes:
7
+ base:
8
+ database_unavailable: "The application database is inaccessible or unavailable"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ get 'status.json', to: 'statuses#show', defaults: {format: 'json'}, as: :status
3
+ end
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+ run Combustion::Application
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 3.1.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 3.2.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,111 @@
1
+ require 'singleton'
2
+ require 'set'
3
+
4
+ module Rapporteur
5
+ # The center of the Rapporteur library, Checker manages holding and running
6
+ # the custom checks, holding any application error messages, and provides the
7
+ # controller with that data for rendering.
8
+ #
9
+ class Checker
10
+ include Singleton
11
+ include ActiveModel::Validations
12
+ include ActiveModel::SerializerSupport
13
+
14
+
15
+ # Public: Add a pre-built or custom check to your status endpoint. These
16
+ # checks are used to test the state of the world of the application, and
17
+ # need only respond to `#call`.
18
+ #
19
+ # Once added, the given check will be called and passed an instance of this
20
+ # checker. If everything is good, do nothing! If there is a problem, use
21
+ # `add_error` to add an error message to the checker.
22
+ #
23
+ # Examples
24
+ #
25
+ # Rapporteur::Checker.add_check(lambda { |checker|
26
+ # checker.add_error("Bad luck.") if rand(2) == 1
27
+ # })
28
+ #
29
+ # Returns Rapporteur::Checker.
30
+ # Raises ArgumentError if the given check does not respond to call.
31
+ #
32
+ def self.add_check(object)
33
+ raise ArgumentError, "A check must respond to #call." unless object.respond_to?(:call)
34
+ instance.checks << object
35
+ self
36
+ end
37
+
38
+ # Public: Empties all configured checks from the checker. This may be
39
+ # useful for testing and for cases where you might've built up some basic
40
+ # checks but for one reason or another (environment constraint) need to
41
+ # start from scratch.
42
+ #
43
+ # Returns Rapporteur::Checker.
44
+ #
45
+ def self.clear
46
+ instance.checks.clear
47
+ self
48
+ end
49
+
50
+ # Public: This is the primary execution point for this class. Use run to
51
+ # exercise the configured checker and collect any application errors or
52
+ # data for rendering.
53
+ #
54
+ # Returns a Rapporteur::Checker instance.
55
+ #
56
+ def self.run
57
+ instance.errors.clear
58
+ instance.run
59
+ end
60
+
61
+
62
+ # Public: Add an error message to the checker in order to have it rendered
63
+ # in the status request.
64
+ #
65
+ # It is suggested that you use I18n and locale files for these messages, as
66
+ # is done with the pre-built checks. If you're using I19n, you'll need to
67
+ # define `activemodel.errors.models.rapporteur/checker.attributes.base.<your key>`.
68
+ #
69
+ # Examples
70
+ #
71
+ # checker.add_error("You failed.")
72
+ # checker.add_error(:i18n_key_is_better)
73
+ #
74
+ # Returns the Rapporteur::Checker instance.
75
+ #
76
+ def add_error(message)
77
+ errors.add(:base, message)
78
+ self
79
+ end
80
+
81
+ # Public: Returns the Set of checks currently configured.
82
+ #
83
+ def checks
84
+ @checks ||= Set.new
85
+ end
86
+
87
+ # Public: Returns a String containing the current revision of the
88
+ # application.
89
+ #
90
+ def revision
91
+ Revision.current
92
+ end
93
+
94
+ # Public: Executes the configured checks.
95
+ #
96
+ # Returns the Rapporteur::Checker instance.
97
+ #
98
+ def run
99
+ checks.each do |object|
100
+ object.call(self)
101
+ end
102
+ self
103
+ end
104
+
105
+ # Public: Returns a Time instance containing the current system time.
106
+ #
107
+ def time
108
+ Time.now
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,11 @@
1
+ module Rapporteur
2
+ module Checks
3
+ class ActiveRecordCheck
4
+ def self.call(checker)
5
+ ActiveRecord::Base.connection.execute("SELECT current_time AS time").first.fetch('time')
6
+ rescue
7
+ checker.add_error(:database_unavailable)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Rapporteur
2
+ module Checks
3
+ autoload :ActiveRecordCheck, 'rapporteur/checks/active_record_check'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Rapporteur
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,32 @@
1
+ module Rapporteur
2
+ # A customization of the default Rails ActionController::Responder.
3
+ # Primarily, this is used to smooth out the differences between Rails
4
+ # responder versions and allow for error messages in GET requests.
5
+ #
6
+ class Responder < ActionController::Responder
7
+ # Internal: Overrides the default behavior by ignoring the HTTP verb and
8
+ # always responding with errors if the rendering resource contains errors.
9
+ #
10
+ def to_format
11
+ if has_errors?
12
+ display_errors
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+
19
+ protected
20
+
21
+
22
+ if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 1
23
+ def display_errors
24
+ controller.render format => {errors: resource.errors}, status: :internal_server_error
25
+ end
26
+ elsif Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 2
27
+ def display_errors
28
+ controller.render format => resource_errors, status: :internal_server_error
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,56 @@
1
+ module Rapporteur
2
+ # Manages memoizing and maintaining the current application revision.
3
+ #
4
+ class Revision
5
+ class_attribute :_current
6
+
7
+ # Public: Returns the current revision as a String.
8
+ #
9
+ def self.current
10
+ self._current ||= calculate_current
11
+ end
12
+
13
+ # Public: Forcibly sets the current application revision.
14
+ #
15
+ # revision - Either a String or a callable object (Proc, for example) to
16
+ # use your own environment logic to determine the revision.
17
+ #
18
+ # Examples
19
+ #
20
+ # Rapporteur::Revision.current = ENV['REVISION'].strip
21
+ # Rapporteur::Revision.current = Rails.root.join("REVISION").read.strip
22
+ #
23
+ # Returns the revision given.
24
+ #
25
+ def self.current=(revision)
26
+ self._current = calculate_current(revision)
27
+ end
28
+
29
+ # Internal: The default method of determining the current revision. This
30
+ # assumes a git executable is in the current PATH and that the process
31
+ # context is running the the appropriate git application directory.
32
+ #
33
+ # Returns a String containing the current git revision, hopefully.
34
+ #
35
+ def self.default_revision_source
36
+ `git rev-parse HEAD 2>/dev/null`.strip
37
+ rescue
38
+ end
39
+
40
+ # Internal: Calculates the current revision from the configured revision
41
+ # source.
42
+ #
43
+ def self.calculate_current(revision = default_revision_source)
44
+ case revision
45
+ when String
46
+ revision
47
+ when Proc
48
+ revision.call.to_s
49
+ when NilClass
50
+ "You must provide a Rapporteur::Revision.current= String or Proc"
51
+ else
52
+ raise ArgumentError, "Unknown revision type given: #{revision.inspect}"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,68 @@
1
+ require 'rapporteur'
2
+
3
+ shared_examples_for 'a successful status response' do
4
+ it 'responds with HTTP 200' do
5
+ expect(subject.response_code).to(eq(200))
6
+ end
7
+
8
+ it 'responds with a JSON content header' do
9
+ expect(subject.content_type).to(eq(Mime::JSON))
10
+ end
11
+
12
+ it 'responds with valid JSON' do
13
+ expect { JSON.parse(subject.body) }.not_to(raise_error)
14
+ end
15
+
16
+ context 'the response payload' do
17
+ subject { get(status_path) ; JSON.parse(response.body) }
18
+
19
+ it 'does not contain errors' do
20
+ expect(subject).not_to(have_key('errors'))
21
+ end
22
+
23
+ it 'contains the current application revision' do
24
+ expect(subject.fetch('revision')).to(match(/^[a-f0-9]{40}$/))
25
+ end
26
+
27
+ it 'contains the current time in ISO8601' do
28
+ time = Time.now
29
+ Time.stub(:now).and_return(time)
30
+ expect(subject.fetch('time')).to(match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/))
31
+ expect(subject.fetch('time')).to(eq(time.utc.iso8601))
32
+ end
33
+ end
34
+ end
35
+
36
+ shared_examples_for 'an erred status response' do
37
+ it 'responds with HTTP 500' do
38
+ expect(subject.response_code).to(eq(500))
39
+ end
40
+
41
+ it 'responds with a JSON content header' do
42
+ expect(subject.content_type).to(eq(Mime::JSON))
43
+ end
44
+
45
+ it 'responds with valid JSON' do
46
+ expect { JSON.parse(subject.body) }.not_to(raise_error)
47
+ end
48
+
49
+ it 'contains errors' do
50
+ expect(JSON.parse(subject.body)).to(have_key('errors'))
51
+ expect(JSON.parse(subject.body).fetch('errors')).not_to(be_empty)
52
+ end
53
+ end
54
+
55
+ RSpec::Matchers.define :include_status_error_message do |message|
56
+ match do |response|
57
+ @body = JSON.parse(response.body)
58
+ @body.fetch('errors', {}).fetch('base').include?(message)
59
+ end
60
+
61
+ failure_message_for_should do |actual|
62
+ "expected #{@body.inspect} to include a #{message.inspect} error message"
63
+ end
64
+
65
+ failure_message_for_should_not do |actual|
66
+ "expected #{@body.inspect} to not include a #{message.inspect} error message"
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ module Rapporteur
2
+ # An ActiveModel::Serializer used to serialize the checker data for JSON
3
+ # rendering.
4
+ #
5
+ class Serializer < ActiveModel::Serializer
6
+ self.root = false
7
+
8
+ attributes :revision,
9
+ :time
10
+
11
+ # Internal: Converts the checker instance time into UTC to provide a
12
+ # consistent public representation.
13
+ #
14
+ # Returns a Time instance in UTC.
15
+ #
16
+ def time
17
+ object.time.utc
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Rapporteur
2
+ VERSION = "1.0.0"
3
+ end
data/lib/rapporteur.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'active_model_serializers'
2
+
3
+ require "rapporteur/engine" if defined?(Rails)
4
+ require "rapporteur/version"
5
+
6
+ # Rapporteur is a Rails Engine which provides your application with an
7
+ # application status endpoint.
8
+ #
9
+ module Rapporteur
10
+ autoload :Checker, 'rapporteur/checker'
11
+ autoload :Checks, 'rapporteur/checks'
12
+ autoload :Responder, 'rapporteur/responder'
13
+ autoload :Revision, 'rapporteur/revision'
14
+ autoload :Serializer, 'rapporteur/serializer'
15
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rapporteur/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rapporteur"
8
+ spec.version = Rapporteur::VERSION
9
+ spec.authors = ["Envy Labs", "Code School"]
10
+ spec.email = [""]
11
+ spec.description = %q{An engine that provides common status polling endpoint.}
12
+ spec.summary = %q{An engine that provides common status polling endpoint.}
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_dependency 'active_model_serializers', '>= 0.8'
22
+ spec.add_dependency 'railties', '~> 3.0'
23
+
24
+ spec.add_development_dependency "appraisal", "~> 0.5"
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "combustion", "~> 0.5"
27
+ spec.add_development_dependency "rails", "~> 3.0"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec-rails", "~> 2.0"
30
+ spec.add_development_dependency "sqlite3"
31
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,3 @@
1
+ ActiveRecord::Schema.define do
2
+ #
3
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'A status request with an ActiveRecordCheck' do
4
+ before do
5
+ Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
6
+ end
7
+
8
+ subject { get(status_path) ; response }
9
+
10
+ context 'with an unerring ActiveRecord connection' do
11
+ it_behaves_like 'a successful status response'
12
+ end
13
+
14
+ context 'with a failed ActiveRecord connection' do
15
+ before do
16
+ ActiveRecord::Base.connection.stub(:execute).
17
+ and_raise(ActiveRecord::ConnectionNotEstablished)
18
+ end
19
+
20
+ it_behaves_like 'an erred status response'
21
+
22
+ it 'contains a message regarding the database failure' do
23
+ expect(subject).to include_status_error_message(I18n.t('activemodel.errors.models.rapporteur/checker.attributes.base.database_unavailable'))
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'A status request with no checks' do
4
+ before { Rapporteur::Checker.clear }
5
+
6
+ subject { get(status_path) ; response }
7
+
8
+ it_behaves_like 'a successful status response'
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe "status routes" do
4
+ it 'routes /status.json to statuses#show' do
5
+ expect({ get: '/status.json'}).to route_to({
6
+ action: 'show',
7
+ controller: 'statuses',
8
+ format: 'json',
9
+ })
10
+ end
11
+
12
+ it 'does not route /status' do
13
+ expect({ get: '/status'}).to_not be_routable
14
+ end
15
+
16
+ it 'does not route /status.html' do
17
+ expect({ get: '/status.html'}).to_not be_routable
18
+ end
19
+
20
+ it 'does not route /status.xml' do
21
+ expect({ get: '/status.xml'}).to_not be_routable
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'combustion'
4
+
5
+ ENV["RAILS_ENV"] ||= 'test'
6
+ Combustion.initialize! :action_controller, :active_record
7
+
8
+ require 'rspec/rails'
9
+ require 'rapporteur/rspec'
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+
16
+ config.order = 'random'
17
+
18
+ config.before { Rapporteur::Checker.clear }
19
+ end
metadata ADDED
@@ -0,0 +1,215 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rapporteur
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Envy Labs
8
+ - Code School
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_model_serializers
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0.8'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0.8'
28
+ - !ruby/object:Gem::Dependency
29
+ name: railties
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '3.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: appraisal
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '0.5'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '0.5'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '1.3'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ - !ruby/object:Gem::Dependency
71
+ name: combustion
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '0.5'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '0.5'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rails
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: '3.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '3.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rake
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rspec-rails
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ version: '2.0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '2.0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: sqlite3
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ description: An engine that provides common status polling endpoint.
141
+ email:
142
+ - ''
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - .gitignore
148
+ - .rspec
149
+ - .travis.yml
150
+ - Appraisals
151
+ - CHANGELOG.md
152
+ - Gemfile
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - app/controllers/statuses_controller.rb
157
+ - config.ru
158
+ - config/locales/en.yml
159
+ - config/routes.rb
160
+ - gemfiles/rails3.1.gemfile
161
+ - gemfiles/rails3.2.gemfile
162
+ - lib/rapporteur.rb
163
+ - lib/rapporteur/checker.rb
164
+ - lib/rapporteur/checks.rb
165
+ - lib/rapporteur/checks/active_record_check.rb
166
+ - lib/rapporteur/engine.rb
167
+ - lib/rapporteur/responder.rb
168
+ - lib/rapporteur/revision.rb
169
+ - lib/rapporteur/rspec.rb
170
+ - lib/rapporteur/serializer.rb
171
+ - lib/rapporteur/version.rb
172
+ - rapporteur.gemspec
173
+ - spec/internal/config/database.yml
174
+ - spec/internal/config/routes.rb
175
+ - spec/internal/db/schema.rb
176
+ - spec/internal/log/.gitignore
177
+ - spec/internal/public/favicon.ico
178
+ - spec/requests/active_record_check_spec.rb
179
+ - spec/requests/no_checks_spec.rb
180
+ - spec/routing/routes_spec.rb
181
+ - spec/spec_helper.rb
182
+ homepage: ''
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ! '>='
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.0.3
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: An engine that provides common status polling endpoint.
206
+ test_files:
207
+ - spec/internal/config/database.yml
208
+ - spec/internal/config/routes.rb
209
+ - spec/internal/db/schema.rb
210
+ - spec/internal/log/.gitignore
211
+ - spec/internal/public/favicon.ico
212
+ - spec/requests/active_record_check_spec.rb
213
+ - spec/requests/no_checks_spec.rb
214
+ - spec/routing/routes_spec.rb
215
+ - spec/spec_helper.rb