facades 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,212 @@
1
+ module Facades::Builders
2
+ module FormBuilder
3
+ module Base
4
+ # Access the template object
5
+ attr_accessor :template
6
+ # Tracks the order in which fields are used in the form, this allows the easy rebuilding of the submitted form
7
+ # data when submitted since normally the hash isn't ordered.
8
+ attr_accessor :field_order
9
+ # Tracks the field currently being "processed"
10
+ attr_accessor :current_field_type
11
+
12
+
13
+ # Overrides fields_for to make sure the form builder is set properly
14
+ #
15
+ def fields_for(record_or_name_or_array, *args, &block) #:nodoc:
16
+ opts = args.extract_options!
17
+ opts[:builder] ||= ActionView::Base.default_form_builder
18
+ args.push(opts)
19
+ with_custom_error_proc do
20
+ super(record_or_name_or_array, *args, &block)
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ ##
27
+ #
28
+ # Checks to see if a particular attribute is required, if so, a "required" attribute is added to the field.
29
+ #
30
+ # @param [Symbol] attribute The attribute to check against validators
31
+ # @return [Boolean]
32
+ #
33
+ def attribute_required?(attribute)
34
+ validates_presence?(attribute) || validates_inclusion?(attribute)
35
+ end
36
+
37
+ # Convenience method to use the +content_tag+ method from our template
38
+ #
39
+ def content_tag(tag, content, options = {}, escape = true, &block) #:nodoc:
40
+ @template.content_tag(tag, content, options, escape, &block)
41
+ end
42
+
43
+ ##
44
+ # Checks to see if there are errors for the particular method or attribute
45
+ #
46
+ # @param [Symbol] method The method/attribute to check
47
+ #
48
+ # @return [Boolean]
49
+ #
50
+ def errors_on_attribute?(method)
51
+ return false if @object.nil?
52
+ !(@object.errors.empty? || !@object.errors[method.to_sym].present? || [@object.errors[method.to_sym]].flatten.empty?)
53
+ end
54
+
55
+ ##
56
+ #
57
+ # Checks a passed validator to see if it is required
58
+ # 'borrowed' from Formtastic by Justin French (see https://github.com/justinfrench/formtastic)
59
+ #
60
+ # @param [Hash] options Validator options
61
+ #
62
+ def options_require_validation?(options)
63
+
64
+ allow_blank = options[:allow_blank]
65
+ return !allow_blank unless allow_blank.nil?
66
+ if_condition = !options[:if].nil?
67
+ condition = if_condition ? options[:if] : options[:unless]
68
+
69
+ condition = if condition.respond_to?(:call)
70
+ condition.call(@object)
71
+ elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
72
+ @object.send(condition)
73
+ else
74
+ condition
75
+ end
76
+ if_condition ? !!condition : !condition
77
+ end
78
+
79
+ ##
80
+ #
81
+ # Checks an options hash to determine if a required method/attribute was overridden manually
82
+ # @param [Hash] options The options hash to check
83
+ #
84
+ #
85
+ def required_by_option?(options)
86
+ req = (options.is_a?(Hash) ? options.stringify_keys[:required] : options)
87
+ !(req.to_s === 'false' || req.nil?)
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Wrapper method used by all form fields to customize the output
93
+ #
94
+ # @param [Symbol] helper_method Original Rails helper method name
95
+ # @param [Symbol] method Object method / symbol used for the element
96
+ # @param [Array] args Array of original arguments passed to the helper
97
+ #
98
+ # @return [String] Rendered HTML tag for the element
99
+ #
100
+ def render_field_as_custom(helper_method, method, *args)
101
+
102
+ @current_field_type = helper_method
103
+
104
+ options = args.extract_options!
105
+ (@field_order ||= []) << method
106
+
107
+ # Add an error class to the field if it has errors
108
+ #
109
+ if errors_on_attribute?(method)
110
+ klasses = (options.delete(:class) || "").split(" ")
111
+ klasses << 'field-with-error'
112
+ options[:class] = klasses.join(" ")
113
+ end
114
+
115
+ # Add a required attribute to the field if it is required
116
+ # Skip if false was passed as the required option
117
+ #
118
+ options[:required] = "required" if attribute_required?(method) && options.delete(:required).to_s != 'false'
119
+ options['data-validates-uniqueness'] = "true" if validates_uniqueness?(method)
120
+
121
+ result = send(:"_super_#{helper_method}", method, *(args << options))
122
+ messages = @object.nil? ? [] : @object.errors[method]
123
+ render_field_with_errors(method, result, messages)
124
+
125
+ end
126
+
127
+ ##
128
+ #
129
+ # Renders the passed +html_tag+ with the custom error_template
130
+ #
131
+ # @param [Symbol] method The method/attribute to check for errors
132
+ # @param [Object, String] html_tag Instance of an input tag or a string
133
+ # @param [Array] messages An array of all error messages to be added to the template
134
+ #
135
+ def render_field_with_errors(method, html_tag, messages)
136
+ return html_tag unless errors_on_attribute?(method)
137
+ error_template = %{
138
+ <span class="field-with-errors">
139
+ <%= html_tag %>
140
+ <span class="errors-for-field"><%= [messages].flatten.join(",") %></span>
141
+ </span>}
142
+
143
+ render_binding = binding
144
+ renderer = ERB.new(error_template)
145
+ renderer.result(render_binding).to_s.html_safe
146
+ end
147
+
148
+ ##
149
+ #
150
+ # Compiles an array of all validators for a particular attribute
151
+ # @param [Symbol] attribtue The attribute to check
152
+ #
153
+ def validators_for(attribute)
154
+
155
+ return [] if @object.nil?
156
+ return [] unless @object.class.respond_to?(:validators_on)
157
+
158
+ attribute = attribute.to_s.sub(/_id$/, '').to_sym
159
+ @object.class.validators_on(attribute).uniq
160
+
161
+ end
162
+
163
+ ##
164
+ #
165
+ # Convenience method to see if a particular attribute has validators
166
+ # @param [Symbol] attribute The attribute to check
167
+ #
168
+ def validators_for?(attribute)
169
+ !validators_for(attribute).empty?
170
+ end
171
+
172
+ ##
173
+ #
174
+ # Checks for a presence validation on a particular attribute
175
+ # @param [Symbol] attribute The attribute to check
176
+ #
177
+ def validates_presence?(attribute)
178
+ validator_of_type_exists?(validators_for(attribute), :presence)
179
+ end
180
+
181
+ ##
182
+ #
183
+ # Checks for a uniqueness validation on a particular attribute
184
+ # @param [Symbol] attribute The attribute to check
185
+ #
186
+ def validates_uniqueness?(attribute)
187
+ validator_of_type_exists?(validators_for(attribute), :uniqueness, false)
188
+ end
189
+
190
+ ##
191
+ #
192
+ # Checks for inclusion validation on a particular attribute
193
+ # @param [Symbol] attribute The attribute to check
194
+ #
195
+ def validates_inclusion?(attribute)
196
+ validator_of_type_exists?(validators_for(attribute), :inclusion)
197
+ end
198
+
199
+ private
200
+
201
+ def validator_of_type_exists?(validators, kind, check_options = true) #:nodoc: @private
202
+ validators.detect do |validator|
203
+ exists = (validator.kind.to_s == kind.to_s)
204
+ next exists unless (check_options && exists) && validator.options.present?
205
+ options_require_validation?(validator.options)
206
+ end
207
+ end
208
+
209
+
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,112 @@
1
+ module Facades::Builders
2
+ module FormBuilder
3
+ module Elements
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # create overrides for custom rendering
8
+ [:email_field, :password_field, :text_field, :text_area, :url_field].each do |method|
9
+ class_eval <<-FUNC, __FILE__, __LINE__ + 1
10
+
11
+ alias :_super_#{method} :#{method}
12
+
13
+ def #{method}(method_name, *args)
14
+ render_field_as_custom(:#{method}, method_name, *args)
15
+ end
16
+ FUNC
17
+ end
18
+ end
19
+
20
+ ##
21
+ # Modified label tag to support adding a 'required' asterisk to the end of the label.
22
+ # Same params as the original implementation
23
+ #
24
+ def label(method, text = nil, options = {}, &block) #:nodoc:
25
+
26
+ options, text = text, nil if text.is_a?(Hash)
27
+ text ||= method.to_s.humanize
28
+
29
+ options.stringify_keys!
30
+ klasses = (options.delete(['class']) || "").split(" ")
31
+ klasses << 'field-with-error' if errors_on_attribute?(method)
32
+ options['class'] = klasses.join(" ") unless klasses.compact.empty?
33
+
34
+ text = "#{text} <abbr title='Required'>*</abbr>".html_safe if attribute_required?(method) || required_by_option?(options.delete('required'))
35
+ super(method, text, options, &block)
36
+
37
+ end
38
+
39
+ ##
40
+ #
41
+ # Creates a button tag to be used in a form instead of the default input[type=submit]
42
+ # to help make CSS styling easier
43
+ #
44
+ # @param [String] value The text for the button
45
+ # @param [Hash] options HTML options to be passed to the button
46
+ # @option [String] icon If included, adds an image to the button to be used as an icon
47
+ #
48
+ def button(value = nil, options = {})
49
+
50
+ value, options = nil, value if value.is_a?(Hash)
51
+ value ||= submit_default_value
52
+
53
+ value = [image_tag(icon, :class => 'icon'), value].join(' ') if icon = options.delete(:icon)
54
+ klasses = (options.delete(:class) || "").split(" ")
55
+ klasses << "button"
56
+ options['class'] = klasses.join(" ")
57
+ content_tag(:button, value.to_s.html_safe, options.reverse_merge!({ "type" => "submit", "name" => "commit" }))
58
+
59
+ end
60
+
61
+ ##
62
+ #
63
+ # Generate a select tag with the 50 US states as options
64
+ #
65
+ # @param [Symbol] method The object method/attribute to be used
66
+ # @param [Hash] options Same as Rails' select options hash
67
+ # @option options [Symbol] :international Include an international option
68
+ # @option options [Symbol] :abbreviate Use an abbreviated version of the state name for the value
69
+ # @param [Hash] html_options Same as Rails' html_options hash
70
+ #
71
+ # @return [String] HTML select tag
72
+ #
73
+ def state_select(method, options = {}, html_options = {})
74
+ abbr = options.delete(:abbreviate)
75
+ abbr = !(abbr.nil? || abbr === false)
76
+ select(method, @template.options_for_select(options_for_state_select(abbr, options.delete(:international)), @object.try(:state)), options, html_options)
77
+ end
78
+
79
+ protected
80
+
81
+ ##
82
+ #
83
+ # Returns a list of US states as an array
84
+ #
85
+ # @param [Boolean] abbreviate Abbreviate the value
86
+ # @param [Boolean, String] incl_international Include an additional state for "International"
87
+ #
88
+ # @return [Array] An array of states
89
+ #
90
+ def options_for_state_select(abbreviate = false, incl_international = false)
91
+ incl_international ||= false
92
+ state_list = [
93
+ ['Alabama', "AL"],['Alaska', "AK"],['Arizona', "AZ"],['Arkansas', "AR"],['California', "CA"],['Colorado', "CO"],
94
+ ['Connecticut', "CT"],['District of Columbia', "DC"],['Delaware', "DE"],['Florida', "FL"],['Georgia', "GA"],
95
+ ['Hawaii', "HI"],['Idaho', "ID"],['Illinois', "IL"],['Indiana', "IN"],['Iowa', "IA"],['Kansas', "KS"],['Kentucky', "KY"],
96
+ ['Louisiana', "LA"],['Maine', "ME"],['Maryland', "MD"],['Massachusetts', "MA"],['Michigan', "MI"],['Minnesota', "MN"],
97
+ ['Mississippi', "MS"],['Missouri', "MO"],['Montana', "MT"],['Nebraska', "NE"],['Nevada', "NV"],['New Hampshire', "NH"],
98
+ ['New Jersey', "NJ"],['New Mexico', "NM"],['New York', "NY"],['North Carolina', "NC"],['North Dakota', "ND"],
99
+ ['Ohio', "OH"],['Oklahoma', "OK"],['Oregon', "OR"],['Pennsylvania', "PA"],['Rhode Island', "RI"],['South Carolina', "SC"],
100
+ ['South Dakota', "SD"],['Tennessee', "TN"],['Texas', "TX"],['Utah', "UT"],['Vermont', "VT"],['Virginia', "VA"],['Washington', "WA"],
101
+ ['West Virginia', "WV"],['Wisconsin', "WI"],['Wyoming', "WY"]
102
+
103
+ ].map do |state|
104
+ (abbreviate ? state : [state.first, state.first])
105
+ end
106
+ state_list << ['International', incl_international] unless incl_international === false
107
+ state_list
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,29 @@
1
+ module Facades::Builders
2
+ module FormHelper
3
+ ##
4
+ #
5
+ # Cusomizes the default form_for helper to add additional functionality
6
+ # All params are the same as Rails' form_for
7
+ #
8
+ # @param [Mixed] record_name_or_array
9
+ # @param [Array] args
10
+ # @param [Block] &block
11
+ #
12
+ def form_for(record_name_or_array, *args, &proc)
13
+
14
+ raise ArgumentError, "Missing block" unless block_given?
15
+
16
+ options = args.extract_options!
17
+ options.reverse_merge!(:builder => Facades::Builders::Form)
18
+ options[:html] ||= {}
19
+ options[:html].merge!('data-js-validatable' => true) if options.delete(:validate)
20
+
21
+ default_proc = ::ActionView::Base.field_error_proc
22
+ ::ActionView::Base.field_error_proc = lambda{ |html_tag, instance_tag| html_tag }
23
+ super(record_name_or_array, *(args << options), &proc)
24
+ ensure
25
+ ::ActionView::Base.field_error_proc = default_proc
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ require 'facades/builders/form/base'
2
+ require 'facades/builders/form/elements'
3
+ require 'facades/builders/form/helper'
4
+
5
+ module Facades
6
+ module Builders
7
+ class Form < ActionView::Helpers::FormBuilder #:nodoc:
8
+ include Facades::Builders::FormBuilder::Base
9
+ include Facades::Builders::FormBuilder::Elements
10
+
11
+ def with_custom_error_proc(&block)
12
+ default_proc = ::ActionView::Base.field_error_proc
13
+ ::ActionView::Base.field_error_proc = lambda{ |html_tag, instance_tag| html_tag }
14
+ yield
15
+ ensure
16
+ ::ActionView::Base.field_error_proc = default_proc
17
+ end
18
+
19
+ ActionView::Base.send(:include, Facades::Builders::FormHelper)
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,5 @@
1
+ require 'rails'
2
+
1
3
  # Stub engine to integrate asset pipeline.
2
4
  module Facades
3
5
  class Engine < Rails::Engine
@@ -1,3 +1,3 @@
1
1
  module Facades
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/facades.rb CHANGED
@@ -14,6 +14,7 @@ module Facades
14
14
 
15
15
  module Builders
16
16
  autoload :Sprite, 'facades/builders/sprite'
17
+ autoload :Form, 'facades/builders/form'
17
18
  end
18
19
 
19
20
  # When enabled, HTML5 elements are used within helpers
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: facades
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-30 00:00:00.000000000Z
12
+ date: 2011-10-01 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sass
16
- requirement: &70151854513160 !ruby/object:Gem::Requirement
16
+ requirement: &70211602186260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70151854513160
24
+ version_requirements: *70211602186260
25
25
  description: ! 'Facades is a front-end development framework which takes '
26
26
  email:
27
27
  - brent@kurbmedia.com
@@ -37,6 +37,10 @@ files:
37
37
  - app/views/facades/_pagination.html.erb
38
38
  - facades.gemspec
39
39
  - lib/facades.rb
40
+ - lib/facades/builders/form.rb
41
+ - lib/facades/builders/form/base.rb
42
+ - lib/facades/builders/form/elements.rb
43
+ - lib/facades/builders/form/helper.rb
40
44
  - lib/facades/builders/sprite.rb
41
45
  - lib/facades/builders/table.rb
42
46
  - lib/facades/debug/html.rb