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
@@ -0,0 +1,30 @@
1
+ class BMC::MiniModelSerializer::Serialize
2
+ attr_reader :object, :options
3
+
4
+ def initialize(object, options = {})
5
+ @object = object
6
+ @options = options
7
+ end
8
+
9
+ def call
10
+ if object.is_a?(Hash)
11
+ object.map { |k, v| [k.to_s, serialize(v)] }.to_h
12
+ elsif object.is_a?(Enumerable)
13
+ object.map { |e| serialize(e) }
14
+ elsif (serializer = "#{object.class}Serializer".safe_constantize)
15
+ serializer.call(object, options)
16
+ else
17
+ object.as_json
18
+ end
19
+ end
20
+
21
+ def self.call(...)
22
+ new(...).call
23
+ end
24
+
25
+ private
26
+
27
+ def serialize(object)
28
+ BMC::MiniModelSerializer::Serialize.call(object, options)
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ class BMC::MiniModelSerializer::Serializer < BMC::MiniModelSerializer::Serialize
2
+ def attributes
3
+ raise NotImplementedError
4
+ end
5
+
6
+ def call
7
+ serialize attributes.map { |k| [k, send(k)] }.to_h
8
+ end
9
+
10
+ private
11
+
12
+ def method_missing(m, *args)
13
+ if respond_to_missing?(m)
14
+ object.send(m, *args)
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def respond_to_missing?(m, *)
21
+ super || object.respond_to?(m, true)
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ module BMC::Monkey
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def prepend_instances(&block)
6
+ m = Module.new(&block)
7
+ send(:prepend, m)
8
+ end
9
+
10
+ def prepend_class(&block)
11
+ m = Module.new(&block)
12
+ singleton_class.send(:prepend, m)
13
+ end
14
+
15
+ def prepend_instance_method(name, &block)
16
+ check_instance_method_exist!(name)
17
+
18
+ m = Module.new
19
+ m.send(:define_method, name, &block)
20
+ send(:prepend, m)
21
+ end
22
+
23
+ def prepend_class_method(name, &block)
24
+ check_class_method_exist!(name)
25
+
26
+ m = Module.new
27
+ m.send(:define_method, name, &block)
28
+ singleton_class.send(:prepend, m)
29
+ end
30
+
31
+ def check_instance_method_exist!(name)
32
+ raise "instance method `#{name}` does not exist" unless instance_methods.include?(name)
33
+ end
34
+
35
+ def check_class_method_exist!(name)
36
+ raise "class method `#{name}` does not exist" unless methods.include?(name)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ # Microtime based uuids to be sortable
2
+ class BMC::SortableUUIDGenerator
3
+ REGEX_WITH_DASHES = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/
4
+ REGEX_WITHOUT_DASHES = /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/
5
+
6
+ def self.call
7
+ prefix = Time.zone.now.strftime("%s%9N").to_i.to_s(16)
8
+ suffix = SecureRandom.hex(8)
9
+ (prefix + suffix).gsub(REGEX_WITHOUT_DASHES, '\1-\2-\3-\4-\5')
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ class BMC::TokenGenerator
2
+ attr_reader :options
3
+
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
7
+
8
+ def size
9
+ options[:size] || self.class.default_size
10
+ end
11
+
12
+ def alphabet
13
+ options[:alphabet] || self.class.default_alphabet
14
+ end
15
+
16
+ def call
17
+ (alphabet * size).sample(size).join
18
+ end
19
+
20
+ class << self
21
+ def call(*args)
22
+ new(*args).call
23
+ end
24
+
25
+ attr_writer :default_size
26
+
27
+ def default_size
28
+ @default_size ||= 64
29
+ end
30
+
31
+ attr_writer :default_alphabet
32
+
33
+ def default_alphabet
34
+ @default_alphabet ||= (0..9).to_a + ("a".."z").to_a + ("A".."Z").to_a
35
+ end
36
+ end # class << self
37
+ end
@@ -0,0 +1,2 @@
1
+ class BMC::ApplicationMailer < BMC.parent_mailer
2
+ end
@@ -0,0 +1,9 @@
1
+ class BMC::GenericMailer < BMC::ApplicationMailer
2
+ def generic_email(data)
3
+ data.delete(:attachments).to_h.each do |filename, content|
4
+ attachments[filename] = content
5
+ end
6
+
7
+ mail(data)
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module BMC::ActiveRecordUUIDConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def assign_default_uuid
7
+ unless self.class.columns_hash["id"].type == :uuid
8
+ raise "invalid id type, please change to uuid"
9
+ end
10
+
11
+ self.id ||= BMC::SortableUUIDGenerator.call
12
+ end
13
+
14
+ included do
15
+ before_save :assign_default_uuid
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module BMC::DefaultValuesConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ def assign_default_values
5
+ end
6
+
7
+ def assign_default(attribute, value)
8
+ send("#{attribute}=", value) if send(attribute).nil?
9
+ end
10
+
11
+ included do
12
+ after_initialize :assign_default_values, if: :new_record?
13
+ end
14
+ end
@@ -0,0 +1,56 @@
1
+ module BMC::ModelI18n
2
+ extend ActiveSupport::Concern
3
+
4
+ MissingTranslationError = Class.new(StandardError)
5
+ class << self
6
+ attr_accessor :raise_on_missing_translations
7
+ end
8
+ self.raise_on_missing_translations = false
9
+
10
+ def t(*args)
11
+ self.class.t(*args)
12
+ end
13
+
14
+ def ts
15
+ self.class.ts
16
+ end
17
+
18
+ def tv(attribute)
19
+ value = public_send(attribute)
20
+ t("#{attribute}.#{value}") if value.present?
21
+ end
22
+
23
+ class_methods do
24
+ def t(attribute = nil, options = {})
25
+ should_raise = BMC::ModelI18n.raise_on_missing_translations
26
+ exception_class = BMC::ModelI18n::MissingTranslationError
27
+
28
+ if should_raise
29
+ options = options.merge(default: "")
30
+ end
31
+
32
+ if should_raise && attribute.nil?
33
+ human = model_name.human(options)
34
+ type = options[:count].to_i > 1 ? "plural" : "singular"
35
+ raise exception_class, "translation missing: #{self} #{type} model name" if human.blank?
36
+ return human
37
+ end
38
+
39
+ if should_raise && attribute
40
+ human = human_attribute_name(attribute, options)
41
+ raise exception_class, "translation missing: #{self}##{attribute}" if human.blank?
42
+ return human
43
+ end
44
+
45
+ if attribute.nil?
46
+ return model_name.human(options)
47
+ end
48
+
49
+ human_attribute_name(attribute, options) if attribute
50
+ end
51
+
52
+ def ts
53
+ t(nil, count: 2)
54
+ end
55
+ end # class_methods
56
+ end # BMC::ModelI18n
@@ -0,0 +1,9 @@
1
+ module BMC::ModelToS
2
+ def to_s
3
+ %w(name title label).map do |m|
4
+ return send(m) if respond_to?(m)
5
+ end
6
+
7
+ super
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module BMC::PluckDistinct
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def pluck_distinct(column)
6
+ all
7
+ .reorder(column)
8
+ .select(column)
9
+ .distinct
10
+ .pluck(column)
11
+ end
12
+ end # class_methods
13
+ end
@@ -0,0 +1,9 @@
1
+ module BMC::PluckToHash
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def pluck_to_hash(*attributes)
6
+ pluck(*attributes).map { |values| attributes.zip(values).to_h }
7
+ end
8
+ end # class_methods
9
+ end
@@ -0,0 +1,37 @@
1
+ module BMC::PolymorphicId
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ def self.polymorphic_id_for(relation_name)
6
+ module_src = File.read(__FILE__).split("__END__").last
7
+ module_src = module_src.gsub("relation", relation_name.to_s)
8
+ send :include, eval(module_src)
9
+ end
10
+
11
+ def guid
12
+ return nil if new_record?
13
+
14
+ "#{self.class.base_class}-#{id}"
15
+ end
16
+ end # included
17
+ end # module
18
+
19
+ __END__
20
+
21
+ Module.new do
22
+ def relation_guid
23
+ return nil if relation_type.blank? || relation_id.blank?
24
+
25
+ "#{relation_type}-#{relation_id}"
26
+ end
27
+
28
+ def relation_guid=(guid)
29
+ if guid.blank?
30
+ self.relation = nil
31
+ return
32
+ end
33
+
34
+ type, id = guid.split("-", 2)
35
+ self.relation = type.constantize.find(id)
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ module BMC::Search
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def default_search_fields
6
+ columns
7
+ .select { |column| column.type.in?([:string, :text]) }
8
+ .map { |column| "#{table_name}.#{column.name}" }
9
+ end # def default_search_fields
10
+
11
+ def search(q, *fields)
12
+ words = q.to_s.parameterize.split("-")
13
+ fields = default_search_fields if fields.empty?
14
+
15
+ return all if words.empty?
16
+
17
+ sql_query = words.map.with_index { |_word, index|
18
+ fields.map { |field|
19
+ "(UNACCENT(CAST(#{field} AS TEXT)) ILIKE :w#{index})"
20
+ }.join(" OR ")
21
+ }.map { |e| "(#{e})" }.join(" AND ")
22
+
23
+ sql_params = words.map.with_index { |word, index| ["w#{index}".to_sym, "%#{word}%"] }.to_h
24
+
25
+ where(sql_query, sql_params)
26
+ end # def search
27
+ end # class_methods
28
+ end # class BMC::Search
@@ -0,0 +1,17 @@
1
+ module BMC::TimestampHelpers
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def first_created
6
+ reorder(:created_at, :id).first
7
+ end
8
+
9
+ def last_created
10
+ reorder(:created_at, :id).last
11
+ end
12
+
13
+ def last_updated
14
+ reorder(:updated_at, :id).last
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ class BMC::Serializers::Base
2
+ attr_reader :data, :options
3
+
4
+ def initialize(data, options = {})
5
+ @data = data
6
+ @options = options
7
+ end
8
+
9
+ def formatted_data
10
+ data.map do |line|
11
+ line.map { |value| self.class.format(value) }
12
+ end
13
+ end
14
+
15
+ def render_inline
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def render_file(_file_path)
20
+ raise NotImplementedError
21
+ end
22
+
23
+ class << self
24
+ def decimals_precision
25
+ @decimals_precision ||= 2
26
+ end
27
+
28
+ attr_writer :decimals_precision
29
+
30
+ def format_integer(value)
31
+ value
32
+ end
33
+
34
+ def format_decimal(value)
35
+ value.to_f.round(decimals_precision) # Fix BigDecimal and 0.1 + 0.2
36
+ end
37
+
38
+ def format_boolean(value)
39
+ I18n.t(value.to_s)
40
+ end
41
+
42
+ def format_date_or_time(value)
43
+ value
44
+ end
45
+
46
+ def format_default(value)
47
+ value.to_s
48
+ end
49
+
50
+ def formatter_for(value)
51
+ return :integer if value.is_a?(Integer)
52
+ return :decimal if value.is_a?(Numeric)
53
+ return :boolean if value.is_a?(TrueClass) || value.is_a?(FalseClass)
54
+ return :date_or_time if value.is_a?(Date) || value.is_a?(Time)
55
+ return :default
56
+ end
57
+
58
+ def format(value)
59
+ formatter = "format_" + formatter_for(value).to_s
60
+ public_send(formatter, value)
61
+ end
62
+ end # class << self
63
+ end
@@ -0,0 +1,38 @@
1
+ class BMC::Serializers::XLSX < BMC::Serializers::Base
2
+ def render_inline
3
+ headers, *data = formatted_data
4
+
5
+ SpreadsheetArchitect.to_xlsx(
6
+ headers: headers,
7
+ data: data,
8
+ freeze_headers: true,
9
+ range_styles: range_styles(data[0]),
10
+ )
11
+ end
12
+
13
+ def render_file(file_path)
14
+ File.open(file_path, "w+b") do |f|
15
+ f.write(render_inline)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def range_styles(row)
22
+ return [] if row.nil?
23
+
24
+ date_range_styles(row) + time_range_styles(row)
25
+ end
26
+
27
+ def date_range_styles(row)
28
+ row.each_index.select { row[_1].is_a?(Date) }.map do
29
+ {range: {rows: :all, columns: _1}, styles: {format_code: "dd/mm/yyyy"}}
30
+ end
31
+ end
32
+
33
+ def time_range_styles(row)
34
+ row.each_index.select { row[_1].is_a?(Time) }.map do
35
+ {range: {rows: :all, columns: _1}, styles: {format_code: "dd/mm/yyyy hh:mm:ss"}}
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,2 @@
1
+ module BMC::Serializers
2
+ end
@@ -0,0 +1,42 @@
1
+ class BMC::SMS::ApplicationSMS
2
+ attr_reader :options
3
+
4
+ def initialize(options)
5
+ @options = options
6
+ end
7
+
8
+ private
9
+
10
+ def action_name
11
+ options[:action_name]
12
+ end
13
+
14
+ def t(key, **args)
15
+ if key.start_with?(".")
16
+ path = self.class.to_s.underscore.tr("/", ".")
17
+ key = "#{path}.#{action_name}#{key}"
18
+ end
19
+
20
+ I18n.t(key, **args)
21
+ end
22
+
23
+ def sms(data)
24
+ BMC::SMS::Message.new(data)
25
+ end
26
+
27
+ class << self
28
+ private :new
29
+
30
+ def method_missing(m, *args)
31
+ if respond_to_missing?(m)
32
+ new(action_name: m).public_send(m, *args)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def respond_to_missing?(m, *)
39
+ super || public_instance_methods.include?(m)
40
+ end
41
+ end # class << self
42
+ end
@@ -0,0 +1,25 @@
1
+ class BMC::SMS::Message
2
+ attr_reader :data
3
+
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def strategy=(value)
9
+ @strategy = BMC::SMS.parse_strategy(value)
10
+ end
11
+
12
+ def strategy(value = :no_argument)
13
+ self.strategy = value unless value == :no_argument
14
+ @strategy || BMC::SMS.strategy
15
+ end
16
+
17
+ def deliver_now
18
+ strategy.call(data)
19
+ end
20
+
21
+ # TODO : Delay
22
+ def deliver_later
23
+ deliver_now
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ class BMC::SMS::Strategies::AmazonSNS < BMC::SMS::Strategies::Base
2
+ class << self
3
+ attr_writer :sns_region
4
+
5
+ def sns_region
6
+ @sns_region ||= (ENV["SNS_REGION"] || ENV["AWS_REGION"])
7
+ end
8
+
9
+ attr_writer :sns_access_key_id
10
+
11
+ def sns_access_key_id
12
+ @sns_access_key_id ||= (ENV["SNS_ACCESS_KEY_ID"] || ENV["AWS_ACCESS_KEY_ID"])
13
+ end
14
+
15
+ attr_writer :sns_secret_access_key
16
+
17
+ def sns_secret_access_key
18
+ @sns_secret_access_key ||= (ENV["SNS_SECRET_ACCESS_KEY"] || ENV["AWS_SECRET_ACCESS_KEY"])
19
+ end
20
+ end # class << self
21
+
22
+ def client
23
+ @client ||= Aws::SNS::Client.new(
24
+ :region => self.class.sns_region,
25
+ :access_key_id => self.class.sns_access_key_id,
26
+ :secret_access_key => self.class.sns_secret_access_key,
27
+ )
28
+ end
29
+
30
+ def call
31
+ from = data[:from] || BMC::SMS.default_from
32
+
33
+ client.publish(
34
+ :phone_number => data[:to],
35
+ :message => data[:body],
36
+ :message_attributes => {
37
+ "AWS.SNS.SMS.SenderID" => {
38
+ :data_type => "String",
39
+ :string_value => from,
40
+ },
41
+ "AWS.SNS.SMS.SMSType" => {
42
+ :data_type => "String",
43
+ :string_value => "Transactional",
44
+ },
45
+ },
46
+ )
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ class BMC::SMS::Strategies::Base
2
+ attr_reader :data
3
+
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def call
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def self.call(...)
13
+ new(...).call
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ class BMC::SMS::Strategies::Test < BMC::SMS::Strategies::Base
2
+ def self.deliveries
3
+ @deliveries ||= []
4
+ end
5
+
6
+ def call
7
+ self.class.deliveries << data
8
+ Rails.logger.info "New SMS sent : #{data.inspect}"
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module BMC::SMS
2
+ class << self
3
+ def strategy=(value)
4
+ @strategy = parse_strategy(value)
5
+ end
6
+
7
+ def strategy
8
+ @strategy ||= default_strategy
9
+ end
10
+
11
+ attr_writer :default_from
12
+
13
+ def default_from
14
+ @default_from ||= Rails.application.class.to_s.chomp("::Application")
15
+ end
16
+
17
+ def parse_strategy(value)
18
+ if value.is_a?(Symbol)
19
+ "BMC::SMS::Strategies::#{value.to_s.camelcase}".constantize
20
+ else
21
+ value
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def default_strategy
28
+ if Rails.env.development? || Rails.env.test?
29
+ BMC::SMS::Strategies::Test
30
+ else
31
+ BMC::SMS::Strategies::AmazonSNS
32
+ end
33
+ end
34
+ end # class << self
35
+ end
@@ -0,0 +1,26 @@
1
+ class BMC::Sorter
2
+ include BMC::SortingHelper
3
+
4
+ attr_reader :collection, :sort_param, :column, :direction
5
+
6
+ def initialize(collection, sort_param = nil)
7
+ @collection = collection
8
+ @sort_param = sort_param
9
+ @column, @direction = sortable_column_order(sort_param.to_s)
10
+ end
11
+
12
+ def sort
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def call
17
+ # Don't replace by `collection.reorder(sort)`
18
+ # #sort can change #collection and must be called before #collection
19
+ order = sort
20
+ collection.reorder(order)
21
+ end
22
+
23
+ def self.call(...)
24
+ new(...).call
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ = render "bmc/search/form_bs#{Bootstrap::VERSION[0]}", action: action