web47core 0.0.9 → 0.0.10
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 +4 -4
- data/Gemfile.lock +16 -4
- data/README.md +37 -33
- data/config/locales/en.yml +11 -0
- data/lib/app/models/concerns/app47_logger.rb +1 -1
- data/lib/app/models/concerns/core_system_configuration.rb +5 -1
- data/lib/app/models/concerns/email_able.rb +84 -0
- data/lib/app/models/concerns/search_able.rb +100 -0
- data/lib/app/models/concerns/standard_model.rb +2 -2
- data/lib/app/models/concerns/time_zone_able.rb +49 -0
- data/lib/app/models/notification.rb +3 -3
- data/lib/app/models/redis_configuration.rb +137 -0
- data/lib/app/models/slack_notification.rb +14 -30
- data/lib/app/models/sms_notification.rb +5 -7
- data/lib/app/models/smtp_configuration.rb +14 -89
- data/lib/web47core.rb +4 -0
- data/test/fixtures/redis/host.yml +5 -0
- data/test/fixtures/redis/options.yml +8 -0
- data/test/fixtures/redis/sentinel.yml +8 -0
- data/test/fixtures/redis/url.yml +2 -0
- data/test/models/concerns/email_able_test.rb +145 -0
- data/test/models/concerns/search_able_test.rb +80 -0
- data/test/models/concerns/standard_model_test.rb +62 -1
- data/test/models/concerns/time_zone_able_test.rb +77 -0
- data/test/models/redis_configuration_test.rb +86 -0
- data/test/models/slack_notification_test.rb +41 -0
- data/test/models/sms_notification_test.rb +69 -0
- data/test/models/smtp_configuration_test.rb +66 -0
- data/test/test_helper.rb +5 -1
- data/web47core.gemspec +7 -4
- metadata +87 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97cb8fc1a923a3c6a911a319404529ef67ad9de4d1f20d6c90b088f99d8e366a
|
4
|
+
data.tar.gz: 53ce4e9da8a4279b4bc4eb3ffb7a0f58b9ec1844c6f1f42adeed9d3e572f3f7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3598aefea3e3b1411e4e4c60c1096129ba90fc5d33ae2c0907762d223ce234f3a4c56e50d28b2dc218a82544a51e9003bc20e0bf4f68bff70d0e767667887ad
|
7
|
+
data.tar.gz: 773f67eac21317c9da0c286c1071383f864b42f4a1d36dcef624bf3968dedf40d96452f9a3eb252057fb805dd6ce66c4302746e72838a4fcd856d30c23e95563
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
web47core (0.0.
|
4
|
+
web47core (0.0.10)
|
5
5
|
activesupport (~> 5.0)
|
6
6
|
aws-sdk-ec2 (> 1.140, <= 1.160)
|
7
7
|
delayed_job_mongoid (~> 2.3)
|
8
|
+
email_format
|
8
9
|
haml
|
9
10
|
jwt
|
10
11
|
liquid
|
@@ -13,6 +14,8 @@ PATH
|
|
13
14
|
redis (~> 4.1)
|
14
15
|
redis-rails (> 5, < 6)
|
15
16
|
rest-client (>= 0, <= 2.1.0)
|
17
|
+
twilio-ruby (>= 3.0, <= 4.13)
|
18
|
+
tzinfo
|
16
19
|
|
17
20
|
GEM
|
18
21
|
remote: https://rubygems.org/
|
@@ -63,7 +66,7 @@ GEM
|
|
63
66
|
ansi (1.5.0)
|
64
67
|
arel (9.0.0)
|
65
68
|
aws-eventstream (1.0.3)
|
66
|
-
aws-partitions (1.
|
69
|
+
aws-partitions (1.289.0)
|
67
70
|
aws-sdk-core (3.92.0)
|
68
71
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
69
72
|
aws-partitions (~> 1, >= 1.239.0)
|
@@ -99,6 +102,10 @@ GEM
|
|
99
102
|
docile (1.3.2)
|
100
103
|
domain_name (0.5.20190701)
|
101
104
|
unf (>= 0.0.5, < 1.0.0)
|
105
|
+
email_format (1.0.0)
|
106
|
+
activemodel
|
107
|
+
email_regex
|
108
|
+
email_regex (0.0.1)
|
102
109
|
erubi (1.9.0)
|
103
110
|
factory_bot (5.1.1)
|
104
111
|
activesupport (>= 4.2.0)
|
@@ -119,7 +126,7 @@ GEM
|
|
119
126
|
concurrent-ruby (~> 1.0)
|
120
127
|
jmespath (1.4.0)
|
121
128
|
json (2.3.0)
|
122
|
-
jwt (
|
129
|
+
jwt (1.5.6)
|
123
130
|
liquid (4.0.3)
|
124
131
|
listen (3.2.1)
|
125
132
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
@@ -167,6 +174,7 @@ GEM
|
|
167
174
|
mongoid-compatibility (0.5.1)
|
168
175
|
activesupport
|
169
176
|
mongoid (>= 2.0)
|
177
|
+
multi_json (1.14.1)
|
170
178
|
netrc (0.11.0)
|
171
179
|
nio4r (2.5.2)
|
172
180
|
nokogiri (1.10.9)
|
@@ -252,6 +260,10 @@ GEM
|
|
252
260
|
thor (1.0.1)
|
253
261
|
thread_safe (0.3.6)
|
254
262
|
tilt (2.0.10)
|
263
|
+
twilio-ruby (4.13.0)
|
264
|
+
builder (>= 2.1.2)
|
265
|
+
jwt (~> 1.0)
|
266
|
+
multi_json (>= 1.3.0)
|
255
267
|
tzinfo (1.2.6)
|
256
268
|
thread_safe (~> 0.1)
|
257
269
|
unf (0.1.4)
|
@@ -284,7 +296,7 @@ DEPENDENCIES
|
|
284
296
|
minitest-reporters
|
285
297
|
mocha
|
286
298
|
shoulda (= 3.6.0)
|
287
|
-
shoulda-matchers
|
299
|
+
shoulda-matchers
|
288
300
|
simplecov (= 0.16.1)
|
289
301
|
test-unit
|
290
302
|
vcr
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# web47core
|
2
2
|
Core components used commonly among all web apps for both App47 and RedMonocle
|
3
3
|
|
4
|
+
## We don't need no sticking badges
|
5
|
+
|
6
|
+
* [](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=App47/web47core&utm_campaign=Badge_Grade)
|
7
|
+
* [](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=App47/web47core&utm_campaign=Badge_Coverage)
|
8
|
+
|
4
9
|
## Requirements
|
5
10
|
|
6
11
|
* Ruby 2.4.1
|
@@ -27,62 +32,62 @@ bundle install --path vendor
|
|
27
32
|
```
|
28
33
|
The `--path vendor` simply puts all gem files in a `vendor` directory.
|
29
34
|
|
30
|
-
|
35
|
+
## Development
|
31
36
|
|
32
37
|
Your `RubyMine` environment should be setup now, however to verify all is well, please run the test suite
|
33
38
|
```
|
34
39
|
bundle exec rake test
|
35
40
|
```
|
36
41
|
|
37
|
-
|
42
|
+
## Deployment
|
38
43
|
The `web47core` project is a gem that will be deployed via [Ruby Gems](https://rubygems.org). When an update is ready, the following steps should be followed
|
39
44
|
|
40
|
-
1.
|
41
|
-
|
42
|
-
|
45
|
+
1. Build the gem `gem build web47core.gemspec`
|
46
|
+
2. Push the new gem to [Ruby Gems](https://rubygems.org) `gem push web47core-version.gem`
|
47
|
+
3. There may be a delay when using the gem file
|
43
48
|
|
44
|
-
|
45
|
-
|
49
|
+
## Usage
|
50
|
+
### Importing the gem
|
46
51
|
To use the `app47core` gem in a project, first add the gem to your Gemfile in one of two ways
|
47
52
|
|
48
53
|
Using the gem from [Ruby Gems](https://rubygems.org)
|
49
|
-
```
|
54
|
+
```rbenv-gemsets
|
50
55
|
gem 'web47core'
|
51
56
|
```
|
52
57
|
|
53
58
|
If you need the gem immediately or need to pull from development branch, you can use the git repo
|
54
|
-
```
|
59
|
+
```rbenv-gemsets
|
55
60
|
gem 'web47core', git: 'git@github.com:App47/web47core.git', branch: :master
|
56
61
|
```
|
57
62
|
or from the develop branch
|
58
|
-
```
|
63
|
+
```rbenv-gemsets
|
59
64
|
gem 'web47core', git: 'git@github.com:App47/web47core.git', branch: :develop
|
60
65
|
```
|
61
66
|
|
62
67
|
_Please do not ship to production code using the git repo, as the production servers will not have keys to pull from the web47core repo_
|
63
68
|
|
64
|
-
|
65
|
-
1.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
1.
|
80
|
-
|
81
|
-
|
82
|
-
|
69
|
+
### Remove the following files
|
70
|
+
1. `StandardModel` - Includes the common set of includes, Mongoid, Auditable, AutoClearCache, etc.
|
71
|
+
2. `CdnUrl` - Provide CDN URLs of your classes URLs when cdn_url is configured in system configuration
|
72
|
+
3. `AutoClearCache` - Clears your objects cache out of Rails.cache using the object's id
|
73
|
+
4. `Formable` - Consolidated into StandardModel, was used to provide common form operations for mongoid documents
|
74
|
+
5. `EmailNotification`
|
75
|
+
6. `EmailTemplate`
|
76
|
+
7. `Notification`
|
77
|
+
8. `NotificationTemplate`
|
78
|
+
9. `SlackNotification`
|
79
|
+
10. `SmsNotification`
|
80
|
+
11. `Template`
|
81
|
+
|
82
|
+
#### Models
|
83
|
+
##### Concerns
|
84
|
+
1. `StandardModel` - Includes the common set of includes, Mongoid, etc.
|
85
|
+
2. `CoreSystemConfiguration` - Base system configuration to be included in the app's SystemConfiguration Object
|
86
|
+
3. `CoreAccount` - Base account object that is related to notifications and the ilk.
|
87
|
+
##### System Configuration
|
83
88
|
Define a `SystemConfiguration` class in your project and import the core concern.
|
84
89
|
|
85
|
-
```
|
90
|
+
```ruby
|
86
91
|
class SystemConfiguration
|
87
92
|
include StandardModel
|
88
93
|
include CoreSystemConfiguration
|
@@ -91,10 +96,10 @@ class SystemConfiguration
|
|
91
96
|
field :google_sso_client_id, type: String
|
92
97
|
end
|
93
98
|
```
|
94
|
-
|
99
|
+
##### Account
|
95
100
|
Define a `Account` class in your project and import the core concern.
|
96
101
|
|
97
|
-
```
|
102
|
+
```ruby
|
98
103
|
class Account
|
99
104
|
include StandardModel
|
100
105
|
include CoreAccount
|
@@ -103,4 +108,3 @@ class Account
|
|
103
108
|
has_many :users
|
104
109
|
end
|
105
110
|
```
|
106
|
-
|
@@ -63,7 +63,7 @@ module App47Logger
|
|
63
63
|
|
64
64
|
max_frame = -1 if ENV['RACK_ENV'].eql?('test')
|
65
65
|
log_message(level, exception.message)
|
66
|
-
exception.backtrace[0..max_frame].each { |frame| log_message(level, frame) }
|
66
|
+
exception.backtrace[0..max_frame].each { |frame| log_message(level, frame) } if exception.backtrace.present?
|
67
67
|
end
|
68
68
|
|
69
69
|
#
|
@@ -117,6 +117,10 @@ module CoreSystemConfiguration
|
|
117
117
|
def respond_to?(method_name, _include_private = false)
|
118
118
|
SystemConfiguration.fields.include?(method_name)
|
119
119
|
end
|
120
|
+
|
121
|
+
def respond_to_missing?(method_name, _include_private = false)
|
122
|
+
SystemConfiguration.fields.include?(method_name)
|
123
|
+
end
|
120
124
|
# rubocop:enable Style/MethodMissingSuper
|
121
125
|
end
|
122
126
|
|
@@ -208,7 +212,7 @@ module CoreSystemConfiguration
|
|
208
212
|
config = SystemConfiguration.configuration
|
209
213
|
path = if config.zendesk_configured? && user.present?
|
210
214
|
time_now = Time.now.to_i
|
211
|
-
jti = "#{time_now}/#{rand(36
|
215
|
+
jti = "#{time_now}/#{rand(36**64).to_s(36)}"
|
212
216
|
payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
|
213
217
|
jti: jti, # Unique token identifier, helps prevent replay attacks
|
214
218
|
name: user.name,
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#
|
2
|
+
# Objects that emails can be sent too...
|
3
|
+
#
|
4
|
+
module EmailAble
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include App47Logger
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
field :email, type: String
|
11
|
+
field :email_bounced_at, type: Time
|
12
|
+
field :email_bounce_reason, type: String
|
13
|
+
field :unconfirmed_email, type: String
|
14
|
+
field :email_enabled, type: Boolean, default: true
|
15
|
+
#
|
16
|
+
# Validations
|
17
|
+
#
|
18
|
+
validates :email, presence: true, email_format: { strict: true }
|
19
|
+
#
|
20
|
+
# Callbacks
|
21
|
+
#
|
22
|
+
before_validation :downcase_email
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Support legacy apps with old name
|
28
|
+
#
|
29
|
+
def email_bounce_date(date = nil)
|
30
|
+
self.email_bounced_at = date if date.present?
|
31
|
+
email_bounced_at
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Is this a valid email to send to?
|
36
|
+
#
|
37
|
+
def valid_email?
|
38
|
+
email_enabled? && !email_bounced?
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Has the email bounced?
|
43
|
+
#
|
44
|
+
def email_bounced?
|
45
|
+
email_bounced_at.present?
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Set the email to a bounced status with the given reason
|
50
|
+
#
|
51
|
+
def bounced(reason)
|
52
|
+
set email_bounced_at: Time.now.utc, email_bounce_reason: reason
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Reset the bounced email
|
57
|
+
#
|
58
|
+
def reset_bounce_status
|
59
|
+
return unless SystemConfiguration.mailgun_configured? && email_bounced?
|
60
|
+
|
61
|
+
reset_url = "https://api.mailgun.net/v3/#{SystemConfiguration.smtp_domain}/bounces/#{CGI.escape(email)}"
|
62
|
+
RestClient.delete(reset_url, user: 'api', password: SystemConfiguration.mailgun_api_key)
|
63
|
+
rescue RestClient::Exception => error
|
64
|
+
log_error "Unable to reset email bounce status: #{inspect}", error
|
65
|
+
ensure
|
66
|
+
set email_bounced_at: nil, email_bounce_reason: nil
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Return the gravatar URL based on email address
|
71
|
+
#
|
72
|
+
def gravatar_url(size = '32', default = 'mm')
|
73
|
+
"https://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}?s=#{size}&d=#{default}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
#
|
79
|
+
# Make sure emails are always downcased
|
80
|
+
#
|
81
|
+
def downcase_email
|
82
|
+
self.email = email.strip.downcase if email.present?
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Public: Add search and sort text to an object
|
5
|
+
#
|
6
|
+
module SearchAble
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :search_text, type: String
|
15
|
+
field :sort_text, type: String
|
16
|
+
#
|
17
|
+
# Call backs
|
18
|
+
#
|
19
|
+
before_validation :update_search_and_sort_text
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Public: Add to class methods a way to search for records with matching
|
25
|
+
# search text.
|
26
|
+
#
|
27
|
+
# Examples
|
28
|
+
#
|
29
|
+
# Model.matching_search_text('some text')
|
30
|
+
# # => <Mongoid::critera>
|
31
|
+
#
|
32
|
+
module ClassMethods
|
33
|
+
def matching_search_text(search_text = nil)
|
34
|
+
(search_text.blank? ? all : where(search_text: /#{search_text.downcase}/)).order(sort_order)
|
35
|
+
end
|
36
|
+
|
37
|
+
def sort_order
|
38
|
+
new.sort_fields.collect { |field| [field, 1] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Internal: Update the search and sort text
|
44
|
+
#
|
45
|
+
# Call before validation to update, changes are persisted with the object.
|
46
|
+
#
|
47
|
+
def update_search_and_sort_text
|
48
|
+
return if destroyed?
|
49
|
+
|
50
|
+
update_text(search_fields, :search_text)
|
51
|
+
update_text(sort_fields, :sort_text)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Internal: Update the search text
|
56
|
+
#
|
57
|
+
# Examples
|
58
|
+
#
|
59
|
+
# update_search_text
|
60
|
+
#
|
61
|
+
def update_text(fields, field_to_update)
|
62
|
+
items = fields.reject { |field| send(field.to_sym).blank? }.collect do |field|
|
63
|
+
value = send(field.to_sym)
|
64
|
+
if value.is_a? String
|
65
|
+
value.downcase
|
66
|
+
elsif value.is_a? Array
|
67
|
+
value.empty? ? nil : value.join(' ').downcase
|
68
|
+
end
|
69
|
+
end
|
70
|
+
send "#{field_to_update}=", items.compact.join(' ')
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Internal: Which fields to add to search text
|
75
|
+
#
|
76
|
+
# Examples
|
77
|
+
#
|
78
|
+
# search_fields
|
79
|
+
# # => ['name', 'email', 'code']
|
80
|
+
#
|
81
|
+
# Return which fields should be added to search
|
82
|
+
#
|
83
|
+
def search_fields
|
84
|
+
%w[name]
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Internal: Which fields to add to sort on
|
89
|
+
#
|
90
|
+
# Examples
|
91
|
+
#
|
92
|
+
# sort_fields
|
93
|
+
# # => ['name', 'email']
|
94
|
+
#
|
95
|
+
# Return which fields should be added to sort
|
96
|
+
#
|
97
|
+
def sort_fields
|
98
|
+
search_fields
|
99
|
+
end
|
100
|
+
end
|
@@ -101,7 +101,7 @@ module StandardModel
|
|
101
101
|
super(remove_blank_secure_fields(params))
|
102
102
|
end
|
103
103
|
|
104
|
-
alias
|
104
|
+
alias update_attributes update
|
105
105
|
|
106
106
|
#
|
107
107
|
# Remove updates for secure fields that come across as blank to start with and get removed on update
|
@@ -110,7 +110,7 @@ module StandardModel
|
|
110
110
|
super(remove_blank_secure_fields(params))
|
111
111
|
end
|
112
112
|
|
113
|
-
alias
|
113
|
+
alias update_attributes! update!
|
114
114
|
|
115
115
|
#
|
116
116
|
# List of secure fields, to add fields in concrete class, simply override this method
|