vario 0.4.12

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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +61 -0
  4. data/Rakefile +37 -0
  5. data/app/controllers/vario/application_controller.rb +15 -0
  6. data/app/controllers/vario/levels_controller.rb +63 -0
  7. data/app/controllers/vario/settings_controller.rb +35 -0
  8. data/app/helpers/vario/application_helper.rb +13 -0
  9. data/app/jobs/vario/application_job.rb +4 -0
  10. data/app/mailers/vario/application_mailer.rb +6 -0
  11. data/app/models/vario/application_record.rb +5 -0
  12. data/app/models/vario/condition.rb +39 -0
  13. data/app/models/vario/level.rb +94 -0
  14. data/app/models/vario/setting.rb +188 -0
  15. data/app/views/vario/levels/_form.html.slim +53 -0
  16. data/app/views/vario/levels/create.js.erb +4 -0
  17. data/app/views/vario/levels/destroy.js.erb +4 -0
  18. data/app/views/vario/levels/update.js.erb +4 -0
  19. data/app/views/vario/settings/_levels.html.slim +22 -0
  20. data/app/views/vario/settings/_new_level.html.slim +7 -0
  21. data/app/views/vario/settings/_settings.html.slim +30 -0
  22. data/app/views/vario/settings/index.html.slim +28 -0
  23. data/app/views/vario/settings/levels.html.slim +1 -0
  24. data/app/views/vario/settings/show.html.slim +13 -0
  25. data/config/locales/en.yml +6 -0
  26. data/config/locales/nl.yml +6 -0
  27. data/config/routes.rb +10 -0
  28. data/db/migrate/20180628103728_create_vario_settings.rb +20 -0
  29. data/db/migrate/20180706130136_remove_fields_from_settings.rb +7 -0
  30. data/db/migrate/20180706155055_add_keys_to_setting.rb +5 -0
  31. data/db/migrate/20211027040546_unique_levels.rb +8 -0
  32. data/lib/vario/action_view_helpers.rb +31 -0
  33. data/lib/vario/active_record_helpers.rb +23 -0
  34. data/lib/vario/api.rb +148 -0
  35. data/lib/vario/config.rb +112 -0
  36. data/lib/vario/engine.rb +15 -0
  37. data/lib/vario/settable.rb +44 -0
  38. data/lib/vario/settings_reader.rb +35 -0
  39. data/lib/vario/version.rb +5 -0
  40. data/lib/vario.rb +30 -0
  41. metadata +194 -0
