bmc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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