bmc 1.0.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 +7 -0
- data/CHANGELOG.md +10 -0
- data/README.md +28 -0
- data/Rakefile +41 -0
- data/app/assets/config/bmc_manifest.js +2 -0
- data/app/controllers/bmc/application_controller.rb +3 -0
- data/app/controllers/bmc/filters_controller.rb +38 -0
- data/app/controllers/concerns/bmc/api_controller_concern.rb +69 -0
- data/app/controllers/concerns/bmc/back_url_concern.rb +26 -0
- data/app/emails/bmc/email.rb +113 -0
- data/app/filters/bmc/filter/by_date.rb +6 -0
- data/app/filters/bmc/filter/by_date_begin.rb +7 -0
- data/app/filters/bmc/filter/by_date_end.rb +7 -0
- data/app/filters/bmc/filter/by_date_or_datetime_period.rb +56 -0
- data/app/filters/bmc/filter/by_date_period.rb +5 -0
- data/app/filters/bmc/filter/by_datetime_period.rb +5 -0
- data/app/filters/bmc/filter/by_key_value.rb +27 -0
- data/app/filters/bmc/filter/by_key_values.rb +13 -0
- data/app/filters/bmc/filter.rb +86 -0
- data/app/forms/bmc/mini_form_object.rb +29 -0
- data/app/helpers/bmc/all_helpers.rb +15 -0
- data/app/helpers/bmc/bootstrap_helper.rb +63 -0
- data/app/helpers/bmc/button_helper.rb +150 -0
- data/app/helpers/bmc/filters_helper.rb +16 -0
- data/app/helpers/bmc/font_awesome_helper.rb +67 -0
- data/app/helpers/bmc/form_helper.rb +24 -0
- data/app/helpers/bmc/i18n_helper.rb +5 -0
- data/app/helpers/bmc/link_helper.rb +42 -0
- data/app/helpers/bmc/pagination_helper.rb +22 -0
- data/app/helpers/bmc/routes_helper.rb +24 -0
- data/app/helpers/bmc/sorting_helper.rb +55 -0
- data/app/helpers/bmc/text_helper.rb +127 -0
- data/app/helpers/h.rb +3 -0
- data/app/jobs/concerns/bmc/setup_job_concern.rb +35 -0
- data/app/libs/bmc/collection_update.rb +38 -0
- data/app/libs/bmc/fcm/notifier.rb +32 -0
- data/app/libs/bmc/fcm/request.rb +54 -0
- data/app/libs/bmc/mini_model_serializer/serialize.rb +30 -0
- data/app/libs/bmc/mini_model_serializer/serializer.rb +23 -0
- data/app/libs/bmc/monkey.rb +39 -0
- data/app/libs/bmc/sortable_uuid_generator.rb +11 -0
- data/app/libs/bmc/token_generator.rb +37 -0
- data/app/mailers/bmc/application_mailer.rb +2 -0
- data/app/mailers/bmc/generic_mailer.rb +9 -0
- data/app/models/concerns/bmc/active_record_uuid_concern.rb +17 -0
- data/app/models/concerns/bmc/default_values_concern.rb +14 -0
- data/app/models/concerns/bmc/model_i18n.rb +56 -0
- data/app/models/concerns/bmc/model_to_s.rb +9 -0
- data/app/models/concerns/bmc/pluck_distinct.rb +13 -0
- data/app/models/concerns/bmc/pluck_to_hash.rb +9 -0
- data/app/models/concerns/bmc/polymorphic_id.rb +37 -0
- data/app/models/concerns/bmc/search.rb +28 -0
- data/app/models/concerns/bmc/timestamp_helpers.rb +17 -0
- data/app/serializers/bmc/serializers/base.rb +63 -0
- data/app/serializers/bmc/serializers/xlsx.rb +38 -0
- data/app/serializers/bmc/serializers.rb +2 -0
- data/app/sms/bmc/sms/application_sms.rb +42 -0
- data/app/sms/bmc/sms/message.rb +25 -0
- data/app/sms/bmc/sms/strategies/amazon_sns.rb +48 -0
- data/app/sms/bmc/sms/strategies/base.rb +15 -0
- data/app/sms/bmc/sms/strategies/test.rb +10 -0
- data/app/sms/bmc/sms.rb +35 -0
- data/app/sorters/bmc/sorter.rb +26 -0
- data/app/views/bmc/search/_form.html.slim +1 -0
- data/app/views/bmc/search/_form_bs3.html.slim +13 -0
- data/app/views/bmc/search/_form_bs4.html.slim +9 -0
- data/app/views/bmc/search/_form_bs5.html.slim +8 -0
- data/app/views/kaminari/bootstrap4/_first_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_gap.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_last_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_next_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_page.html.slim +6 -0
- data/app/views/kaminari/bootstrap4/_paginator.html.slim +12 -0
- data/app/views/kaminari/bootstrap4/_prev_page.html.slim +2 -0
- data/config/locales/actions.en.yml +76 -0
- data/config/locales/actions.fr.yml +76 -0
- data/config/locales/attributes.en.yml +108 -0
- data/config/locales/attributes.fr.yml +108 -0
- data/config/locales/common.en.yml +39 -0
- data/config/locales/common.fr.yml +39 -0
- data/config/locales/dates.fr.yml +8 -0
- data/config/locales/errors.en.yml +5 -0
- data/config/locales/errors.fr.yml +5 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20000101000000_enable_bmc_extensions.rb +7 -0
- data/lib/bmc/active_model_custom_error_messages.rb +23 -0
- data/lib/bmc/active_model_type_cast.rb +52 -0
- data/lib/bmc/config.rb +19 -0
- data/lib/bmc/core_and_rails_ext.rb +3 -0
- data/lib/bmc/engine.rb +36 -0
- data/lib/bmc/engine_file.rb +10 -0
- data/lib/bmc/errors_middleware.rb +50 -0
- data/lib/bmc/form_back_url.rb +18 -0
- data/lib/bmc/version.rb +3 -0
- data/lib/bmc.rb +5 -0
- data/lib/tasks/bmc_tasks.rake +4 -0
- metadata +236 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1f44e62a2b31e04cc714d58d89b5bf5752a26cb99b2e9013da86a5a7c7b52a15
|
|
4
|
+
data.tar.gz: 71693371c8912830ecbdbde9a6582c357dd0fdf63c8233ab1fc8c1e52723e828
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4a865f824ca4aa26d2cb0d0e92360d4aa88f7a0e8cfa7a10d32d35875bfe4a33c53c7cd6f0809da4810c613426b9a56b8d7b3e29fad162f5e816f5137cbe7496
|
|
7
|
+
data.tar.gz: a3febb518d32c8bccbb22b414fad2bbbe3473c4689b66d76cf7836b5bde532be34e0b22485c344dd24c59d70b0522e214357e50379ee3b60b1aaf78b44946748
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# BMC
|
|
2
|
+
Short description and motivation.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
How to use my plugin.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'bmc'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install bmc
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Contributing
|
|
25
|
+
Contribution directions go here.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'BMC'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
load 'rails/tasks/statistics.rake'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
require 'bundler/gem_tasks'
|
|
26
|
+
|
|
27
|
+
require 'rake/testtask'
|
|
28
|
+
|
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
|
30
|
+
t.libs << 'lib'
|
|
31
|
+
t.libs << 'test'
|
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
|
33
|
+
t.verbose = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
task default: :test
|
|
38
|
+
|
|
39
|
+
task "assets:precompile" do
|
|
40
|
+
Rake::Task["app:assets:precompile"].invoke
|
|
41
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class BMC::FiltersController < BMC::ApplicationController
|
|
2
|
+
skip_before_action :verify_authenticity_token, raise: false
|
|
3
|
+
skip_before_action :authenticate_user!, raise: false
|
|
4
|
+
skip_after_action :verify_authorized, raise: false
|
|
5
|
+
skip_after_action :verify_policy_scoped, raise: false
|
|
6
|
+
|
|
7
|
+
def create
|
|
8
|
+
filters = BMC::Filter.new(cookies)
|
|
9
|
+
new_filters = params.fetch(:filters, {}).permit!.to_h
|
|
10
|
+
filters.merge new_filters
|
|
11
|
+
|
|
12
|
+
# Rewrite cookie with 1 year expiry
|
|
13
|
+
cookies[:filters] = {
|
|
14
|
+
:value => cookies[:filters],
|
|
15
|
+
:expires => 1.year.from_now,
|
|
16
|
+
:path => "/",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
redirect_to back_url
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def back_url
|
|
25
|
+
url = [
|
|
26
|
+
params[:form_url],
|
|
27
|
+
request.referer,
|
|
28
|
+
main_app.try(:root_path),
|
|
29
|
+
"/",
|
|
30
|
+
].find(&:present?)
|
|
31
|
+
|
|
32
|
+
# Delete page param
|
|
33
|
+
base, query_string = url.split("?")
|
|
34
|
+
query_string = query_string.to_s.split("&").delete_if { |p| p.include?("page=") }.join("&")
|
|
35
|
+
query_string = "?#{query_string}" if query_string.present?
|
|
36
|
+
base + query_string
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module BMC::ApiControllerConcern
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def render_json(json = {}, options = {})
|
|
7
|
+
json.reverse_merge!(current_user: current_user)
|
|
8
|
+
options.reverse_merge!(current_user: current_user)
|
|
9
|
+
|
|
10
|
+
json = BMC::MiniModelSerializer::Serialize.call(json, options)
|
|
11
|
+
|
|
12
|
+
render options.merge(json: json)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render_json_error(any_object, options = {})
|
|
16
|
+
if any_object.is_a?(ActiveModel::Validations)
|
|
17
|
+
json = {
|
|
18
|
+
:error => json_error_string_for_model(any_object),
|
|
19
|
+
:model_errors => json_errors_hash_for_model(any_object),
|
|
20
|
+
}
|
|
21
|
+
elsif any_object.is_a?(String)
|
|
22
|
+
json = {error: any_object}
|
|
23
|
+
else
|
|
24
|
+
json = any_object
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
options[:status] ||= :unprocessable_entity
|
|
28
|
+
|
|
29
|
+
render_json(json, options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def json_errors_hash_for_model(object)
|
|
33
|
+
object.errors
|
|
34
|
+
.to_hash
|
|
35
|
+
.map { |a, m| [a, message: m.first, full_message: object.errors.full_message(a, m.first)] }
|
|
36
|
+
.uniq(&:first)
|
|
37
|
+
.to_h
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def json_error_string_for_model(object)
|
|
41
|
+
json_errors_hash_for_model(object).values.pluck(:full_message).join(", ")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_not_found
|
|
45
|
+
render_json_error t("errors.not_found"), status: :not_found
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def render_forbidden
|
|
49
|
+
render_json_error t("errors.forbidden"), status: :forbidden
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def render_unauthorized
|
|
53
|
+
render_json_error t("errors.unauthorized"), status: :unauthorized
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def render_forbidden_or_unauthorized
|
|
57
|
+
current_user ? render_forbidden : render_unauthorized
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
included do |controller|
|
|
61
|
+
if controller < ActionController::Rescue
|
|
62
|
+
if defined?(Pundit::NotAuthorizedError)
|
|
63
|
+
rescue_from Pundit::NotAuthorizedError, with: :render_forbidden_or_unauthorized
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module BMC::BackUrlConcern
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def default_back_url
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def back_url
|
|
10
|
+
url = [
|
|
11
|
+
params[:back_url],
|
|
12
|
+
request.referer,
|
|
13
|
+
default_back_url,
|
|
14
|
+
main_app.try(:root_path),
|
|
15
|
+
"/",
|
|
16
|
+
].find(&:present?)
|
|
17
|
+
|
|
18
|
+
uri = URI.parse(url)
|
|
19
|
+
uri.host = nil
|
|
20
|
+
uri.port = nil
|
|
21
|
+
uri.scheme = nil
|
|
22
|
+
uri.user = nil
|
|
23
|
+
uri.password = nil
|
|
24
|
+
uri.to_s
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
class BMC::Email
|
|
2
|
+
include ActiveModel::Model
|
|
3
|
+
include BMC::ModelToS
|
|
4
|
+
include BMC::ModelI18n
|
|
5
|
+
|
|
6
|
+
validates :to, presence: true
|
|
7
|
+
validates :subject, presence: true
|
|
8
|
+
validates :body, presence: true
|
|
9
|
+
|
|
10
|
+
validate :validate_addr_formats
|
|
11
|
+
|
|
12
|
+
attr_accessor(
|
|
13
|
+
:current_user,
|
|
14
|
+
:from,
|
|
15
|
+
:reply_to,
|
|
16
|
+
:to,
|
|
17
|
+
:cc,
|
|
18
|
+
:subject,
|
|
19
|
+
:body,
|
|
20
|
+
:attachments,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def initialize(*)
|
|
24
|
+
super
|
|
25
|
+
assign_default_values
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def attachment_names
|
|
29
|
+
attachments.keys.join(", ")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validate_and_deliver
|
|
33
|
+
ok = valid?
|
|
34
|
+
deliver if ok
|
|
35
|
+
ok
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def data
|
|
39
|
+
{
|
|
40
|
+
:from => from,
|
|
41
|
+
:reply_to => reply_to,
|
|
42
|
+
:to => to,
|
|
43
|
+
:cc => cc,
|
|
44
|
+
:subject => subject,
|
|
45
|
+
:body => body,
|
|
46
|
+
:attachments => attachments,
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def deliver
|
|
51
|
+
deliver_now
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def deliver_now
|
|
55
|
+
BMC::GenericMailer.generic_email(data).deliver_now
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def deliver_later
|
|
59
|
+
BMC::GenericMailer.generic_email(data).deliver_later
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def assign_default_values
|
|
65
|
+
self.from ||= default_from
|
|
66
|
+
self.reply_to ||= default_reply_to
|
|
67
|
+
self.to ||= default_to
|
|
68
|
+
self.cc ||= default_cc
|
|
69
|
+
self.subject ||= default_subject
|
|
70
|
+
self.body ||= default_body
|
|
71
|
+
self.attachments ||= default_attachments
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def default_from
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def default_reply_to
|
|
78
|
+
"#{current_user} <#{current_user.email}>" if current_user
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def default_to
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def default_cc
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def default_subject
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def default_body
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def default_attachments
|
|
94
|
+
{}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def validate_addr_formats
|
|
98
|
+
validate_addr_formats_for(:from)
|
|
99
|
+
validate_addr_formats_for(:reply_to)
|
|
100
|
+
validate_addr_formats_for(:to)
|
|
101
|
+
validate_addr_formats_for(:cc)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def validate_addr_formats_for(attr)
|
|
105
|
+
require "mail"
|
|
106
|
+
|
|
107
|
+
string = public_send(attr).to_s
|
|
108
|
+
return if string.blank?
|
|
109
|
+
addrs = Mail.new(to: string).to_addrs
|
|
110
|
+
return true if addrs.any? && addrs.all? { |addr| URI::MailTo::EMAIL_REGEXP.match?(addr) }
|
|
111
|
+
errors.add(attr, :invalid)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
class BMC::Filter::ByDateOrDatetimePeriod < BMC::Filter::ByKeyValue
|
|
2
|
+
def initialize(*)
|
|
3
|
+
if instance_of?(BMC::Filter::ByDateOrDatetimePeriod)
|
|
4
|
+
raise "please use FilterStrategyByDatePeriod or FilterStrategyByDatetimePeriod"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def apply(query, value) # rubocop:disable Metrics/MethodLength
|
|
11
|
+
value = value.to_s
|
|
12
|
+
|
|
13
|
+
if value == "today"
|
|
14
|
+
a = now
|
|
15
|
+
b = now
|
|
16
|
+
elsif value == "yesterday"
|
|
17
|
+
a = (now - 1.day)
|
|
18
|
+
b = (now - 1.day)
|
|
19
|
+
elsif value == "this_week"
|
|
20
|
+
a = now.beginning_of_week
|
|
21
|
+
b = now.end_of_week
|
|
22
|
+
elsif value == "this_month"
|
|
23
|
+
a = now.beginning_of_month
|
|
24
|
+
b = now.end_of_month
|
|
25
|
+
elsif value == "this_year"
|
|
26
|
+
a = now.beginning_of_year
|
|
27
|
+
b = now.end_of_year
|
|
28
|
+
elsif value == "last_week"
|
|
29
|
+
a = (now - 1.week).beginning_of_week
|
|
30
|
+
b = (now - 1.week).end_of_week
|
|
31
|
+
elsif value == "last_month"
|
|
32
|
+
a = (now - 1.month).beginning_of_month
|
|
33
|
+
b = (now - 1.month).end_of_month
|
|
34
|
+
elsif value == "last_year"
|
|
35
|
+
a = (now - 1.year).beginning_of_year
|
|
36
|
+
b = (now - 1.year).end_of_year
|
|
37
|
+
elsif (m = value.match(/last\.([0-9]+)\.(days?|weeks?|months?|years?)/))
|
|
38
|
+
a = now - m[1].to_i.public_send(m[2])
|
|
39
|
+
b = now
|
|
40
|
+
else
|
|
41
|
+
return query
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if now.is_a?(Time)
|
|
45
|
+
a = a.beginning_of_day if a
|
|
46
|
+
b = b.end_of_day if b
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
column = column_for(query)
|
|
50
|
+
|
|
51
|
+
query = query.where("#{column} >= ?", a) if a
|
|
52
|
+
query = query.where("#{column} <= ?", b) if b
|
|
53
|
+
|
|
54
|
+
query
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class BMC::Filter::ByKeyValue
|
|
2
|
+
attr_reader :key
|
|
3
|
+
|
|
4
|
+
def initialize(key = nil)
|
|
5
|
+
super()
|
|
6
|
+
@key = key
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def apply(query, value)
|
|
10
|
+
value = true if value == "true"
|
|
11
|
+
value = false if value == "false"
|
|
12
|
+
|
|
13
|
+
column = column_for(query)
|
|
14
|
+
|
|
15
|
+
if value.to_s.in?(%w(nil null))
|
|
16
|
+
query.where("#{column} IS NULL")
|
|
17
|
+
elsif value.to_s.in?(%w(not_nil not_null))
|
|
18
|
+
query.where("#{column} IS NOT NULL")
|
|
19
|
+
else
|
|
20
|
+
query.where("#{column} = ?", value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def column_for(query)
|
|
25
|
+
key.is_a?(Symbol) ? "#{query.model.table_name}.#{key}" : key.to_s
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class BMC::Filter::ByKeyValues < BMC::Filter::ByKeyValue
|
|
2
|
+
def apply(query, value)
|
|
3
|
+
value = value.split if value.is_a?(String)
|
|
4
|
+
value = value.select(&:present?)
|
|
5
|
+
column = column_for(query)
|
|
6
|
+
|
|
7
|
+
if value.any?
|
|
8
|
+
query.where("#{column} IN (?)", value)
|
|
9
|
+
else
|
|
10
|
+
query
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class BMC::Filter
|
|
2
|
+
include ActiveModel::Model
|
|
3
|
+
|
|
4
|
+
STRATEGIES = {}
|
|
5
|
+
|
|
6
|
+
attr_reader :jar
|
|
7
|
+
|
|
8
|
+
def initialize(jar)
|
|
9
|
+
@jar = jar
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def strategies
|
|
13
|
+
self.class::STRATEGIES.with_indifferent_access
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def apply(query)
|
|
17
|
+
strategies.each do |key, strategy|
|
|
18
|
+
value = get(key)
|
|
19
|
+
|
|
20
|
+
next if value.blank?
|
|
21
|
+
|
|
22
|
+
query = strategy.apply(query, value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
return query
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(method, *args)
|
|
29
|
+
if method.to_s.end_with?("=")
|
|
30
|
+
key = method.to_s[0..-2]
|
|
31
|
+
value = args.first
|
|
32
|
+
action = :write
|
|
33
|
+
else
|
|
34
|
+
key = method.to_s
|
|
35
|
+
action = :read
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if strategies.key?(key) && action == :read
|
|
39
|
+
get(key)
|
|
40
|
+
elsif strategies.key?(key) && action == :write
|
|
41
|
+
set(key, value)
|
|
42
|
+
else
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def respond_to_missing?(method, *)
|
|
48
|
+
super || strategies.key?(method.to_s) || strategies.key?(method.to_s.chomp("="))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def read
|
|
52
|
+
JSON.parse jar["filters"].to_s
|
|
53
|
+
rescue JSON::ParserError
|
|
54
|
+
{}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def write(filters)
|
|
58
|
+
jar["filters"] = filters.to_json
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def merge(new_filters)
|
|
62
|
+
write read.merge(new_filters)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def actives_count
|
|
66
|
+
read.count { |k, v| strategies.key?(k.to_s) && v.present? }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def any?
|
|
70
|
+
actives_count.positive?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def empty?
|
|
74
|
+
!any?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def get(key)
|
|
80
|
+
read[key.to_s]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def set(key, value)
|
|
84
|
+
merge(key.to_s => value)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class BMC::MiniFormObject < SimpleDelegator
|
|
2
|
+
include ActiveModel::Validations
|
|
3
|
+
extend Forwardable
|
|
4
|
+
|
|
5
|
+
def valid?
|
|
6
|
+
__getobj__.valid?
|
|
7
|
+
run_validations!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def invalid?
|
|
11
|
+
!valid?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
alias validate valid?
|
|
15
|
+
|
|
16
|
+
def validate!
|
|
17
|
+
valid? || raise_validation_error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def_delegators :__getobj__, :errors
|
|
21
|
+
|
|
22
|
+
def save
|
|
23
|
+
valid? && __getobj__.save
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def save!
|
|
27
|
+
validate! && __getobj__.save!
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module BMC::AllHelpers
|
|
2
|
+
include BMC::BootstrapHelper
|
|
3
|
+
include BMC::ButtonHelper
|
|
4
|
+
include BMC::FiltersHelper
|
|
5
|
+
include BMC::FontAwesomeHelper
|
|
6
|
+
include BMC::FormHelper
|
|
7
|
+
include BMC::I18nHelper
|
|
8
|
+
include BMC::LinkHelper
|
|
9
|
+
include BMC::PaginationHelper
|
|
10
|
+
include BMC::RoutesHelper
|
|
11
|
+
include BMC::SortingHelper
|
|
12
|
+
include BMC::TextHelper
|
|
13
|
+
|
|
14
|
+
extend self
|
|
15
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module BMC::BootstrapHelper
|
|
2
|
+
class << self
|
|
3
|
+
attr_writer :card_classes
|
|
4
|
+
|
|
5
|
+
def card_classes
|
|
6
|
+
@card_classes ||= {
|
|
7
|
+
:card => "card",
|
|
8
|
+
:header => "card-header",
|
|
9
|
+
:body => "card-body",
|
|
10
|
+
:footer => "card-footer",
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def bs_progress_bar(percentage)
|
|
16
|
+
percentage = percentage.round if percentage.respond_to?(:round)
|
|
17
|
+
|
|
18
|
+
tag.div(class: "progress") do
|
|
19
|
+
tag.div(class: "progress-bar", style: "width:#{percentage}%") do
|
|
20
|
+
"#{percentage}%"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def bs_card( # rubocop:disable Metrics/ParameterLists
|
|
26
|
+
header: nil,
|
|
27
|
+
body: true,
|
|
28
|
+
footer: nil,
|
|
29
|
+
card_tag: :div,
|
|
30
|
+
header_tag: :div,
|
|
31
|
+
body_tag: :div,
|
|
32
|
+
footer_tag: :div,
|
|
33
|
+
card_class: nil,
|
|
34
|
+
header_class: nil,
|
|
35
|
+
body_class: nil,
|
|
36
|
+
footer_class: nil,
|
|
37
|
+
&block
|
|
38
|
+
)
|
|
39
|
+
global_classes = BMC::BootstrapHelper.card_classes
|
|
40
|
+
card_classes = ([global_classes[:card]] + card_class.to_s.split).compact.sort
|
|
41
|
+
header_classes = ([global_classes[:header]] + header_class.to_s.split).compact.sort
|
|
42
|
+
body_classes = ([global_classes[:body]] + body_class.to_s.split).compact.sort
|
|
43
|
+
footer_classes = ([global_classes[:footer]] + footer_class.to_s.split).compact.sort
|
|
44
|
+
|
|
45
|
+
if header
|
|
46
|
+
header_html = content_tag(header_tag, class: header_classes) { header }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if body
|
|
50
|
+
body_html = content_tag(body_tag, class: body_classes) { capture(&block) }
|
|
51
|
+
else
|
|
52
|
+
body_html = capture(&block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if footer
|
|
56
|
+
footer_html = content_tag(footer_tag, class: footer_classes) { footer }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
content_tag(card_tag, class: card_classes) do
|
|
60
|
+
[header_html, body_html, footer_html].compact.sum
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|