sorry-rails 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +28 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +96 -0
- data/LICENSE.txt +21 -0
- data/README.md +107 -0
- data/Rakefile +6 -0
- data/lib/sorry/rails.rb +38 -0
- data/lib/sorry/rails/configuration.rb +22 -0
- data/lib/sorry/rails/railtie.rb +21 -0
- data/lib/sorry/rails/script_tag_helper.rb +64 -0
- data/lib/sorry/rails/subscriber_serializer.rb +93 -0
- data/lib/sorry/rails/version.rb +8 -0
- data/sorry-rails.gemspec +28 -0
- data/spec/sorry/rails/configuration_spec.rb +16 -0
- data/spec/sorry/rails/script_tag_helper_spec.rb +72 -0
- data/spec/sorry/rails/subscriber_serializer_spec.rb +89 -0
- data/spec/sorry/rails_spec.rb +62 -0
- data/spec/spec_helper.rb +32 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47ef1033ef828a5f626e82e9917e4b933faf134b
|
4
|
+
data.tar.gz: 468b191147aa0fe4f8609b2f3e6d7ed3cd5f5013
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 493917af41e616934deed8ae63115eda700c7972a1e1f8ee2fca69a67cf087e79aa0ffff669ca3d74ba8f3d44bcde97465e7a909e6d0c9ccabc729d47a648ebe
|
7
|
+
data.tar.gz: f37b3b7e311b7ae90d0d75da0ef90cff99e8d2579369d7d52c40bda59e38ba69a2e51c0bf8891ab1063c11d2cf360c1df4c807679fd6b9e451fe82dff26e6e45
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Layout/IndentationWidth:
|
2
|
+
Width: 4
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Max: 200
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Exclude:
|
9
|
+
- 'spec/**/*.rb'
|
10
|
+
|
11
|
+
Layout/EmptyLinesAroundBlockBody:
|
12
|
+
Exclude:
|
13
|
+
- 'spec/**/*.rb'
|
14
|
+
|
15
|
+
Layout/EmptyLinesAroundClassBody:
|
16
|
+
EnforcedStyle: empty_lines_except_namespace
|
17
|
+
|
18
|
+
Layout/EmptyLinesAroundModuleBody:
|
19
|
+
EnforcedStyle: empty_lines_except_namespace
|
20
|
+
|
21
|
+
Layout/AlignParameters:
|
22
|
+
EnforcedStyle: with_fixed_indentation
|
23
|
+
|
24
|
+
Layout/IndentHash:
|
25
|
+
EnforcedStyle: consistent
|
26
|
+
|
27
|
+
Style/GuardClause:
|
28
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
sorry-rails (0.1.2)
|
5
|
+
actionview
|
6
|
+
activesupport
|
7
|
+
hashie (~> 2.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionview (5.2.0)
|
13
|
+
activesupport (= 5.2.0)
|
14
|
+
builder (~> 3.1)
|
15
|
+
erubi (~> 1.4)
|
16
|
+
rails-dom-testing (~> 2.0)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
18
|
+
activesupport (5.2.0)
|
19
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
20
|
+
i18n (>= 0.7, < 2)
|
21
|
+
minitest (~> 5.1)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
ast (2.4.0)
|
24
|
+
builder (3.2.3)
|
25
|
+
concurrent-ruby (1.0.5)
|
26
|
+
crass (1.0.4)
|
27
|
+
diff-lcs (1.3)
|
28
|
+
erubi (1.7.1)
|
29
|
+
faker (1.8.7)
|
30
|
+
i18n (>= 0.7)
|
31
|
+
hashie (2.1.2)
|
32
|
+
i18n (1.0.1)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
jaro_winkler (1.5.1)
|
35
|
+
loofah (2.2.2)
|
36
|
+
crass (~> 1.0.2)
|
37
|
+
nokogiri (>= 1.5.9)
|
38
|
+
mini_portile2 (2.3.0)
|
39
|
+
minitest (5.11.3)
|
40
|
+
nokogiri (1.8.4)
|
41
|
+
mini_portile2 (~> 2.3.0)
|
42
|
+
parallel (1.12.1)
|
43
|
+
parser (2.5.1.2)
|
44
|
+
ast (~> 2.4.0)
|
45
|
+
powerpack (0.1.2)
|
46
|
+
rails-dom-testing (2.0.3)
|
47
|
+
activesupport (>= 4.2.0)
|
48
|
+
nokogiri (>= 1.6)
|
49
|
+
rails-html-sanitizer (1.0.4)
|
50
|
+
loofah (~> 2.2, >= 2.2.2)
|
51
|
+
rainbow (3.0.0)
|
52
|
+
rake (12.3.1)
|
53
|
+
rspec (3.7.0)
|
54
|
+
rspec-core (~> 3.7.0)
|
55
|
+
rspec-expectations (~> 3.7.0)
|
56
|
+
rspec-mocks (~> 3.7.0)
|
57
|
+
rspec-core (3.7.1)
|
58
|
+
rspec-support (~> 3.7.0)
|
59
|
+
rspec-expectations (3.7.0)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.7.0)
|
62
|
+
rspec-html-matchers (0.9.1)
|
63
|
+
nokogiri (~> 1)
|
64
|
+
rspec (>= 3.0.0.a, < 4)
|
65
|
+
rspec-mocks (3.7.0)
|
66
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
67
|
+
rspec-support (~> 3.7.0)
|
68
|
+
rspec-support (3.7.1)
|
69
|
+
rubocop (0.58.1)
|
70
|
+
jaro_winkler (~> 1.5.1)
|
71
|
+
parallel (~> 1.10)
|
72
|
+
parser (>= 2.5, != 2.5.1.1)
|
73
|
+
powerpack (~> 0.1)
|
74
|
+
rainbow (>= 2.2.2, < 4.0)
|
75
|
+
ruby-progressbar (~> 1.7)
|
76
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
77
|
+
ruby-progressbar (1.9.0)
|
78
|
+
thread_safe (0.3.6)
|
79
|
+
tzinfo (1.2.5)
|
80
|
+
thread_safe (~> 0.1)
|
81
|
+
unicode-display_width (1.4.0)
|
82
|
+
|
83
|
+
PLATFORMS
|
84
|
+
ruby
|
85
|
+
|
86
|
+
DEPENDENCIES
|
87
|
+
bundler
|
88
|
+
faker
|
89
|
+
rake
|
90
|
+
rspec
|
91
|
+
rspec-html-matchers
|
92
|
+
rubocop
|
93
|
+
sorry-rails!
|
94
|
+
|
95
|
+
BUNDLED WITH
|
96
|
+
1.16.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Robert Rawlins
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Sorry™ - Rails
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/sorry-app/sorry-rails.svg?branch=master)](https://travis-ci.org/sorry-app/sorry-rails)
|
4
|
+
|
5
|
+
> A Rails gem to add the [Sorry™ Website Plugin](https://github.com/sorry-app/status-bar) to your application. Easily display notices from your status page to your users, and register them as subscribers to receive updates when the particular parts of your application they use are experiencing issues.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
#### Install the Gem
|
10
|
+
|
11
|
+
Add the gem to your Gemfile.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'sorry-rails'
|
15
|
+
```
|
16
|
+
|
17
|
+
Once this has been done you can run `bundle install` to install the gem into your bundle.
|
18
|
+
|
19
|
+
#### Create an Initializer
|
20
|
+
|
21
|
+
Create an initializer to configure your plugin, we'd suggest putting this in `/config/initializers/sorry-rails.rb`.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# Configure website plugin.
|
25
|
+
Sorry::Rails.configure do |config|
|
26
|
+
# Set the page identity.
|
27
|
+
config.page_id = 'xxxxxxxx'
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Place your own status page ID into the initializer, so the plugin knows where to pull your updates from.
|
32
|
+
|
33
|
+
#### Include the JavaScript
|
34
|
+
|
35
|
+
You can now use the helper method to include the appropriate JavaScript into your layouts and views.
|
36
|
+
|
37
|
+
```erb
|
38
|
+
<!-- Plugin to install status notices. -->
|
39
|
+
<%= sorry_script_tag %>
|
40
|
+
```
|
41
|
+
|
42
|
+
You'll most likely want to add this to `application.html.erb` so it displays on all pages.
|
43
|
+
|
44
|
+
## Subscriber Data
|
45
|
+
|
46
|
+
By default the plugin plays nicely with [Devise](https://github.com/plataformatec/devise) by looking for a method called `current_user`, if this method returns an object with an `email` attribute they are passed along as a subscriber, so they'll appear in your subscribers list.
|
47
|
+
|
48
|
+
### Custom current user method
|
49
|
+
|
50
|
+
If you don't use Devise, or it's default `current_user` method, you can customize the method name in your initializer.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
config.current_user_method = :logged_in_user
|
54
|
+
```
|
55
|
+
|
56
|
+
Pass a symbol which represents the method name, and we'll call that.
|
57
|
+
|
58
|
+
### Subscribing to specific components (Established plan only)
|
59
|
+
|
60
|
+
Sometimes users don't utilize all of your application, and they'll only want updates about incidents which affect the parts they actually use. For example, if one of our customers uses our Mailgun integration, but Sendgrid is currently experiencing issues, they don't need to know.
|
61
|
+
|
62
|
+
You can pass us an array of component_ids that the subscriber is interested in.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# Method name example.
|
66
|
+
config.component_ids_method = :component_ids
|
67
|
+
|
68
|
+
# Proc based example.
|
69
|
+
config.component_ids_method = Proc.new { |user|
|
70
|
+
# Logic here to get array of IDs...
|
71
|
+
}
|
72
|
+
```
|
73
|
+
|
74
|
+
As per the example, this can either be a method_name to be called on your current_user, or a Proc which is passed the current user, both should return an array of numeric component ids.
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
In lieu of a formal style-guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code.
|
79
|
+
|
80
|
+
Once you are happy that your contribution is ready for production please send us a pull request, at which point we'll review the code and merge it in.
|
81
|
+
|
82
|
+
## Versioning
|
83
|
+
|
84
|
+
For transparency and insight into our release cycle, and for striving to maintain backward compatibility, This project will be maintained under the Semantic Versioning guidelines as much as possible.
|
85
|
+
|
86
|
+
Releases will be numbered with the following format:
|
87
|
+
|
88
|
+
`<major>.<minor>.<patch>`
|
89
|
+
|
90
|
+
And constructed with the following guidelines:
|
91
|
+
|
92
|
+
* Breaking backward compatibility bumps the major (and resets the minor and patch)
|
93
|
+
* New additions without breaking backward compatibility bumps the minor (and resets the patch)
|
94
|
+
* Bug fixes and misc changes bumps the patch
|
95
|
+
|
96
|
+
For more information on SemVer, please visit <http://semver.org/>.
|
97
|
+
|
98
|
+
## Authors & Contributors
|
99
|
+
|
100
|
+
**Robert Rawlins**
|
101
|
+
|
102
|
+
+ <http://twitter.com/sirrawlins>
|
103
|
+
+ <https://github.com/SirRawlins>
|
104
|
+
|
105
|
+
## Copyright
|
106
|
+
|
107
|
+
© Copyright - See [LICENSE](LICENSE.txt) for details.
|
data/Rakefile
ADDED
data/lib/sorry/rails.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sorry/rails/configuration'
|
2
|
+
require 'sorry/rails/script_tag_helper'
|
3
|
+
require 'sorry/rails/subscriber_serializer'
|
4
|
+
require 'sorry/rails/railtie' if defined?(::Rails::Railtie)
|
5
|
+
require 'sorry/rails/version'
|
6
|
+
|
7
|
+
module Sorry
|
8
|
+
#
|
9
|
+
# Mail rails plugin definition for
|
10
|
+
# the Sorry gem.
|
11
|
+
#
|
12
|
+
module Rails
|
13
|
+
|
14
|
+
# Define the JS plugin version to be used
|
15
|
+
# by the script tag generator.
|
16
|
+
PLUGIN_VERSION = '4.latest'.freeze
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
# Attr to store the configuration.
|
21
|
+
attr_accessor :configuration
|
22
|
+
|
23
|
+
# Allow configuration by block.
|
24
|
+
def configure
|
25
|
+
# Singleton the config instance.
|
26
|
+
self.configuration ||= Configuration.new
|
27
|
+
|
28
|
+
# Yield the config block.
|
29
|
+
yield(configuration)
|
30
|
+
|
31
|
+
# Return the config.
|
32
|
+
self.configuration
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'hashie/dash'
|
2
|
+
|
3
|
+
module Sorry
|
4
|
+
module Rails
|
5
|
+
#
|
6
|
+
# Configuration class is a glorified hash
|
7
|
+
# for storing settings to be used by the plugin.
|
8
|
+
#
|
9
|
+
class Configuration < Hashie::Dash
|
10
|
+
|
11
|
+
# Define the properties in the config hash.
|
12
|
+
property :page_id
|
13
|
+
# Current user method, defaults to Devise compatible.
|
14
|
+
property :current_user_method, default: :current_user
|
15
|
+
|
16
|
+
# Property accessors for the subscriber.
|
17
|
+
property :email_method, default: :email
|
18
|
+
property :component_ids_method, default: :component_ids
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sorry
|
2
|
+
module Rails
|
3
|
+
#
|
4
|
+
# Railtie loads the script tag helper into
|
5
|
+
# action view so we can inject the JS into
|
6
|
+
# the host applications templates.
|
7
|
+
#
|
8
|
+
class Railtie < ::Rails::Railtie
|
9
|
+
|
10
|
+
# On initialization.
|
11
|
+
initializer 'sorry-rails' do
|
12
|
+
# Action view helpers.
|
13
|
+
ActiveSupport.on_load :action_view do
|
14
|
+
# Include the script tag helper.
|
15
|
+
include Sorry::Rails::ScriptTagHelper
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
module Sorry
|
5
|
+
module Rails
|
6
|
+
#
|
7
|
+
# An ActionView helper which generates the JavaScript
|
8
|
+
# includes required for the plugin to work.
|
9
|
+
#
|
10
|
+
module ScriptTagHelper
|
11
|
+
|
12
|
+
# Base rails helpers we utilize.
|
13
|
+
include ActionView::Context
|
14
|
+
include ActionView::Helpers::AssetTagHelper
|
15
|
+
include ActionView::Helpers::JavaScriptHelper
|
16
|
+
#
|
17
|
+
# Generate the Sorry Website Plugin script
|
18
|
+
# tag to display status notices to the user
|
19
|
+
# and register them as a subscriber.
|
20
|
+
#
|
21
|
+
def sorry_script_tag(options = {})
|
22
|
+
# Merge configuration in options.
|
23
|
+
options.reverse_merge!(Sorry::Rails.configuration)
|
24
|
+
|
25
|
+
# Include the payload tag and the include
|
26
|
+
# tags for the plugin.
|
27
|
+
safe_join([sorry_script_payload_tag(options), sorry_script_include_tag(options)])
|
28
|
+
end
|
29
|
+
|
30
|
+
def sorry_script_include_tag(options)
|
31
|
+
# Build the JavaScript tag for the plugin include.
|
32
|
+
# Use the latest JS version defined in the plugin.
|
33
|
+
javascript_include_tag "https://code.sorryapp.com/status-bar/#{Sorry::Rails::PLUGIN_VERSION}/status-bar.min.js",
|
34
|
+
# Define the pages identity.
|
35
|
+
data: { for: options.fetch('page_id') },
|
36
|
+
# Load asynchronously.
|
37
|
+
async: true
|
38
|
+
end
|
39
|
+
|
40
|
+
def sorry_script_payload_tag(options)
|
41
|
+
# Get the method name
|
42
|
+
current_user_method = options.fetch('current_user_method')
|
43
|
+
|
44
|
+
# See if the current user is signed in, so we can
|
45
|
+
# include them as a subscriber.
|
46
|
+
if respond_to?(current_user_method) && send(current_user_method).present?
|
47
|
+
# Get the current user.
|
48
|
+
current_request_user = send(current_user_method)
|
49
|
+
|
50
|
+
# Serialize the user into a subscriber payload.
|
51
|
+
subscriber_payload = SubscriberSerializer.new(current_request_user).to_json
|
52
|
+
|
53
|
+
# We have a user method, let's include the JS payload
|
54
|
+
# object for them as a subscriber.
|
55
|
+
javascript_tag id: 'sorry-subscriber-data' do
|
56
|
+
# Include the subscriber payload on the window.
|
57
|
+
"window.SorryAPIOptions = { \"subscriber\": #{subscriber_payload} };".html_safe
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Sorry
|
4
|
+
module Rails
|
5
|
+
#
|
6
|
+
# Serializer class takes the applications current_user
|
7
|
+
# and converts it into a safe JSON payload to be included
|
8
|
+
# in the plugin and sent to Sorry as a subscriber.
|
9
|
+
#
|
10
|
+
class SubscriberSerializer
|
11
|
+
|
12
|
+
# Define serializeable fields.
|
13
|
+
SERIALIZEABLE_ATTRIBUTES = %i[email component_ids].freeze
|
14
|
+
|
15
|
+
#
|
16
|
+
# Decorator which receives the current user
|
17
|
+
# model and safely converts them into JSON
|
18
|
+
# for adding to the payload.
|
19
|
+
#
|
20
|
+
|
21
|
+
def initialize(current_user)
|
22
|
+
# Set user instance.
|
23
|
+
@current_user = current_user
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json(serializeable_attributes = SERIALIZEABLE_ATTRIBUTES)
|
27
|
+
# Check the serializeable attributes are allowed.
|
28
|
+
serializeable_attributes!(serializeable_attributes)
|
29
|
+
|
30
|
+
# Start with an empty attributes hash
|
31
|
+
attributes = {}
|
32
|
+
|
33
|
+
# Loop over the collection or attributes
|
34
|
+
# we can serialize.
|
35
|
+
serializeable_attributes.each do |attribute|
|
36
|
+
# Add this attribute to the hash.
|
37
|
+
attributes[attribute] = attribute_value(attribute)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Serialize the collection
|
41
|
+
# remove any attributes not serialized.
|
42
|
+
attributes.compact.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# The value of the attribute can come
|
47
|
+
# from a proc, or from a custom field, or
|
48
|
+
# by looking for a direct attribute on the user.
|
49
|
+
#
|
50
|
+
def attribute_value(attribute)
|
51
|
+
# Get the configured method for the attribute.
|
52
|
+
attribute_method = Sorry::Rails.configuration.fetch("#{attribute}_method")
|
53
|
+
|
54
|
+
# See if the method is a proc.
|
55
|
+
if attribute_method.respond_to?(:call)
|
56
|
+
# It's a proc, call it with the model.
|
57
|
+
attribute_method.call(@current_user)
|
58
|
+
# See if the instance responds to it.
|
59
|
+
elsif @current_user.respond_to?(attribute_method)
|
60
|
+
# It's a method name, invoke
|
61
|
+
# the method on the model.
|
62
|
+
@current_user.public_send(attribute_method)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Check attributes are serializeable and throw an exception
|
68
|
+
# if they're not included in SERIALIZEABLE_ATTRIBUTES
|
69
|
+
#
|
70
|
+
def serializeable_attributes!(serializeable_attributes)
|
71
|
+
# See if the attributes are serializeable.
|
72
|
+
unless serializeable_attributes?(serializeable_attributes)
|
73
|
+
# They're not, so throw an error.
|
74
|
+
raise UnserializableAttributeError, "The attributes (#{(serializeable_attributes - SERIALIZEABLE_ATTRIBUTES)}) are not included in the approved list."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Determine if a collection of attribute names
|
80
|
+
# can be serialized or not.
|
81
|
+
#
|
82
|
+
def serializeable_attributes?(serializeable_attributes)
|
83
|
+
# Check that all appear in SERIALIZEABLE_ATTRIBUTES
|
84
|
+
(serializeable_attributes - SERIALIZEABLE_ATTRIBUTES).empty?
|
85
|
+
end
|
86
|
+
|
87
|
+
# Custom error for when someone asks to serialize an
|
88
|
+
# attribute not listed in SERIALIZEABLE_ATTRIBUTES.
|
89
|
+
class UnserializableAttributeError < StandardError; end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/sorry-rails.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'sorry/rails/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'sorry-rails'
|
7
|
+
spec.version = Sorry::Rails::VERSION
|
8
|
+
spec.authors = ['Robert Rawlins']
|
9
|
+
spec.email = ['robert@sorryapp.com']
|
10
|
+
|
11
|
+
spec.summary = 'Add the Sorry™ website plugin to your Rails application.'
|
12
|
+
spec.description = 'Display status updates from your Sorry™ status page to your users, and affectively customize which notices each user should see.'
|
13
|
+
spec.homepage = 'https://github.com/sorry-app/sorry-rails'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
|
18
|
+
spec.add_development_dependency 'bundler'
|
19
|
+
spec.add_development_dependency 'faker'
|
20
|
+
spec.add_development_dependency 'rake'
|
21
|
+
spec.add_development_dependency 'rspec'
|
22
|
+
spec.add_development_dependency 'rspec-html-matchers'
|
23
|
+
spec.add_development_dependency 'rubocop'
|
24
|
+
|
25
|
+
spec.add_dependency 'actionview'
|
26
|
+
spec.add_dependency 'activesupport'
|
27
|
+
spec.add_dependency 'hashie', '~> 2.0'
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
RSpec.describe Sorry::Rails::Configuration do
|
2
|
+
|
3
|
+
# Get an instance for testing.
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe '#current_user_method' do
|
7
|
+
# Get the current user method.
|
8
|
+
subject { super().current_user_method }
|
9
|
+
|
10
|
+
context 'by default' do
|
11
|
+
# Check the Devise focussed default.
|
12
|
+
it { is_expected.to eq(:current_user) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
RSpec.describe Sorry::Rails::ScriptTagHelper, type: :helper do
|
2
|
+
|
3
|
+
# Include the helper methods for testing.
|
4
|
+
include described_class
|
5
|
+
|
6
|
+
# Mock a page identity to be used.
|
7
|
+
let(:page_id) { Faker::Lorem.characters(8) }
|
8
|
+
|
9
|
+
describe '#sorry_script_tag' do
|
10
|
+
# Get the resulting tag for testing.
|
11
|
+
subject { sorry_script_tag('page_id' => page_id) }
|
12
|
+
|
13
|
+
# Expect string return.
|
14
|
+
it { is_expected.to be_a(String) }
|
15
|
+
it { is_expected.to be_html_safe }
|
16
|
+
it {
|
17
|
+
# Expect an asynchronous JavaScript tag.
|
18
|
+
is_expected.to have_tag('script[async]', with: {
|
19
|
+
# Pointing at the latest version.
|
20
|
+
src: "https://code.sorryapp.com/status-bar/#{Sorry::Rails::PLUGIN_VERSION}/status-bar.min.js",
|
21
|
+
# With the configured page identity.
|
22
|
+
'data-for': page_id
|
23
|
+
})
|
24
|
+
}
|
25
|
+
|
26
|
+
describe 'subscriber_payload' do
|
27
|
+
# The subscribe payload tag id.
|
28
|
+
let(:subscriber_payload_tag) { 'script[id="sorry-subscriber-data"]' }
|
29
|
+
|
30
|
+
context 'without current_user method' do
|
31
|
+
# IT should not include the payload
|
32
|
+
it { is_expected.to_not have_tag(subscriber_payload_tag) }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with current_user method' do
|
36
|
+
# Make the configured user method available, and
|
37
|
+
# have it return the mock user.
|
38
|
+
before(:each) do
|
39
|
+
# Mock the method onto the test class.
|
40
|
+
allow(self).to receive(Sorry::Rails.configuration.current_user_method).and_return(mock_user)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'and no user' do
|
44
|
+
# Mock no user.
|
45
|
+
let(:mock_user) { nil }
|
46
|
+
|
47
|
+
# IT should not include the payload
|
48
|
+
it { is_expected.to_not have_tag(subscriber_payload_tag) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'and user is signed in' do
|
52
|
+
# Mock a user object with an email address
|
53
|
+
# we can pretend is signed in.
|
54
|
+
let(:mock_user) { double('User', email: Faker::Internet.email) }
|
55
|
+
|
56
|
+
# Expect the subscriber payload
|
57
|
+
it 'contains the expect payload tag' do
|
58
|
+
# Mock the JSON payload for the user.
|
59
|
+
subscriber_payload = Sorry::Rails::SubscriberSerializer.new(mock_user).to_json
|
60
|
+
|
61
|
+
# Check for the script tag.
|
62
|
+
is_expected.to have_tag(subscriber_payload_tag) do
|
63
|
+
# Check the serializer JSON payload.
|
64
|
+
with_text(/window.SorryAPIOptions = { \"subscriber\": #{subscriber_payload} };/)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
RSpec.describe Sorry::Rails::SubscriberSerializer do
|
2
|
+
|
3
|
+
# Mock a subscriber / user to serialize
|
4
|
+
# during the tests.
|
5
|
+
let(:current_user) { double('User') }
|
6
|
+
|
7
|
+
describe '#to_json' do
|
8
|
+
# Loop over the attributes to check each.
|
9
|
+
described_class::SERIALIZEABLE_ATTRIBUTES.each do |attribute|
|
10
|
+
# Context for this attribute.
|
11
|
+
context "when serializing #{attribute}" do
|
12
|
+
# Get the JSON hash for testing, only include
|
13
|
+
# a single attribute at a time, easier mocking for tests.
|
14
|
+
subject { described_class.new(current_user).to_json([attribute]) }
|
15
|
+
|
16
|
+
context 'the resulting value' do
|
17
|
+
# Get the payload result for testing.
|
18
|
+
subject { JSON.parse(super()).fetch(attribute.to_s) }
|
19
|
+
|
20
|
+
context 'when customized' do
|
21
|
+
# Mock the custom method result.
|
22
|
+
let(:custom_method_result) { Faker::Lorem.sentence }
|
23
|
+
|
24
|
+
context 'when a customer proc' do
|
25
|
+
# Stage the user/config for the method.
|
26
|
+
before(:each) do
|
27
|
+
# Allow the user to receive the method.
|
28
|
+
allow(current_user).to receive(:value_through_proc).and_return(custom_method_result)
|
29
|
+
|
30
|
+
# Mock the method as a Proc.
|
31
|
+
Sorry::Rails.configuration.send(:"#{attribute}_method=", proc { |user| user.value_through_proc })
|
32
|
+
end
|
33
|
+
|
34
|
+
# Expect the custom result.
|
35
|
+
it { is_expected.to eq(custom_method_result) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when a custom method name' do
|
39
|
+
# Mock a custom method name.
|
40
|
+
let(:custom_method_name) { Faker::Lorem.word.to_sym }
|
41
|
+
|
42
|
+
# Stage the user/config for the method.
|
43
|
+
before(:each) do
|
44
|
+
# Allow the user to receive the method.
|
45
|
+
allow(current_user).to receive(custom_method_name).and_return(custom_method_result)
|
46
|
+
|
47
|
+
# Add the method to the config.
|
48
|
+
Sorry::Rails.configuration.send(:"#{attribute}_method=", custom_method_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Expect the custom result.
|
52
|
+
it { is_expected.to eq(custom_method_result) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when the default method' do
|
57
|
+
# Mock the attribute method on the model.
|
58
|
+
before(:each) { allow(current_user).to receive(attribute).and_return(Faker::Lorem.sentence) }
|
59
|
+
|
60
|
+
# Is the users own attribute.
|
61
|
+
it { is_expected.to eq(current_user.send(attribute)) }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when no matching method on model' do
|
65
|
+
# Remove the method from the model.
|
66
|
+
before(:each) { allow(current_user).to receive(:respond_to?).with(attribute).and_return(false) }
|
67
|
+
|
68
|
+
it 'does not include the attribute' do
|
69
|
+
# Expect no matching key in the hash.
|
70
|
+
expect { subject }.to raise_error(KeyError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when passed non-serializeable attribute name' do
|
78
|
+
# Ask for JSON with a random attribute name not
|
79
|
+
# found in the SERIALIZEABLE_ATTRIBUTES constant.
|
80
|
+
subject { described_class.new(current_user).to_json([Faker::Lorem.word]) }
|
81
|
+
|
82
|
+
it 'raises a custom error' do
|
83
|
+
# Expect an error to be thrown.
|
84
|
+
expect { subject }.to raise_error(Sorry::Rails::SubscriberSerializer::UnserializableAttributeError)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
RSpec.describe Sorry::Rails do
|
2
|
+
|
3
|
+
describe '#configure' do
|
4
|
+
# Mock some detailed to pass to the config.
|
5
|
+
let(:page_id) { Faker::Lorem.characters(8) }
|
6
|
+
|
7
|
+
context 'when passed a block' do
|
8
|
+
subject do
|
9
|
+
# Invoke the configure with
|
10
|
+
# a block, setting a mock Page ID.
|
11
|
+
described_class.configure do |config|
|
12
|
+
# Set the mock page identity.
|
13
|
+
config.page_id = page_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Check a configuration instance records.
|
18
|
+
it { is_expected.to be_a(described_class::Configuration) }
|
19
|
+
|
20
|
+
describe 'the configured page_id' do
|
21
|
+
# Get the page if for testing.
|
22
|
+
subject { super().page_id }
|
23
|
+
|
24
|
+
# Is the one passed in.
|
25
|
+
it { is_expected.to eq(page_id) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#configuration' do
|
31
|
+
# Get the configuration for testing.
|
32
|
+
subject { described_class.configuration }
|
33
|
+
|
34
|
+
context 'when previously configured' do
|
35
|
+
# Mock the configuration.
|
36
|
+
let(:configuration) { described_class::Configuration.new }
|
37
|
+
|
38
|
+
# Put the configuration in place.
|
39
|
+
before(:each) { described_class.configuration = configuration }
|
40
|
+
|
41
|
+
# Returns a config class.
|
42
|
+
it { is_expected.to be(configuration) }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when not configured' do
|
46
|
+
# Put the configuration in place.
|
47
|
+
before(:each) { described_class.configuration = nil }
|
48
|
+
|
49
|
+
# It has not config.
|
50
|
+
it { is_expected.to be_nil }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'version number' do
|
55
|
+
# Get version for testing.
|
56
|
+
subject { described_class::VERSION }
|
57
|
+
|
58
|
+
# Should be present.
|
59
|
+
it { is_expected.not_to be_nil }
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'faker'
|
3
|
+
require 'sorry/rails'
|
4
|
+
require 'rspec-html-matchers'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
# Enable flags like --only-failures and --next-failure
|
8
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
9
|
+
|
10
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
11
|
+
config.disable_monkey_patching!
|
12
|
+
|
13
|
+
config.expect_with :rspec do |c|
|
14
|
+
# Use more modern syntax.
|
15
|
+
c.syntax = :expect
|
16
|
+
end
|
17
|
+
|
18
|
+
# Reset config singleton after each test.
|
19
|
+
config.around(:each) do |example|
|
20
|
+
# Create a default plugin config.
|
21
|
+
Sorry::Rails.configuration ||= Sorry::Rails::Configuration.new
|
22
|
+
|
23
|
+
# Run the examples.
|
24
|
+
example.run
|
25
|
+
|
26
|
+
# Remove the config.
|
27
|
+
Sorry::Rails.configuration = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Include have_tag matchers.
|
31
|
+
config.include RSpecHtmlMatchers
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorry-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Rawlins
|
@@ -143,7 +143,28 @@ email:
|
|
143
143
|
executables: []
|
144
144
|
extensions: []
|
145
145
|
extra_rdoc_files: []
|
146
|
-
files:
|
146
|
+
files:
|
147
|
+
- ".gitignore"
|
148
|
+
- ".rspec"
|
149
|
+
- ".rubocop.yml"
|
150
|
+
- ".travis.yml"
|
151
|
+
- Gemfile
|
152
|
+
- Gemfile.lock
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- lib/sorry/rails.rb
|
157
|
+
- lib/sorry/rails/configuration.rb
|
158
|
+
- lib/sorry/rails/railtie.rb
|
159
|
+
- lib/sorry/rails/script_tag_helper.rb
|
160
|
+
- lib/sorry/rails/subscriber_serializer.rb
|
161
|
+
- lib/sorry/rails/version.rb
|
162
|
+
- sorry-rails.gemspec
|
163
|
+
- spec/sorry/rails/configuration_spec.rb
|
164
|
+
- spec/sorry/rails/script_tag_helper_spec.rb
|
165
|
+
- spec/sorry/rails/subscriber_serializer_spec.rb
|
166
|
+
- spec/sorry/rails_spec.rb
|
167
|
+
- spec/spec_helper.rb
|
147
168
|
homepage: https://github.com/sorry-app/sorry-rails
|
148
169
|
licenses:
|
149
170
|
- MIT
|