@@ -0,0 +1,22 @@
1
+ - context = {} if context.blank?
2
+
3
+ div data-id="#{setting.id}_levels"
4
+ .grid.grid-cols-12.gap-4 data-controller='draggable' data-draggable-handle='.draggable'
5
+ - setting.levels.each do |level|
6
+ .col-span-12 data-draggable-url="#{vario.move_setting_level_path(setting_id: setting.id, id: level.id)}" class= "#{"hidden" unless level.with_context_values?(context)}"
7
+ = render partial: 'vario/levels/form', locals: { setting: setting, level: level, context: context }
8
+
9
+ - function_name = "reload_#{setting.id.gsub('-', '')}"
10
+ javascript:
11
+ function #{{function_name}}() {
12
+ document.querySelector("[data-id='#{setting.id}_levels']").addEventListener('draggable.success', function(e) {
13
+ fetch("#{vario.levels_setting_path(setting)}")
14
+ .then(response => response.text())
15
+ .then(function(html) {
16
+ e.detail.element.outerHTML = html
17
+ #{{function_name}}()
18
+ })
19
+ })
20
+ }
21
+
22
+ #{{function_name}}()
@@ -0,0 +1,7 @@
1
+ - context = {} if context.blank?
2
+ - level = setting.new_level
3
+
4
+ div data-id="#{setting.id}_new_level"
5
+ .grid.grid-cols-12.gap-4
6
+ .col-span-12 class="#{"hidden" if level.conditions_not_for(context).size == 0 && setting.levels.find { |level| level.with_context_values?(context)}.present?}"
7
+ = render partial: 'vario/levels/form', locals: { setting: setting, level: level, label: 'New level', context: context, draggable: false }
@@ -0,0 +1,30 @@
1
+ - ui_card_title = 'Settings' if ui_card_title.blank?
2
+ - context = {} if context.blank?
3
+
4
+ = sts.card title: ui_card_title, icon: 'fad fa-slider' do |card|
5
+ - settings_with_contexts.sort_by { |setting| setting[:setting].name}.each do |setting|
6
+ - card.tab setting[:setting].name, title: setting[:setting].title, padding: true
7
+ .grid.grid-cols-12.gap-4
8
+ .col-span-12.prose
9
+ p= setting[:setting].description
10
+ .col-span-12
11
+ = render partial: 'vario/settings/levels', locals: setting
12
+ .col-span-12
13
+ = render partial: 'vario/settings/new_level', locals: setting
14
+
15
+ javascript:
16
+ $(document).on('ajax:error', function(event) {
17
+ window.alert('An error has occurred, your changes are likely not saved. Please try again.')
18
+ })
19
+
20
+ css:
21
+ .row.level {
22
+ min-height: 32px;
23
+ padding: 4px 0;
24
+ }
25
+ .row.level:nth-of-type(even) {
26
+ background-color: #f9f9f9;
27
+ }
28
+ .row.level:nth-of-type(odd) {
29
+ background-color: #f0f0f0;
30
+ }
@@ -0,0 +1,28 @@
1
+ = sts.card title: @title, icon: 'fad fa-sliders' do |card|
2
+ .grid.grid-cols-12.gap-4
3
+ .col-span-4
4
+
5
+ - @groups.each do |group|
6
+ - card.tab group[:name], padding: true
7
+ table.table
8
+ thead
9
+ th Name
10
+ th Levels
11
+ tbody
12
+ - @settings.for_group(group[:name]).each do |setting|
13
+ tr
14
+ td= link_to(setting.setting_name.humanize, setting_path(setting))
15
+ td= setting.levels.count
16
+
17
+ - if @settings.without_group.present?
18
+ - card.tab :ungrouped, padding: true
19
+ table.table
20
+ thead
21
+ th Name
22
+ th Levels
23
+ tbody
24
+ - @settings.without_group.each do |setting|
25
+ tr
26
+ td= link_to(setting.setting_name.humanize, setting_path(setting))
27
+ td= setting.levels.count
28
+
@@ -0,0 +1 @@
1
+ = render partial: 'vario/settings/levels', locals: { setting: @setting }
@@ -0,0 +1,13 @@
1
+ = sts.card title: @setting.title, icon: 'fad fa-sliders' do |card|
2
+
3
+ - card.action
4
+ = link_to("Back", settings_path(settable: @setting.settable.to_sgid(for: 'Vario').to_s), class: 'button')
5
+
6
+ .grid.grid-cols-12.gap-4
7
+ .col-span-12.prose
8
+ - if @setting.default.present?
9
+ p= "#{@setting.description} (default: #{[*@setting.default].join(', ')})"
10
+ - else
11
+ p= @setting.description
12
+ .col-span-12= render partial: 'vario/settings/levels', locals: { setting: @setting }
13
+ .col-span-12= render partial: 'vario/settings/new_level', locals: { setting: @setting }
@@ -0,0 +1,6 @@
1
+ en:
2
+ vario:
3
+ levels:
4
+ form:
5
+ save: Save
6
+ delete: Delete
@@ -0,0 +1,6 @@
1
+ nl:
2
+ vario:
3
+ levels:
4
+ form:
5
+ save: Opslaan
6
+ delete: Verwijder
data/config/routes.rb ADDED
@@ -0,0 +1,10 @@
1
+ Vario::Engine.routes.draw do
2
+ resources :settings do
3
+ get :levels, on: :member
4
+ resources :levels do
5
+ member do
6
+ patch :move
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ class CreateVarioSettings < ActiveRecord::Migration[5.2]
2
+ def change
3
+ enable_extension 'plpgsql'
4
+ enable_extension 'uuid-ossp'
5
+ enable_extension 'pgcrypto'
6
+
7
+ create_table :vario_settings, id: :uuid do |t|
8
+ t.references :settable, polymorphic: true, index: true, type: :uuid
9
+
10
+ t.string :name
11
+ t.string :category
12
+ t.string :description
13
+ t.string :keys, default: [], array: true
14
+
15
+ t.json :levels
16
+
17
+ t.timestamps
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ class RemoveFieldsFromSettings < ActiveRecord::Migration[5.2]
2
+ def change
3
+ remove_column :vario_settings, :category
4
+ remove_column :vario_settings, :description
5
+ remove_column :vario_settings, :keys
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class AddKeysToSetting < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :vario_settings, :keys, :string, array: true
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class UniqueLevels < ActiveRecord::Migration[6.1]
2
+ def up
3
+ Vario::Setting.all.each(&:uniq_levels!)
4
+ end
5
+
6
+ def down
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vario
4
+ module ActionViewHelpers
5
+ def render_vario_settings(*args)
6
+ settings_with_contexts = []
7
+
8
+ args = [args] unless args.first.is_a?(Array)
9
+ args.each do |argument_set|
10
+ settable = argument_set.first
11
+
12
+ if argument_set.last.is_a?(Hash)
13
+ setting_names = argument_set[1..-2]
14
+ context = argument_set.last
15
+ else
16
+ setting_names = argument_set[1..-1]
17
+ context = {}
18
+ end
19
+
20
+ Vario.config.pre_create_settings(settable)
21
+ settable.settings.where(name: setting_names).each do |setting|
22
+ settings_with_contexts << { setting: setting, context: context }
23
+ end
24
+ end
25
+
26
+ render partial: 'vario/settings/settings', locals: {
27
+ settings_with_contexts: settings_with_contexts
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vario
4
+ module ActiveRecordHelpers
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # provides settings_reader method to all models
9
+ include Vario::SettingsReader
10
+ end
11
+
12
+ class_methods do
13
+ def settable(options = {})
14
+ include Vario::Settable
15
+
16
+ configuration = { name: :settings }
17
+ configuration.update(options) if options.is_a?(Hash)
18
+
19
+ has_many configuration[:name], as: :settable, class_name: 'Vario::Setting'
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/vario/api.rb ADDED
@@ -0,0 +1,148 @@
1
+ # frozen_literal_string: true
2
+
3
+ require 'grape-swagger'
4
+
5
+ module Vario
6
+ class API < Grape::API
7
+ default_format :json
8
+ format :json
9
+
10
+ helpers do
11
+ def declared_params
12
+ declared(params, include_missing: false)
13
+ end
14
+
15
+ def settable
16
+ configuration[:settable]
17
+ end
18
+
19
+ def find_settable(id)
20
+ record = settable.find(id)
21
+ error!("#{settable.name} not found", 404) unless record.present?
22
+ record
23
+ end
24
+
25
+ def settable_settings
26
+ Vario.config.settable_settings[settable.name]
27
+ end
28
+
29
+ def settable_setting(setting)
30
+ settable_settings[setting]
31
+ end
32
+
33
+ def settable_setting_context(setting, context)
34
+ vario_setting = settable_setting(setting)
35
+ context.symbolize_keys!
36
+ vario_setting[:keys].each do |key|
37
+ context[key] ||= nil
38
+ end
39
+ context
40
+ end
41
+ end
42
+
43
+ given configuration[:settable] do
44
+ [*configuration[:include]].each do |m|
45
+ include m
46
+ end
47
+
48
+ Vario.config.settable_settings[configuration[:settable].name].each do |setting_name, setting_data|
49
+ before do
50
+ [*configuration[:before]].each do |method|
51
+ send(method) if method.is_a?(Symbol)
52
+ end
53
+ end if configuration[:before].present?
54
+
55
+ group_name, short_setting_name = setting_name.split('.', 2)
56
+
57
+ next unless Vario.config.show_group?(group_name)
58
+
59
+ route_param :id, type: String do
60
+ namespace configuration[:prefix] do
61
+ namespace group_name do
62
+ desc "Get setting value" do
63
+ detail "Get the setting value for #{setting_name}. Setting description: #{setting_data[:description]}"
64
+ end
65
+ params do
66
+ setting_data[:keys].each do |context_key|
67
+ optional context_key, type: String
68
+ end
69
+ end
70
+ scopes = [configuration[:oauth2].to_sym, "#{configuration[:oath2]}:read".to_sym] if configuration[:oauth2]
71
+ scopes ||= nil
72
+ get "/#{short_setting_name}", scopes: scopes do
73
+ record = find_settable(declared_params[:id])
74
+ context = settable_setting_context(setting_name, declared_params.reject { |key, value| key == 'id' }.to_h )
75
+
76
+ { setting: setting_name, value: record.setting(setting_name, context) }
77
+ end
78
+
79
+ setting_type =
80
+ case setting_data[:type]
81
+ when :array
82
+ Array[String]
83
+ when :boolean
84
+ Boolean
85
+ when :integer
86
+ Integer
87
+ else
88
+ String
89
+ end
90
+
91
+ desc "Add or change setting value" do
92
+ detail "Add or change setting value for #{setting_name}. Setting description: #{setting_data[:description]}"
93
+ end
94
+ params do
95
+ setting_data[:keys].each do |context_key|
96
+ optional context_key, type: String, documentation: { in: 'body' }
97
+ end
98
+ requires :value, type: setting_type, values: setting_data[:collection].present? ? setting_data[:collection].map { |item| item.last.to_s } : nil, documentation: { in: 'body' }
99
+ end
100
+ scopes = [configuration[:oauth2].to_sym, "#{configuration[:oath2]}:write".to_sym] if configuration[:oauth2]
101
+ scopes ||= nil
102
+ route [:post, :put], "/#{short_setting_name}", scopes: scopes do
103
+ record = find_settable(declared_params[:id])
104
+ context = settable_setting_context(setting_name, declared_params.reject { |key, value| %w[id value].include?(key) }.to_h )
105
+ vario_setting = record.settings.find_or_initialize_by(name: setting_name)
106
+ conditions_hash = context.values.compact.blank? ? {} : context
107
+ level = vario_setting.levels.find { |level| conditions_hash == level.conditions_hash }
108
+
109
+ if level.nil?
110
+ level = Level.new(vario_setting, { conditions: context })
111
+ vario_setting.levels.unshift level
112
+ end
113
+
114
+ level.value = declared_params[:value]
115
+ vario_setting.save!
116
+
117
+ { setting: setting_name, value: record.setting(setting_name, context) }
118
+ end
119
+
120
+ desc "Remove setting value" do
121
+ detail "Remove setting value for #{setting_name}. Setting description: #{setting_data[:description]}"
122
+ end
123
+ params do
124
+ setting_data[:keys].each do |context_key|
125
+ optional context_key, type: String
126
+ end
127
+ end
128
+ scopes = [configuration[:oauth2].to_sym, "#{configuration[:oath2]}:write".to_sym] if configuration[:oauth2]
129
+ scopes ||= nil
130
+ delete "/#{short_setting_name}", scopes: scopes do
131
+ record = find_settable(declared_params[:id])
132
+ context = settable_setting_context(setting_name, declared_params.reject { |key, value| key == 'id' }.to_h )
133
+ conditions_hash = context.values.compact.blank? ? {} : context
134
+
135
+ vario_setting = record.settings.find_or_initialize_by(name: setting_name)
136
+ vario_setting.levels.reject! { |level| conditions_hash == level.conditions_hash }
137
+ vario_setting.save!
138
+
139
+ { setting: setting_name, value: record.setting(setting_name, context) }
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
@@ -0,0 +1,112 @@
1
+ module Vario
2
+ class Config
3
+ attr_writer :logger, :current_settable
4
+ attr_reader :keys, :settable_types, :settable_settings
5
+ attr_accessor :base_controller, :admin_layout
6
+
7
+ def initialize
8
+ @logger = Logger.new(STDOUT)
9
+ @logger.level = Logger::INFO
10
+ @keys = {}
11
+ @base_controller = '::ApplicationController'
12
+ @settable_types = {}
13
+ @settable_settings = {}
14
+ @settable_type = nil
15
+ @current_settable = nil
16
+ @admin_layout = 'application'
17
+ @show_group_if = {}
18
+ end
19
+
20
+ # Config setters
21
+
22
+ def key(name, options)
23
+ options.symbolize_keys!
24
+ raise ArgumentError, 'name is required' unless options.keys.include?(:name)
25
+ raise ArgumentError, 'type is required' unless options.keys.include?(:type)
26
+ keys[name.to_s] = options
27
+ end
28
+
29
+ def show_group_if(name, block)
30
+ @show_group_if[name] = block
31
+ end
32
+
33
+ def show_group?(name)
34
+ if @show_group_if[name].is_a?(Proc)
35
+ @show_group_if[name].call
36
+ else
37
+ true
38
+ end
39
+ end
40
+
41
+ def setting(name, options = {})
42
+ settable_type = options.delete(:settable_type) || @settable_type
43
+ options[:type] ||= :string
44
+ options[:keys] ||= []
45
+ options[:default] ||= nil
46
+ options[:description] ||= "Configuration for #{name}, possible on: #{(options[:keys] + ['default']).map(&:to_s).join(', ')}"
47
+ settable_settings[settable_type] ||= {}
48
+ settable_settings[settable_type][name.to_s] = options
49
+ end
50
+
51
+ # If true, raise Vario::UnknownSetting if a unknown setting is retrieved, if false unknown settings return nil
52
+ def raise_on_undefined(value, options = {})
53
+ settable_type = options.delete(:settable_type) || @settable_type
54
+ settable_types[settable_type] ||= {}
55
+ settable_types[settable_type][:raise_on_undefined] = value
56
+ end
57
+
58
+ # If true, a new setting will be saved for settings that do not exist.
59
+ def create_on_request(value, options = {})
60
+ settable_type = options.delete(:settable_type) || @settable_type
61
+ settable_types[settable_type] ||= {}
62
+ settable_types[settable_type][:create_on_request] = value
63
+ settable_types[settable_type][:default_keys] = options[:with_keys]
64
+ end
65
+
66
+ def for_settable_type(settable_type)
67
+ @settable_type = settable_type
68
+ yield self
69
+ @settable_type = nil
70
+ end
71
+
72
+ # Config retrieval
73
+
74
+ # Config: logger [Object].
75
+ def logger
76
+ @logger.is_a?(Proc) ? instance_exec(&@logger) : @logger
77
+ end
78
+
79
+ def current_settable
80
+ @current_settable.is_a?(Proc) ? instance_exec(&@current_settable) : @current_settable
81
+ end
82
+
83
+ def settable_setting(settable_type, name)
84
+ settable_settings.dig(settable_type, name)
85
+ end
86
+
87
+ def key_data_for(key)
88
+ keys[key.to_s] || { name: key, type: :string, description: 'Not configured in initializer' }
89
+ end
90
+
91
+ def create_on_request?(settable_type)
92
+ settable_types.dig(settable_type, :create_on_request)
93
+ end
94
+
95
+ def raise_on_undefined?(settable_type)
96
+ settable_types.dig(settable_type, :raise_on_undefined)
97
+ end
98
+
99
+ def default_keys?(settable_type)
100
+ settable_types.dig(settable_type, :default_keys) || []
101
+ end
102
+
103
+ def pre_create_settings(settable)
104
+ return unless settable_settings[settable.class.name]
105
+
106
+ settable_settings[settable.class.name].keys.each do |key|
107
+ s = settable.settings.find_or_initialize_by(name: key)
108
+ s.save unless s.persisted?
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ module Vario
2
+ class Engine < ::Rails::Engine
3
+ config.autoload_paths << File.expand_path("../../lib", __dir__)
4
+
5
+ isolate_namespace Vario
6
+
7
+ initializer :append_migrations do |app|
8
+ unless app.root.to_s.match root.to_s
9
+ config.paths['db/migrate'].expanded.each do |expanded_path|
10
+ app.config.paths['db/migrate'] << expanded_path
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vario
4
+ module Settable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def setting(name, context = {})
9
+ name = name.to_s
10
+ context.symbolize_keys!
11
+ @vario_setting_cache ||= {}
12
+ @vario_setting_cache[name] ||= settings.find_or_initialize_by(name: name)
13
+ @vario_setting_cache[name]&.value_for(context)
14
+ end
15
+
16
+ def settings_hash(name, context = {})
17
+ name = name.to_s
18
+ context.symbolize_keys!
19
+ raise ArgumentError, 'Cannot request hash with a default' if context.key?(:default)
20
+
21
+ settings.where('name ILIKE ?', "#{name}.%").map do |vario_setting|
22
+ [vario_setting.name.gsub("#{name}.", ''), vario_setting.value_for(context)]
23
+ end.to_h.with_indifferent_access
24
+ end
25
+
26
+ def settings_save_unsaved
27
+ return unless @vario_setting_cache
28
+
29
+ @vario_setting_cache.each do |_key, vario_setting|
30
+ vario_setting.save unless vario_setting.persisted?
31
+ end
32
+ end
33
+
34
+ def settings_prepopulate_cache
35
+ return if @vario_setting_cache
36
+
37
+ @vario_setting_cache ||= {}
38
+ settings.map do |vario_setting|
39
+ @vario_setting_cache[vario_setting.name] ||= vario_setting
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vario
4
+ # Make vario settings accessible as readers on the object
5
+ module SettingsReader
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def settings_reader(klass, settings)
10
+ settings.each do |setting|
11
+ vario_setting = Vario.config.settable_settings[klass.name.to_s][setting]
12
+
13
+ method_name = setting.split('.').last
14
+ method_name += '?' if vario_setting[:type] == :boolean
15
+
16
+ define_method(method_name.to_sym) do
17
+ result = nil
18
+
19
+ if respond_to?(:settings) && self.settings.respond_to?(:dig)
20
+ result = self.settings.dig(*setting.split('.'))
21
+ end
22
+
23
+ if result.nil?
24
+ context = vario_setting[:keys].map { |key| [key, send(key)] }
25
+ result = self.setting(setting, context.to_h) if respond_to?(:settings) && self.class.name == klass.name
26
+ result = send(klass.name.underscore.to_sym)&.setting(setting, context.to_h) if result.nil? && self.respond_to?(klass.name.underscore.to_sym)
27
+ end
28
+
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vario
4
+ VERSION = "0.4.12"
5
+ end
data/lib/vario.rb ADDED
@@ -0,0 +1,30 @@
1
+ require_relative 'vario/engine'
2
+ require_relative 'vario/config'
3
+ require_relative 'vario/settable'
4
+ require_relative 'vario/settings_reader'
5
+ require_relative 'vario/action_view_helpers'
6
+ require_relative 'vario/active_record_helpers'
7
+ require_relative 'vario/api'
8
+
9
+ module Vario
10
+ class Error < StandardError; end
11
+ class UnknownSetting < Error; end
12
+
13
+ class << self
14
+ attr_reader :config
15
+
16
+ def setup
17
+ @config = Vario::Config.new
18
+ yield config
19
+ end
20
+ end
21
+
22
+ # Include helpers
23
+ ActiveSupport.on_load(:active_record) do
24
+ include ActiveRecordHelpers
25
+ end
26
+
27
+ ActiveSupport.on_load(:action_view) do
28
+ include ActionViewHelpers
29
+ end
30
+ end