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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +28 -0
  4. data/Rakefile +41 -0
  5. data/app/assets/config/bmc_manifest.js +2 -0
  6. data/app/controllers/bmc/application_controller.rb +3 -0
  7. data/app/controllers/bmc/filters_controller.rb +38 -0
  8. data/app/controllers/concerns/bmc/api_controller_concern.rb +69 -0
  9. data/app/controllers/concerns/bmc/back_url_concern.rb +26 -0
  10. data/app/emails/bmc/email.rb +113 -0
  11. data/app/filters/bmc/filter/by_date.rb +6 -0
  12. data/app/filters/bmc/filter/by_date_begin.rb +7 -0
  13. data/app/filters/bmc/filter/by_date_end.rb +7 -0
  14. data/app/filters/bmc/filter/by_date_or_datetime_period.rb +56 -0
  15. data/app/filters/bmc/filter/by_date_period.rb +5 -0
  16. data/app/filters/bmc/filter/by_datetime_period.rb +5 -0
  17. data/app/filters/bmc/filter/by_key_value.rb +27 -0
  18. data/app/filters/bmc/filter/by_key_values.rb +13 -0
  19. data/app/filters/bmc/filter.rb +86 -0
  20. data/app/forms/bmc/mini_form_object.rb +29 -0
  21. data/app/helpers/bmc/all_helpers.rb +15 -0
  22. data/app/helpers/bmc/bootstrap_helper.rb +63 -0
  23. data/app/helpers/bmc/button_helper.rb +150 -0
  24. data/app/helpers/bmc/filters_helper.rb +16 -0
  25. data/app/helpers/bmc/font_awesome_helper.rb +67 -0
  26. data/app/helpers/bmc/form_helper.rb +24 -0
  27. data/app/helpers/bmc/i18n_helper.rb +5 -0
  28. data/app/helpers/bmc/link_helper.rb +42 -0
  29. data/app/helpers/bmc/pagination_helper.rb +22 -0
  30. data/app/helpers/bmc/routes_helper.rb +24 -0
  31. data/app/helpers/bmc/sorting_helper.rb +55 -0
  32. data/app/helpers/bmc/text_helper.rb +127 -0
  33. data/app/helpers/h.rb +3 -0
  34. data/app/jobs/concerns/bmc/setup_job_concern.rb +35 -0
  35. data/app/libs/bmc/collection_update.rb +38 -0
  36. data/app/libs/bmc/fcm/notifier.rb +32 -0
  37. data/app/libs/bmc/fcm/request.rb +54 -0
  38. data/app/libs/bmc/mini_model_serializer/serialize.rb +30 -0
  39. data/app/libs/bmc/mini_model_serializer/serializer.rb +23 -0
  40. data/app/libs/bmc/monkey.rb +39 -0
  41. data/app/libs/bmc/sortable_uuid_generator.rb +11 -0
  42. data/app/libs/bmc/token_generator.rb +37 -0
  43. data/app/mailers/bmc/application_mailer.rb +2 -0
  44. data/app/mailers/bmc/generic_mailer.rb +9 -0
  45. data/app/models/concerns/bmc/active_record_uuid_concern.rb +17 -0
  46. data/app/models/concerns/bmc/default_values_concern.rb +14 -0
  47. data/app/models/concerns/bmc/model_i18n.rb +56 -0
  48. data/app/models/concerns/bmc/model_to_s.rb +9 -0
  49. data/app/models/concerns/bmc/pluck_distinct.rb +13 -0
  50. data/app/models/concerns/bmc/pluck_to_hash.rb +9 -0
  51. data/app/models/concerns/bmc/polymorphic_id.rb +37 -0
  52. data/app/models/concerns/bmc/search.rb +28 -0
  53. data/app/models/concerns/bmc/timestamp_helpers.rb +17 -0
  54. data/app/serializers/bmc/serializers/base.rb +63 -0
  55. data/app/serializers/bmc/serializers/xlsx.rb +38 -0
  56. data/app/serializers/bmc/serializers.rb +2 -0
  57. data/app/sms/bmc/sms/application_sms.rb +42 -0
  58. data/app/sms/bmc/sms/message.rb +25 -0
  59. data/app/sms/bmc/sms/strategies/amazon_sns.rb +48 -0
  60. data/app/sms/bmc/sms/strategies/base.rb +15 -0
  61. data/app/sms/bmc/sms/strategies/test.rb +10 -0
  62. data/app/sms/bmc/sms.rb +35 -0
  63. data/app/sorters/bmc/sorter.rb +26 -0
  64. data/app/views/bmc/search/_form.html.slim +1 -0
  65. data/app/views/bmc/search/_form_bs3.html.slim +13 -0
  66. data/app/views/bmc/search/_form_bs4.html.slim +9 -0
  67. data/app/views/bmc/search/_form_bs5.html.slim +8 -0
  68. data/app/views/kaminari/bootstrap4/_first_page.html.slim +2 -0
  69. data/app/views/kaminari/bootstrap4/_gap.html.slim +2 -0
  70. data/app/views/kaminari/bootstrap4/_last_page.html.slim +2 -0
  71. data/app/views/kaminari/bootstrap4/_next_page.html.slim +2 -0
  72. data/app/views/kaminari/bootstrap4/_page.html.slim +6 -0
  73. data/app/views/kaminari/bootstrap4/_paginator.html.slim +12 -0
  74. data/app/views/kaminari/bootstrap4/_prev_page.html.slim +2 -0
  75. data/config/locales/actions.en.yml +76 -0
  76. data/config/locales/actions.fr.yml +76 -0
  77. data/config/locales/attributes.en.yml +108 -0
  78. data/config/locales/attributes.fr.yml +108 -0
  79. data/config/locales/common.en.yml +39 -0
  80. data/config/locales/common.fr.yml +39 -0
  81. data/config/locales/dates.fr.yml +8 -0
  82. data/config/locales/errors.en.yml +5 -0
  83. data/config/locales/errors.fr.yml +5 -0
  84. data/config/routes.rb +3 -0
  85. data/db/migrate/20000101000000_enable_bmc_extensions.rb +7 -0
  86. data/lib/bmc/active_model_custom_error_messages.rb +23 -0
  87. data/lib/bmc/active_model_type_cast.rb +52 -0
  88. data/lib/bmc/config.rb +19 -0
  89. data/lib/bmc/core_and_rails_ext.rb +3 -0
  90. data/lib/bmc/engine.rb +36 -0
  91. data/lib/bmc/engine_file.rb +10 -0
  92. data/lib/bmc/errors_middleware.rb +50 -0
  93. data/lib/bmc/form_back_url.rb +18 -0
  94. data/lib/bmc/version.rb +3 -0
  95. data/lib/bmc.rb +5 -0
  96. data/lib/tasks/bmc_tasks.rake +4 -0
  97. 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
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## v1.0.0
4
+
5
+ - Fork from agilibox
6
+ - Zeitwerk compatibility
7
+ - Remove all CSS/JS
8
+ - Filter class names changes
9
+ - Remove test helpers
10
+ - Various view helpers changes
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,2 @@
1
+ //= link_directory ../javascripts/bmc .js
2
+ //= link_directory ../stylesheets/bmc .css
@@ -0,0 +1,3 @@
1
+ class BMC::ApplicationController < BMC.parent_controller
2
+ protect_from_forgery with: :exception
3
+ 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,6 @@
1
+ class BMC::Filter::ByDate < BMC::Filter::ByKeyValue
2
+ def apply(query, value)
3
+ value = Date.parse(value)
4
+ super(query, value)
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class BMC::Filter::ByDateBegin < BMC::Filter::ByKeyValue
2
+ def apply(query, value)
3
+ value = Date.parse(value)
4
+ column = column_for(query)
5
+ query.where("#{column} >= ?", value)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class BMC::Filter::ByDateEnd < BMC::Filter::ByKeyValue
2
+ def apply(query, value)
3
+ value = Date.parse(value)
4
+ column = column_for(query)
5
+ query.where("#{column} <= ?", value)
6
+ end
7
+ 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,5 @@
1
+ class BMC::Filter::ByDatePeriod < BMC::Filter::ByDateOrDatetimePeriod
2
+ def now
3
+ Date.current
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class BMC::Filter::ByDatetimePeriod < BMC::Filter::ByDateOrDatetimePeriod
2
+ def now
3
+ Time.zone.now
4
+ end
5
+ 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