hat-trick 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hat-trick.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Hat Trick
2
+ > Combines jQuery FormWizard, validation_group, and gon for the perfect triple-play of Rails wizarding.
3
+
4
+ ## Install
5
+ gem install hat-trick
6
+
7
+ ### Rails 3.2
8
+ (older versions not supported)
9
+
10
+ Put this in your Gemfile:
11
+
12
+ gem 'hat-trick'
13
+
14
+ ## Setup
15
+ In your controller:
16
+
17
+ wizard do
18
+ step :first_step
19
+ step :second_step
20
+ step :third_step
21
+ end
22
+
23
+ In your view:
24
+
25
+ <%= wizard_form_for @model do |f| %>
26
+ <fieldset id="first_step">...</fieldset>
27
+ <fieldset id="second_step">...</fieldset>
28
+ <fieldset id="third_step">...</fieldset>
29
+ <% end %>
30
+
31
+ The id's of the fieldsets in your form should match the step names you define in your controller.
32
+
33
+ ## Controlling the wizard flow
34
+ Sometimes you need to specify different paths through a wizard based on certain conditions. The way you do that with hat-trick is in the wizard DSL in the controller. Here are some examples:
35
+
36
+ Jumping to a step based on logged in status:
37
+
38
+ wizard do
39
+ step :first_step do
40
+ # after_this_step defines a callback to run after the current step is completed by the user
41
+ after_this_step do
42
+ # code in this block will be exec'd in the context of your controller
43
+ if user_signed_in?
44
+ next_step :third_step
45
+ end
46
+ end
47
+ end
48
+
49
+ step :second_step # wizard will go here after :first_step if user is not signed in
50
+
51
+ step :third_step # wizard will go here after :first_step if user is signed in
52
+
53
+ Repeating a previous step (for example, to show address sanitization results to the user):
54
+
55
+ wizard do
56
+ step :enter_address
57
+
58
+ step :confirm_santized_address do
59
+ repeat_step :enter_address
60
+ end
61
+
62
+ Skipping a step under certain conditions:
63
+
64
+ wizard do
65
+ step :first_step
66
+ step :second_step do
67
+ # before_this_step defines a callback to run before the user sees this step
68
+ before_this_step do
69
+ # code in this block will be exec'd in the context of your controller
70
+ skip_this_step unless foo.present?
71
+ end
72
+ end
73
+ end
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task :default => :spec
@@ -0,0 +1,233 @@
1
+ #= require vendor_js
2
+
3
+ class HatTrickWizard
4
+ constructor: (formElem, @wizard) ->
5
+ @form = $(formElem)
6
+ fieldsets = @form.find("fieldset")
7
+ fieldsets.addClass("step")
8
+ wizard_buttons = '<input type="reset" /><input type="submit" />'
9
+ fieldsets.find("div.buttons").html wizard_buttons
10
+ window.htData = {}
11
+ # prevent submitting the step that happens to be the last fieldset
12
+ this.addFakeLastStep()
13
+ this.enableFormwizard() # unless this.formwizardEnabled()
14
+ this.setCurrentStepField()
15
+ # TODO: Try this out instead of putting :start first
16
+ # this.showStep(@wizard.currentStep)
17
+ this.bindEvents()
18
+
19
+ buttons: {}
20
+
21
+ findStep: (stepId) ->
22
+ @form.find("fieldset##{stepId}")
23
+
24
+ createMethodField: (method) ->
25
+ """<input type="hidden" name="_method" value="#{method}" />"""
26
+
27
+ setAction: (url, method) ->
28
+ methodLower = method.toLowerCase()
29
+ console.log "Setting form action to #{methodLower} #{url}"
30
+ @form.attr("action", url)
31
+ @form.attr("method", "post")
32
+ methodField = @form.find('input[name="_method"]')
33
+ methodField.remove()
34
+ if methodLower isnt "post"
35
+ @form.prepend(this.createMethodField(method))
36
+
37
+ currentStepId: ->
38
+ @form.formwizard("state").currentStep
39
+
40
+ currentStep: ->
41
+ stepId = this.currentStepId()
42
+ this.findStep(stepId)
43
+
44
+ nextStepFieldHTML: """<input type="hidden" name="_ht_next_step" class="_ht_link" value="" />"""
45
+
46
+ fieldsets: ->
47
+ @form.find("fieldset")
48
+
49
+ ajaxEvents: (firstStep=false) ->
50
+ remoteAjax = {}
51
+ $fieldsets = this.fieldsets()
52
+ $fieldsets = $fieldsets.filter(":first") if firstStep
53
+ $fieldsets.each (index, element) =>
54
+ stepId = $(element).attr("id")
55
+ remoteAjax[stepId] = this.createAjaxEvent(stepId)
56
+ remoteAjax
57
+
58
+ createAjaxEvent: (step) ->
59
+ # console.log "Adding AJAX to step #{step}"
60
+ ajax =
61
+ url: @form.attr("action"),
62
+ dataType: "json",
63
+ beforeSubmit: (data) =>
64
+ console.log "Sending these data to the server: #{$.param(data)}"
65
+ success: (data) =>
66
+ console.log "Successful form POST; got #{$.param(data)}"
67
+ if data.wizardMetadata?
68
+ this.setAction(data.wizardMetadata.url, data.wizardMetadata.method)
69
+ # merge new data with window.htData
70
+ $.extend(window.htData, data)
71
+ error: (data) =>
72
+ appErrors = eval "(#{data.responseText})"
73
+ this.addErrorItem value[0] for key, value of appErrors.formModel
74
+ ajax
75
+
76
+ addErrorItem: (message) ->
77
+ $errorList = this.currentStep().find("ul.hat_trick_errors")
78
+ if $errorList.length > 0
79
+ $errorList.append("<li>#{message}</li>")
80
+ $errorList.show()
81
+
82
+ updateSteps: ->
83
+ @form.formwizard("update_steps")
84
+ @form.formwizard("option", remoteAjax: this.ajaxEvents())
85
+
86
+ goToStepId: (stepId) ->
87
+ console.log "Setting up goto #{stepId}"
88
+ this.setHTMeta("next_step", stepId)
89
+ @form.formwizard("next")
90
+
91
+ repeatStep: (step) ->
92
+ $sourceStep = this.findStep(step.repeatOf.fieldset)
93
+ console.log "Cloning repeated step #{step.repeatOf.fieldset}"
94
+ $clonedStep = $sourceStep.clone(true, true)
95
+ $clonedStep.css("display", "none")
96
+ $clonedStep.attr("id", step.name)
97
+ $sourceStep.after($clonedStep)
98
+ this.updateSteps()
99
+ @form.formwizard("show", step.name)
100
+
101
+ showStep: (step) ->
102
+ console.log "Showing step #{step.fieldset}"
103
+ @form.formwizard("show", step.fieldset)
104
+
105
+ formwizardEnabled: ->
106
+ @form.formwizard?
107
+
108
+ addFakeLastStep: ->
109
+ @form.append """<fieldset id="_ht_fake_last_step" style="display: none;" class="step"></fieldset>"""
110
+
111
+ enableFormwizard: ->
112
+ @form.formwizard
113
+ formPluginEnabled: true,
114
+ validationEnabled: true,
115
+ focusFirstInput: true,
116
+ historyEnabled: true,
117
+ disableUIStyles: true,
118
+ inDuration: 0,
119
+ linkClass: "_ht_link",
120
+ remoteAjax: this.ajaxEvents(true), # adds first Ajax event
121
+ formOptions:
122
+ success: (data) =>
123
+ console.log "Successful form POST"
124
+ beforeSubmit: (data) =>
125
+ console.log "Sending these data to the server: #{$.param(data)}"
126
+
127
+ htMetaHTML: (name) ->
128
+ """<input type="hidden" name="_ht_meta[#{name}]" id="_ht_#{name}" value="" />"""
129
+
130
+ setHTMeta: (key, value) ->
131
+ $meta = @form.find("input:hidden#_ht_#{key}")
132
+ if $meta.length is 0
133
+ $meta = @form.prepend(this.htMetaHTML(key)).find("#_ht_#{key}")
134
+ $meta.val(value)
135
+
136
+ clearHTMeta: (key) ->
137
+ @form.find("input:hidden#_ht_#{key}").remove()
138
+
139
+ setCurrentStepField: ->
140
+ stepId = this.currentStepId()
141
+ this.setHTMeta("step", stepId)
142
+ console.log "Current form step: #{stepId}"
143
+
144
+ clearNextStepField: ->
145
+ this.clearHTMeta("next_step")
146
+
147
+ fieldRegex: /^([^\[]+)\[([^\]]+)\]$/
148
+
149
+ setFieldValues: (formModel, selector, callback) ->
150
+ $currentStep = this.currentStep()
151
+ $currentStep.find(selector).each (index, element) =>
152
+ $element = $(element)
153
+ elementName = $element.attr("name")
154
+ if elementName? and elementName.search(@fieldRegex) isnt -1
155
+ [_, modelName, fieldName] = elementName.match(@fieldRegex)
156
+ if formModel[modelName]? and formModel[modelName][fieldName]?
157
+ fieldValue = formModel[modelName][fieldName]
158
+ callback($element, fieldValue) if fieldValue?
159
+
160
+ fillTextFields: (formModel) ->
161
+ this.setFieldValues formModel, "input:text", ($input, value) =>
162
+ $input.val(value)
163
+
164
+ setSelectFields: (formModel) ->
165
+ this.setFieldValues formModel, "select", ($select, value) =>
166
+ $select.find("option[value=\"#{value}\"]").attr("selected", "selected")
167
+
168
+ setCheckboxes: (formModel) ->
169
+ this.setFieldValues formModel, "input:checkbox", ($checkbox, value) =>
170
+ $checkbox.attr("checked", "checked") if value
171
+
172
+ setRadioButtons: (formModel) ->
173
+ this.setFieldValues formModel, "input:radio", ($radio, value) =>
174
+ $radio.find("[value=\"#{value}\"]").attr("checked", "checked")
175
+
176
+ setFormFields: (formModel) ->
177
+ this.fillTextFields(formModel)
178
+ this.setSelectFields(formModel)
179
+ this.setCheckboxes(formModel)
180
+ this.setRadioButtons(formModel)
181
+
182
+ createButton: (name, label) ->
183
+ """<input type="button" name="#{name}" value="#{label}" />"""
184
+
185
+ setButton: (name, label) ->
186
+ $buttonsDiv = this.currentStep().find("div.buttons")
187
+ switch name
188
+ when "next"
189
+ console.log "Setting submit button val to #{label}"
190
+ $buttonsDiv.find('input:submit').val(label)
191
+ when "back"
192
+ console.log "Setting reset button val to #{label}"
193
+ $buttonsDiv.find('input:reset').val(label)
194
+ else
195
+ buttonSelector = """input:button[name="#{name}"][value="#{label}"]"""
196
+ $existingButtons = $buttonsDiv.find(buttonSelector)
197
+ if $existingButtons.length == 0
198
+ console.log "Adding new #{name}:#{label} button"
199
+ $newButton = $buttonsDiv.append(this.createButton(name, label))
200
+ $newButton.click (event) =>
201
+ event.preventDefault()
202
+ this.goToStepId(name)
203
+
204
+ bindEvents: ->
205
+ @form.bind "step_shown", (event, data) =>
206
+ this.setCurrentStepField()
207
+ this.clearNextStepField()
208
+ this.setFormFields(htData.formModel)
209
+
210
+ buttons = this.buttons[this.currentStepId()]
211
+ if buttons?
212
+ this.setButton(name, label) for own name, label of buttons
213
+
214
+ if data.previousStep is data.firstStep
215
+ console.log "Adding additional Ajax events"
216
+ # adds additional Ajax events now that we have the update URL
217
+ @form.formwizard("option", remoteAjax: this.ajaxEvents())
218
+
219
+ @form.bind "after_remote_ajax", (event, data) =>
220
+ if htData.wizardMetadata?.currentStep.buttons?
221
+ stepId = htData.wizardMetadata.currentStep.fieldset
222
+ this.buttons[stepId] = htData.wizardMetadata.currentStep.buttons
223
+
224
+ if htData.wizardMetadata?.currentStep.repeatOf?
225
+ this.repeatStep(htData.wizardMetadata.currentStep)
226
+ else
227
+ this.showStep(htData.wizardMetadata.currentStep)
228
+
229
+ $ ->
230
+ $form = $("form.wizard")
231
+ if htData? and !htWizard?
232
+ console.log "Creating new HatTrickWizard instance"
233
+ window.htWizard = new HatTrickWizard($form, htData.wizardMetadata)
@@ -0,0 +1 @@
1
+ <%= include_gon(:namespace => 'htData', :camel_case => true) %>
data/hat-trick.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hat_trick/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hat-trick"
7
+ s.version = HatTrick::VERSION
8
+ s.authors = ["Wes Morgan"]
9
+ s.email = ["cap10morgan@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Rails wizards done right}
12
+ s.description = %q{Combines jQuery FormWizard, validation_group, and gon for the perfect triple-play of Rails wizarding.}
13
+
14
+ s.rubyforge_project = "hat-trick"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "mocha"
23
+ s.add_development_dependency "debugger"
24
+
25
+ s.add_runtime_dependency "rails", "~> 3.1"
26
+ s.add_runtime_dependency "validation_group"
27
+ s.add_runtime_dependency "gon"
28
+ end
data/lib/hat-trick.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "hat_trick/version"
2
+ require "active_support"
3
+ require "active_support/core_ext/module"
4
+ require "hat_trick/rails_engine"
5
+ require "hat_trick/dsl"
6
+ require "gon"
7
+
8
+ ::ActionController::Base.send(:include, HatTrick::DSL)
@@ -0,0 +1,118 @@
1
+ require 'hat_trick/model_methods'
2
+
3
+ module HatTrick
4
+ module ControllerHooks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ alias_method_chain :render, :hat_trick
9
+ end
10
+
11
+ def self.def_action_method_aliases(action_methods)
12
+ action_methods.each do |meth|
13
+ Rails.logger.info "Aliasing #{meth}"
14
+ module_eval <<-RUBY_EVAL
15
+ def #{meth}_with_hat_trick(*args)
16
+ Rails.logger.info "#{meth}_with_hat_trick called"
17
+ #{meth}_hook(*args) if respond_to?("#{meth}_hook", :include_private)
18
+ common_hook(*args)
19
+ #{meth}_without_hat_trick(*args)
20
+ end
21
+ private "#{meth}_with_hat_trick"
22
+ RUBY_EVAL
23
+ end
24
+ true
25
+ end
26
+
27
+ private
28
+
29
+ def params_model_name
30
+ params.each do |k,v|
31
+ return class_name(k) if v.is_a?(Hash) && is_model?(k)
32
+ end
33
+ nil
34
+ end
35
+
36
+ def is_model?(model_name)
37
+ begin
38
+ class_name(model_name).constantize
39
+ rescue NameError
40
+ return false
41
+ end
42
+ true
43
+ end
44
+
45
+ def class_name(hash_key)
46
+ hash_key.to_s.camelize
47
+ end
48
+
49
+ def model_class
50
+ model_name = params_model_name
51
+ return nil if model_name.nil?
52
+ begin
53
+ model_class = params_model_name.constantize
54
+ rescue NameError
55
+ Rails.logger.error "Could not find model class #{params_model_name.camelize}"
56
+ nil
57
+ else
58
+ model_class
59
+ end
60
+ end
61
+
62
+ def setup_validation_group_for(wizard_step)
63
+ klass = model_class
64
+ return if klass.nil?
65
+ step_name = wizard_step.name
66
+ validation_groups = ::ActiveRecord::Base.validation_group_classes[klass] || []
67
+ unless validation_groups.include?(step_name)
68
+ klass.validation_group(step_name, :fields => params.keys)
69
+ end
70
+ HatTrick::ModelMethods.set_current_validation_group_for(model_class, step_name)
71
+ unless klass.included_modules.include?(HatTrick::ModelMethods)
72
+ klass.send(:include, HatTrick::ModelMethods)
73
+ end
74
+ end
75
+
76
+ def create_hook(*args)
77
+ setup_validation_group_for(ht_wizard.current_step)
78
+ end
79
+
80
+ def update_hook(*args)
81
+ setup_validation_group_for(ht_wizard.current_step)
82
+ end
83
+
84
+ def common_hook(*args)
85
+ # nothing here for now
86
+ end
87
+
88
+ def render_with_hat_trick(*args)
89
+ if args.first.has_key?(:json)
90
+ model = args[0][:json]
91
+ ht_wizard.model = model
92
+ end
93
+
94
+ if params.has_key?('_ht_meta')
95
+ next_step = params['_ht_meta']['next_step']
96
+ ht_wizard.advance_step(next_step)
97
+ end
98
+
99
+ wizard_metadata = {
100
+ :url => ht_wizard.current_form_url,
101
+ :method => ht_wizard.current_form_method,
102
+ :currentStep => ht_wizard.current_step,
103
+ }
104
+
105
+ # this sets the wizard_metadata for the initial page load
106
+ gon.wizard_metadata = wizard_metadata
107
+
108
+ if ht_wizard.model && args[0].has_key?(:json)
109
+ # this sets the wizard metadata for subsequent AJAX requests
110
+ args[0][:json] = { :formModel => ht_wizard.model,
111
+ :wizardMetadata => wizard_metadata }
112
+ args[0][:json].merge! ht_wizard.include_data
113
+ end
114
+
115
+ render_without_hat_trick(*args)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,123 @@
1
+ require 'hat_trick/wizard_definition'
2
+ require 'hat_trick/controller_hooks'
3
+
4
+ module HatTrick
5
+ module DSL
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def wizard(&block)
10
+ if block_given?
11
+ include HatTrick::DSL::ControllerInstanceMethods
12
+ include HatTrick::ControllerHooks
13
+
14
+ @wizard_def = HatTrick::WizardDefinition.new
15
+
16
+ @wizard_dsl = HatTrick::DSL::WizardContext.new(@wizard_def)
17
+ @wizard_dsl.instance_eval &block
18
+
19
+ else
20
+ raise ArgumentError, "wizard called without a block"
21
+ end
22
+ end
23
+ end
24
+
25
+ module ControllerInstanceMethods
26
+ extend ActiveSupport::Concern
27
+
28
+ included do
29
+ before_filter :setup_wizard
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :ht_wizard
35
+
36
+ def setup_wizard
37
+ wizard_def = self.class.instance_variable_get("@wizard_def")
38
+ @ht_wizard = wizard_def.get_wizard(self)
39
+
40
+ if params.has_key?('_ht_meta')
41
+ step_name = params['_ht_meta']['step']
42
+ @ht_wizard.current_step = step_name if step_name
43
+ end
44
+ end
45
+ end
46
+
47
+ class WizardContext
48
+ attr_reader :wizard_def
49
+ attr_accessor :wizard
50
+
51
+ delegate :model, :previously_visited_step, :to => :wizard
52
+
53
+ def initialize(wizard_def)
54
+ @wizard_def = wizard_def
55
+ end
56
+
57
+ def step(name, args={}, &block)
58
+ wizard_def.add_step(name, args)
59
+ instance_eval &block if block_given?
60
+ end
61
+
62
+ def next_step(name=nil)
63
+ if name.nil?
64
+ wizard.next_step
65
+ else
66
+ raise "next_step should only be called from an after block" if wizard.nil?
67
+ step = wizard.find_step(name)
68
+ wizard.current_step.next_step = step
69
+ end
70
+ end
71
+
72
+ def repeat_step(name)
73
+ repeated_step = wizard_def.find_step(name)
74
+ raise ArgumentError, "Couldn't find step named #{name}" unless repeated_step
75
+ new_step = repeated_step.dup
76
+ # use the repeated step's fieldset id
77
+ new_step.fieldset = repeated_step.fieldset
78
+ # but use the current step's name
79
+ new_step.name = wizard_def.last_step.name
80
+ if wizard
81
+ # TODO: Might turn all these into run-time methods; which would get
82
+ # rid of this wizard / wizard_def distinction
83
+ else
84
+ # replace the step we're in the middle of defining w/ new_step
85
+ wizard_def.replace_step(wizard_def.last_step, new_step)
86
+ end
87
+ end
88
+
89
+ def skip_this_step
90
+ if wizard
91
+ wizard.skip_step(wizard.current_step)
92
+ else
93
+ # skip_this_step in wizard definition context means the step
94
+ # can be explicitly jumped to, but won't be in the normal flow
95
+ wizard_def.last_step.skipped = true
96
+ end
97
+ end
98
+
99
+ def button_to(name, options=nil)
100
+ if wizard
101
+ raise "button_to not yet supported in before/after blocks"
102
+ end
103
+ label = options[:label] if options
104
+ label ||= name.to_s.humanize
105
+ step = wizard_def.last_step
106
+ step.buttons = step.buttons.merge(name => label)
107
+ end
108
+
109
+ def before(&block)
110
+ wizard_def.last_step.before_callback = block
111
+ end
112
+
113
+ def after(&block)
114
+ wizard_def.last_step.after_callback = block
115
+ end
116
+
117
+ def include_data(key, &block)
118
+ wizard_def.last_step.include_data = { key.to_sym => block }
119
+ end
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,30 @@
1
+ module HatTrick
2
+ module FormHelper
3
+ def wizard_form_for(record, *args, &proc)
4
+ options = args.extract_options!
5
+ options[:html] = { :class => 'wizard' }
6
+
7
+ wizard = controller.send(:ht_wizard)
8
+ wizard.start unless wizard.started?
9
+
10
+ # Do we still need these 2 lines?
11
+ wizard.model = record
12
+ controller.gon.form_model = record
13
+
14
+ options[:url] = wizard.current_form_url
15
+ options[:method] = wizard.current_form_method.to_sym
16
+
17
+ output = ActiveSupport::SafeBuffer.new
18
+ output.safe_concat(wizard_partial)
19
+
20
+ # now run the default FormBuilder & append to output
21
+ output << self.form_for(record, *(args << options), &proc)
22
+ end
23
+
24
+ private
25
+
26
+ def wizard_partial
27
+ controller.render_to_string(:partial => 'hat_trick/wizard_meta')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module HatTrick
2
+ module ModelMethods
3
+ extend ActiveSupport::Concern
4
+ mattr_accessor :current_validation_group
5
+
6
+ included do
7
+ alias_method_chain :save, :hat_trick
8
+ end
9
+
10
+ def self.set_current_validation_group_for(klass, validation_group_name)
11
+ self.current_validation_group ||= {}
12
+ current_validation_group[klass.to_s.underscore] = validation_group_name
13
+ end
14
+
15
+ def self.current_validation_group_for(klass)
16
+ current_validation_group[klass.to_s.underscore]
17
+ end
18
+
19
+ def save_with_hat_trick(*args)
20
+ enable_validation_group HatTrick::ModelMethods.current_validation_group_for(self.class)
21
+ save_without_hat_trick(*args)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'hat_trick/form_helper'
2
+
3
+ module HatTrick
4
+ class RailsEngine < ::Rails::Engine
5
+ # just defining this causes Rails to look for assets inside this gem
6
+
7
+ initializer 'hat-trick.form_helpers' do
8
+ ActiveSupport.on_load(:action_view) do
9
+ include HatTrick::FormHelper
10
+ end
11
+ end
12
+ end
13
+ end