fluffery 0.0.1
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.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/fluffery.gemspec +31 -0
- data/lib/fluffery.rb +21 -0
- data/lib/fluffery/config.rb +29 -0
- data/lib/fluffery/forms/builder.rb +142 -0
- data/lib/fluffery/forms/utilities.rb +89 -0
- data/lib/fluffery/forms/validation/base.rb +178 -0
- data/lib/fluffery/forms/validation/validators.rb +21 -0
- data/lib/fluffery/helpers/elements.rb +57 -0
- data/lib/fluffery/helpers/includes.rb +20 -0
- data/lib/fluffery/helpers/navigation.rb +47 -0
- data/lib/fluffery/helpers/page_variables.rb +51 -0
- data/lib/fluffery/railtie.rb +14 -0
- data/lib/fluffery/utils/configurator.rb +9 -0
- data/lib/fluffery/utils/internal.rb +19 -0
- data/lib/fluffery/version.rb +3 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/fluffery.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fluffery/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
|
7
|
+
s.name = "fluffery"
|
8
|
+
s.version = Fluffery::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["kurb media"]
|
11
|
+
s.date = %q{2011-03-10}
|
12
|
+
s.email = %q{info@kurbmedia.com}
|
13
|
+
s.homepage = %q{http://github.com/kurbmedia/fluffery}
|
14
|
+
s.licenses = ["MIT"]
|
15
|
+
s.summary = %q{ummm.. Adds misc fluffery to yer apps.}
|
16
|
+
s.description = %q{Random fluffage for Rails applications.}
|
17
|
+
|
18
|
+
s.rubyforge_project = "fluffery"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
s.add_development_dependency(%q<rails>, ["~> 3.0.0"])
|
26
|
+
s.add_development_dependency(%q<actionpack>, ["~> 3.0.0"])
|
27
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3"])
|
28
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
29
|
+
|
30
|
+
|
31
|
+
end
|
data/lib/fluffery.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fluffery
|
2
|
+
|
3
|
+
autoload :Config, 'fluffery/config'
|
4
|
+
|
5
|
+
module Forms
|
6
|
+
autoload :Builder, 'fluffery/forms/builder'
|
7
|
+
autoload :Utilities, 'fluffery/forms/utilities'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Helpers
|
11
|
+
autoload :Navigation, 'fluffery/helpers/navigation'
|
12
|
+
autoload :PageVariables, 'fluffery/helpers/page_variables'
|
13
|
+
autoload :Elements, 'fluffery/helpers/elements'
|
14
|
+
autoload :Includes, 'fluffery/helpers/includes'
|
15
|
+
end
|
16
|
+
|
17
|
+
module Utils
|
18
|
+
autoload :Internal, 'fluffery/utils/internal'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fluffery
|
2
|
+
|
3
|
+
class Config
|
4
|
+
|
5
|
+
def self.forms
|
6
|
+
@forms ||= {
|
7
|
+
:error_class => 'field_with_errors',
|
8
|
+
:message_error_class => 'errors_for_field',
|
9
|
+
:error_template => %{
|
10
|
+
<span class="<%= error_class %>">
|
11
|
+
<%= html_tag %>
|
12
|
+
<span class="<%= message_error_class %>"><%= [messages].flatten.join(",") %></span>
|
13
|
+
</span>
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dom
|
19
|
+
@dom ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
yield Fluffery::Config if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Forms
|
3
|
+
|
4
|
+
class Builder < ActionView::Helpers::FormBuilder
|
5
|
+
|
6
|
+
include Fluffery::Forms::Utilities
|
7
|
+
|
8
|
+
# Access the template object
|
9
|
+
attr_accessor :template
|
10
|
+
|
11
|
+
# Sets up options custom to our form builder.
|
12
|
+
# It also overrides the default error proc so we can use something more custom.
|
13
|
+
#
|
14
|
+
def initialize(object_name, object, template, options, proc)
|
15
|
+
without_error_proc do
|
16
|
+
super(object_name, object, template, options, proc)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a html button tag for use in forms instead of the default input submit.
|
21
|
+
# Uses a span to wrap the content so it can be styled with css.
|
22
|
+
#
|
23
|
+
def button(value = nil, options = {})
|
24
|
+
value, options = nil, value if value.is_a?(Hash)
|
25
|
+
value ||= submit_default_value
|
26
|
+
|
27
|
+
options = confirm_or_disable(options)
|
28
|
+
options = Fluffery::Utils::Internal.merge_html_classes(options, 'button')
|
29
|
+
|
30
|
+
content_tag(:button, options.reverse_merge!({ "type" => "submit", "name" => "commit" })) do
|
31
|
+
content_tag(:span, value.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# Custom label tag including an asterisk if the field is required.
|
37
|
+
#
|
38
|
+
def label(method, text = nil, options = {}, &block)
|
39
|
+
|
40
|
+
options, text = text, nil if text.is_a?(Hash)
|
41
|
+
text ||= method.to_s.humanize
|
42
|
+
options.stringify_keys!
|
43
|
+
|
44
|
+
options.reverse_merge!('transform' => :titleize) unless option_exists?(options['transform'])
|
45
|
+
text.send(options['transform'].to_sym)
|
46
|
+
options.delete('transform')
|
47
|
+
|
48
|
+
# Check to see if :required is set on a label, or if the attribute/method has a validator that require it exists.
|
49
|
+
# If so, add a * to the label.
|
50
|
+
text = "#{text} <abbr title='Required'>*</abbr>".html_safe if validator.attribute_required?(method, options)
|
51
|
+
super(method, text, options, &block)
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def email_field(method, options = {})
|
56
|
+
render_with_fluff(method, options) do
|
57
|
+
super(method, options.merge!('type' => 'email'))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Renders the default date_select but with an error wrapping if
|
62
|
+
# the method in question has errors
|
63
|
+
#
|
64
|
+
def date_select(method, options = {}, html_options = {})
|
65
|
+
render_with_fluff(method, options, html_options) do
|
66
|
+
super(method, options, html_options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# HTML5 Number field, again, falls back to text in unsupportive browsers.
|
71
|
+
#
|
72
|
+
def number_field(method, range, options = {})
|
73
|
+
options, range = range, nil if range.is_a?(Hash)
|
74
|
+
options.stringify_keys!
|
75
|
+
unless range.nil?
|
76
|
+
range = (range.is_a?(Range)) ? range.to_a : range
|
77
|
+
options.merge!('min' => range.first, 'max' => range.last)
|
78
|
+
end
|
79
|
+
|
80
|
+
render_with_fluff(method, options) do
|
81
|
+
super(method, options.merge!('type' => 'number'))
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def password_field(method, options = {})
|
87
|
+
render_with_fluff(method, options) do
|
88
|
+
super(method, options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def select(method, choices, options = {}, html_options = {})
|
93
|
+
render_with_fluff(method, options, html_options) do
|
94
|
+
@template.select(@object_name, method, choices, options, html_options)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def state_select(method, options = {}, html_options = {})
|
99
|
+
|
100
|
+
state_options = [['Please Select',nil]]
|
101
|
+
state_options << ['International', (int || 'International')] if int = options.delete(:international)
|
102
|
+
[
|
103
|
+
['Alabama', "AL"],['Alaska', "AK"],['Arizona', "AZ"],['Arkansas', "AR"],['California', "CA"],['Colorado', "CO"],
|
104
|
+
['Connecticut', "CT"],['District of Columbia', "DC"],['Delaware', "DE"],['Florida', "FL"],['Georgia', "GA"],
|
105
|
+
['Hawaii', "HI"],['Idaho', "ID"],['Illinois', "IL"],['Indiana', "IN"],['Iowa', "IA"],['Kansas', "KS"],['Kentucky', "KY"],
|
106
|
+
['Louisiana', "LA"],['Maine', "ME"],['Maryland', "MD"],['Massachusetts', "MA"],['Michigan', "MI"],['Minnesota', "MN"],
|
107
|
+
['Mississippi', "MS"],['Missouri', "MO"],['Montana', "MT"],['Nebraska', "NE"],['Nevada', "NV"],['New Hampshire', "NH"],
|
108
|
+
['New Jersey', "NJ"],['New Mexico', "NM"],['New York', "NY"],['North Carolina', "NC"],['North Dakota', "ND"],
|
109
|
+
['Ohio', "OH"],['Oklahoma', "OK"],['Oregon', "OR"],['Pennsylvania', "PA"],['Rhode Island', "RI"],['South Carolina', "SC"],
|
110
|
+
['South Dakota', "SD"],['Tennessee', "TN"],['Texas', "TX"],['Utah', "UT"],['Vermont', "VT"],['Virginia', "VA"],['Washington', "WA"],
|
111
|
+
['West Virginia', "WV"],['Wisconsin', "WI"],['Wyoming', "WY"]
|
112
|
+
].each do |state|
|
113
|
+
should_abbr = options.delete(:abbreviate)
|
114
|
+
state_options << (should_abbr ? state : [state.first, state.first])
|
115
|
+
end
|
116
|
+
|
117
|
+
select(method, @template.options_for_select(state_options, @object.try(:state)), options, html_options)
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def text_field(method, options = {})
|
122
|
+
render_with_fluff(method, options) do
|
123
|
+
super(method, options)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def text_area(method, options = {})
|
128
|
+
render_with_fluff(method, options) do
|
129
|
+
super(method, options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def url_field(method, options = {})
|
134
|
+
render_with_fluff(method, options) do
|
135
|
+
super(method, options.merge!('type' => 'url'))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fluffery/forms/validation/base'
|
3
|
+
|
4
|
+
module Fluffery
|
5
|
+
module Forms
|
6
|
+
|
7
|
+
module Utilities
|
8
|
+
|
9
|
+
# Simply shortening calls to content_tag since it is a method of @template
|
10
|
+
#
|
11
|
+
def content_tag(tag, content, options = {}, escape = true, &block)
|
12
|
+
@template.content_tag(tag, content, options, escape, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds data-confirm, or data-disable-with if set.
|
16
|
+
#
|
17
|
+
def confirm_or_disable(options)
|
18
|
+
options.stringify_keys!
|
19
|
+
options.merge!("data-disable-with" => options["disable_with"]) if options.delete("disable_with")
|
20
|
+
options.merge!("data-confirm" => options["confirm"]) if options.delete("confirm")
|
21
|
+
options
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allows us to call something like f.error_console to log form errors
|
25
|
+
# to a javascript console such as firebug
|
26
|
+
#
|
27
|
+
def error_console
|
28
|
+
"<script type=\"text/javascript\" charset=\"utf-8\">
|
29
|
+
//<![CDATA[
|
30
|
+
try{ console.log(\"#{@template.escape_javascript(@object.errors.inspect)}\")}catch(e){}
|
31
|
+
//]]>
|
32
|
+
</script>".html_safe
|
33
|
+
end
|
34
|
+
|
35
|
+
# Quick helper to see if an option is nil, blank, or false
|
36
|
+
#
|
37
|
+
def option_exists?(opt)
|
38
|
+
!(opt.nil? || opt.to_s.blank? || opt.to_s === "false")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Generate additional options on our fields.
|
42
|
+
# 1. If a field has errors, wrap it with the defined error template.
|
43
|
+
# 2. Also add our error class to the field itself.
|
44
|
+
#
|
45
|
+
def render_with_fluff(method, options, html_options = nil, &block)
|
46
|
+
_options = html_options.nil? ? options : html_options
|
47
|
+
_options = validator.add_html_attributes(method, _options)
|
48
|
+
# If no errors, simply return.
|
49
|
+
unless validator.errors_for?(method)
|
50
|
+
return block.call
|
51
|
+
end
|
52
|
+
|
53
|
+
configs = Fluffery::Config.forms
|
54
|
+
template = configs[:error_template]
|
55
|
+
error_class = configs[:error_class]
|
56
|
+
_options = Fluffery::Utils::Internal.merge_html_classes(_options, error_class)
|
57
|
+
options = _options if html_options.nil?
|
58
|
+
|
59
|
+
# Capture the original html tag with any updated options.
|
60
|
+
html_tag = block.call
|
61
|
+
return html_tag if template.nil? or template.blank?
|
62
|
+
|
63
|
+
renderer = ERB.new(template)
|
64
|
+
messages = @object.errors[method.to_sym]
|
65
|
+
message_error_class = configs[:message_error_class]
|
66
|
+
renderer.result(
|
67
|
+
OpenStruct.new(configs.merge!(:messages => messages)).send(:binding)
|
68
|
+
).to_s.html_safe
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def validator
|
73
|
+
@validator ||= Fluffery::Forms::Validation::Base.new(@object)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Override the default error proc for our forms, making sure to
|
77
|
+
# set it back when we are finished to avoid compatibility issues.
|
78
|
+
#
|
79
|
+
def without_error_proc
|
80
|
+
default_proc = ActionView::Base.field_error_proc
|
81
|
+
ActionView::Base.field_error_proc, proc = lambda{ |html_tag, instance| html_tag }
|
82
|
+
yield
|
83
|
+
ActionView::Base.field_error_proc = default_proc
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Forms
|
3
|
+
module Validation
|
4
|
+
|
5
|
+
class Base
|
6
|
+
|
7
|
+
require 'fluffery/forms/validation/validators'
|
8
|
+
|
9
|
+
attr_accessor :object
|
10
|
+
|
11
|
+
def initialize(form_object)
|
12
|
+
@object = form_object
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_html_attributes(attribute, options)
|
16
|
+
options = Presence.create(attribute, options) if attribute_required?(attribute)
|
17
|
+
matcher = attribute_format_validator(attribute)
|
18
|
+
options = Pattern.create(attribute, options, matcher) unless matcher.nil?
|
19
|
+
options.reverse_merge!(default_messages_for(attribute))
|
20
|
+
options
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks to see if the particular attribute is required, used primarily on labels.
|
24
|
+
#
|
25
|
+
def attribute_required?(attribute, options = nil)
|
26
|
+
options.stringify_keys! if options.is_a?(Hash)
|
27
|
+
unless options.nil?
|
28
|
+
return true if options.has_key?('required') and options['required'] === true
|
29
|
+
end
|
30
|
+
valid_items = validators_for(attribute).find do |validator|
|
31
|
+
([:presence, :inclusion].include?(validator.kind)) &&
|
32
|
+
(validator.options.present? ? options_require_validation?(validator.options) : true)
|
33
|
+
end
|
34
|
+
!valid_items.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks to see if a particular attribute contains a Regex format validator
|
38
|
+
#
|
39
|
+
def attribute_format_validator(attribute)
|
40
|
+
format_validator = validators_for(attribute).detect{ |v| v.kind == :format }
|
41
|
+
return nil unless !format_validator.nil?
|
42
|
+
return nil unless format_validator.options.has_key?(:with) && format_validator.options[:with].is_a?(Regexp)
|
43
|
+
matcher = format_validator.options[:with]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Looks up the default error message so it may be used in our data-message attribute
|
47
|
+
#
|
48
|
+
def default_messages_for(attribute)
|
49
|
+
validators = validators_for(attribute)
|
50
|
+
return {} if validators.empty?
|
51
|
+
message_data = validators.inject({}) do |hash, validator|
|
52
|
+
message = validator.options.has_key?(:message) ? validator.options[:message] : MessageBuilder.message_for(@object, attribute, validator)
|
53
|
+
hash.merge!(validator.kind.to_s => message)
|
54
|
+
end
|
55
|
+
attr_hash = {'data-validation-messages' => CGI::escape(message_data.to_json) }
|
56
|
+
|
57
|
+
# Create a list of data-validates-* attrs on the field so we can catch them with javascript
|
58
|
+
# Skip presence and format because they have their own valid HTML5 attrs.
|
59
|
+
#
|
60
|
+
validators.reject{ |v| v.kind.to_s.match(/(presence|format)/i) }.each{ |v| attr_hash.merge!("data-validates-#{v.kind.to_s}" => 'true') }
|
61
|
+
|
62
|
+
attr_hash
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# Checks to see if the particular attribute has errors
|
67
|
+
#
|
68
|
+
def errors_for?(method)
|
69
|
+
!(@object.nil? || @object.errors.empty? || !@object.errors.key?(method.to_sym) || [@object.errors[method.to_sym]].flatten.empty?)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Checks to see if the validation is required
|
73
|
+
# Courtesy Justin French and Formtastic
|
74
|
+
#
|
75
|
+
def options_require_validation?(options)
|
76
|
+
allow_blank = options[:allow_blank]
|
77
|
+
return !allow_blank unless allow_blank.nil?
|
78
|
+
if_condition = !options[:if].nil?
|
79
|
+
condition = if_condition ? options[:if] : options[:unless]
|
80
|
+
|
81
|
+
condition = if condition.respond_to?(:call)
|
82
|
+
condition.call(@object)
|
83
|
+
elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
|
84
|
+
@object.send(condition)
|
85
|
+
else
|
86
|
+
condition
|
87
|
+
end
|
88
|
+
|
89
|
+
if_condition ? !!condition : !condition
|
90
|
+
end
|
91
|
+
|
92
|
+
# Finds all existing validations for the current object and method
|
93
|
+
#
|
94
|
+
def validators_for(attribute)
|
95
|
+
return [] unless !@object.nil? and @object.class.respond_to?(:validators_on)
|
96
|
+
attribute = attribute.to_s.sub(/_id$/, '').to_sym
|
97
|
+
validators = @object.class.validators_on(attribute).uniq
|
98
|
+
end
|
99
|
+
|
100
|
+
def validators_for?(method)
|
101
|
+
!validators_for(method).empty?
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
class MessageBuilder
|
107
|
+
def self.message_for(object, attribute, validator)
|
108
|
+
message = self.get_validation_message(validator)
|
109
|
+
message = self.defaults(validator.kind) unless message.match(/translation missing/i).nil?
|
110
|
+
message
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def self.defaults(validator)
|
116
|
+
{
|
117
|
+
:presence => "required",
|
118
|
+
:format => 'invalid',
|
119
|
+
:length => "wrong length",
|
120
|
+
:numericality => "not a number",
|
121
|
+
:uniqueness => 'taken',
|
122
|
+
:confirmation => 'required',
|
123
|
+
:acceptance => 'required',
|
124
|
+
:inclusion => 'invalid',
|
125
|
+
:exclusion => 'invalid'
|
126
|
+
}[validator]
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.get_validation_message(validator)
|
130
|
+
key = {
|
131
|
+
:presence => "blank",
|
132
|
+
:format => 'invalid',
|
133
|
+
:length => self.length_options(validator.options),
|
134
|
+
:numericality => self.numericality_options(validator.options),
|
135
|
+
:uniqueness => 'taken',
|
136
|
+
:confirmation => 'confirmation',
|
137
|
+
:acceptance => 'accepted',
|
138
|
+
:inclusion => 'inclusion',
|
139
|
+
:exclusion => 'exclusion'
|
140
|
+
}[validator.kind]
|
141
|
+
key.is_a?(Array) ? I18n.translate("errors.messages.#{key.first}").sub("%{#{:count}}", key.last.to_s) : I18n.translate("errors.messages.#{key}")
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.length_options(opts)
|
145
|
+
if count = opts[:is]
|
146
|
+
["wrong_length", count]
|
147
|
+
elsif count = opts[:minimum]
|
148
|
+
["too_short", count]
|
149
|
+
elsif count = opts[:maximum]
|
150
|
+
["too_long", count]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.numericality_options(opts)
|
155
|
+
if opts[:only_integer]
|
156
|
+
'not_a_number'
|
157
|
+
elsif count = opts[:greater_than]
|
158
|
+
['greater_than', count]
|
159
|
+
elsif count = opts[:greater_than_or_equal_to]
|
160
|
+
['greater_than_or_equal_to', count]
|
161
|
+
elsif count = opts[:less_than]
|
162
|
+
['less_than', count]
|
163
|
+
elsif count = opts[:less_than_or_equal_to]
|
164
|
+
['less_than_or_equal_to', count]
|
165
|
+
elsif opts[:odd]
|
166
|
+
'odd'
|
167
|
+
elsif opts[:even]
|
168
|
+
'even'
|
169
|
+
else
|
170
|
+
'not_a_number'
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Forms
|
3
|
+
module Validation
|
4
|
+
|
5
|
+
class Presence < Fluffery::Forms::Validation::Base
|
6
|
+
def self.create(attribute ,options)
|
7
|
+
options.merge!('required' => 'required') unless options.has_key?('required') && options['required'] === false
|
8
|
+
options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Pattern < Fluffery::Forms::Validation::Base
|
13
|
+
def self.create(attribute, options, matcher)
|
14
|
+
options.reverse_merge!('pattern' => matcher.inspect) unless matcher.nil?
|
15
|
+
options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
module Elements
|
5
|
+
|
6
|
+
# Simply overriding the default form_for to add additional html options/attributes.
|
7
|
+
def form_for(record_or_name_or_array, *args, &block)
|
8
|
+
|
9
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
10
|
+
options[:html] ||= {}
|
11
|
+
|
12
|
+
# Add support for the 'ajax' uploader.
|
13
|
+
options[:html].merge!('data-remote-uploadable' => true) if options.delete(:remote_upload)
|
14
|
+
# Add support for javascript validation.
|
15
|
+
options[:html].merge!('data-js-validatable' => true) if options.delete(:validate)
|
16
|
+
|
17
|
+
args = (args << options)
|
18
|
+
super(record_or_name_or_array, *args, &block)
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a common format for CSS buttons
|
23
|
+
# Example: button_link('Blog', '/blog') yields <a href="/blog" class="button"><span>Blog</span></a>
|
24
|
+
|
25
|
+
def button_link(txt, path, attrs = {})
|
26
|
+
inline_classes = attrs.has_key?(:class) ? "#{attrs[:class]} " : ""
|
27
|
+
link_to "<span>#{txt}</span>".html_safe, path, attrs.merge({ :class => "#{inline_classes}button" })
|
28
|
+
end
|
29
|
+
|
30
|
+
# Outputs all flash messages. This allows you to call <%= flash_messages %> once in your
|
31
|
+
# application's layout file. The output is a div with the class flash_message, and the type used. It also renders
|
32
|
+
# a "close" tag, which can be overridden or set to "". By default this is a span containing 'X'
|
33
|
+
#
|
34
|
+
# <div class="flash_message error">
|
35
|
+
# Your error message from flash[:error]
|
36
|
+
# <span>X</span>
|
37
|
+
# </div>
|
38
|
+
#
|
39
|
+
#
|
40
|
+
def flash_messages(attrs = {})
|
41
|
+
wrapper = attrs.delete(:wrapper) || :div
|
42
|
+
closer = attrs.delete(:close) || "<span>X</span>"
|
43
|
+
classes = (attrs.key?(:class)) ? attrs[:class].split(' ') : []
|
44
|
+
classes << "flash_message"
|
45
|
+
content = ""
|
46
|
+
flash.each_key do |k|
|
47
|
+
classes << "flash_message_#{k.to_s.underscore}"
|
48
|
+
msg_attrs = attrs.merge(:class => [k.to_s, classes].flatten.join(' '))
|
49
|
+
content.concat content_tag(wrapper, "#{flash[k]} #{closer}".html_safe, msg_attrs).html_safe
|
50
|
+
end
|
51
|
+
content.html_safe
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
module Includes
|
5
|
+
|
6
|
+
# Wraps output in IE conditional tags, the condition should be specified in the same
|
7
|
+
# format as it would in the conditional itself. For example:
|
8
|
+
# ie_conditional('lte IE 8') yields [if lte IE 8]
|
9
|
+
#
|
10
|
+
def ie_conditional(condition, &block)
|
11
|
+
output = ["<!--[if #{condition}]>"]
|
12
|
+
output << capture(&block)
|
13
|
+
output << "<![endif]-->"
|
14
|
+
output.join("\n").html_safe
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Helpers
|
3
|
+
module Navigation
|
4
|
+
|
5
|
+
# Creates a navigation link (by default a list item) that has the class 'on' if we are on that particular page or a
|
6
|
+
# page beneath it.
|
7
|
+
# Example: nav_link_to('Blog', 'blog') will have the class of 'on' should we be on /blog or /blog/some_path
|
8
|
+
|
9
|
+
def nav_link_to(txt, path, attrs = {}, wrapper = :li)
|
10
|
+
|
11
|
+
path_validation = false
|
12
|
+
path_validation = attrs.has_key?(:proc) ? attrs[:proc].call(path) : (request.path == path || request.path.match(/#{path}\/.*/i))
|
13
|
+
path_validation = request.path.match(/#{attrs[:matcher].gsub("_path", path)}/i) if attrs.has_key?(:matcher)
|
14
|
+
attrs.delete(:proc)
|
15
|
+
attrs.delete(:matcher)
|
16
|
+
|
17
|
+
wrapper_attrs = attrs.delete(:wrapper)
|
18
|
+
attrs, wrapper_attrs = merge_html_classes(attrs, path_validation), merge_html_classes(wrapper_attrs, path_validation)
|
19
|
+
|
20
|
+
return_val = link_to_unless(path_validation, txt, path, attrs) do
|
21
|
+
link_to(txt, path, attrs)
|
22
|
+
end
|
23
|
+
|
24
|
+
(wrapper.eql? false) ? return_val : content_tag(wrapper, return_val, wrapper_attrs)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a nested navigation block, generally used for lists/nested lists.
|
29
|
+
def navigation(txt, path, attrs = {}, options = {}, &block)
|
30
|
+
options.reverse_merge!(:container => :ol, :wrapper => :li)
|
31
|
+
navigation_content = content_tag(options[:container], capture(&block))
|
32
|
+
wrapper_attrs = attrs.delete(:wrapper)
|
33
|
+
parent_link = nav_link_to(txt, path, attrs, false)
|
34
|
+
content_tag(options[:wrapper], "#{parent_link}\n#{navigation_content}".html_safe, (wrapper_attrs || {}))
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def merge_html_classes(attrs, add_on = false)
|
40
|
+
attrs ||= {}
|
41
|
+
Fluffery::Utils::Internal.merge_html_classes(attrs, 'on') if add_on
|
42
|
+
attrs
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
module PageVariables
|
5
|
+
|
6
|
+
def page_default(param, data = nil)
|
7
|
+
create_variable("helpful_#{param}", data) unless data.nil? or data.blank?
|
8
|
+
end
|
9
|
+
|
10
|
+
def page_id(content = nil)
|
11
|
+
create_internal_variable(:helpful_page_id, content) and return unless content.nil?
|
12
|
+
return retrieve_variable(:helpful_page_id) if content_for?(:helpful_page_id)
|
13
|
+
cname = controller.class.to_s.gsub(/controller$/i,'').underscore.split("/").join('_')
|
14
|
+
aname = controller.action_name
|
15
|
+
"#{cname}_#{aname}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def page_title(content = nil)
|
19
|
+
(content.nil?) ? retrieve_variable(:helpful_page_title) : create_internal_variable(:helpful_page_title, content)
|
20
|
+
end
|
21
|
+
|
22
|
+
def keywords(content = nil)
|
23
|
+
(content.nil?) ? retrieve_variable(:helpful_keywords) : create_internal_variable(:helpful_keywords, content)
|
24
|
+
end
|
25
|
+
|
26
|
+
def description(content = nil)
|
27
|
+
(content.nil?) ? retrieve_variable(:helpful_description) : create_internal_variable(:helpful_description, content)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_variable(var, content, new_method = true)
|
31
|
+
content_for(var.to_sym) do
|
32
|
+
content
|
33
|
+
end
|
34
|
+
self.class_eval{ define_method("#{var.to_s}"){ retrieve_variable(var.to_sym) } } if new_method
|
35
|
+
return ""
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_internal_variable(var, content)
|
41
|
+
create_variable(var, content, false)
|
42
|
+
end
|
43
|
+
|
44
|
+
def retrieve_variable(var)
|
45
|
+
(content_for?(var.to_sym) ? content_for(var.to_sym) : "")
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fluffery
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
|
4
|
+
initializer :after_initialize do
|
5
|
+
# Configure ActionView with any addons.
|
6
|
+
ActionView::Base.send :include, Fluffery::Helpers::Navigation
|
7
|
+
ActionView::Base.send :include, Fluffery::Helpers::PageVariables
|
8
|
+
ActionView::Base.send :include, Fluffery::Helpers::Elements
|
9
|
+
ActionView::Base.send :include, Fluffery::Helpers::Includes
|
10
|
+
ActionView::Base.send :default_form_builder=, Fluffery::Forms::Builder
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Fluffery
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
class Internal
|
5
|
+
|
6
|
+
# Merge any new classes with existing html classes
|
7
|
+
#
|
8
|
+
def self.merge_html_classes(options, classes)
|
9
|
+
classes = [classes].flatten
|
10
|
+
options.stringify_keys!
|
11
|
+
return options.merge!('class' => classes.join(' ')) unless options.has_key?('class')
|
12
|
+
old_classes = options['class'].split(' ')
|
13
|
+
options.merge!('class' => [classes, old_classes].flatten.join(' '))
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluffery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kurb media
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-10 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rails
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.0.0
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: actionpack
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 3.0.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "2.3"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: bundler
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.0.0
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
description: Random fluffage for Rails applications.
|
61
|
+
email: info@kurbmedia.com
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files: []
|
67
|
+
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- Rakefile
|
72
|
+
- fluffery.gemspec
|
73
|
+
- lib/fluffery.rb
|
74
|
+
- lib/fluffery/config.rb
|
75
|
+
- lib/fluffery/forms/builder.rb
|
76
|
+
- lib/fluffery/forms/utilities.rb
|
77
|
+
- lib/fluffery/forms/validation/base.rb
|
78
|
+
- lib/fluffery/forms/validation/validators.rb
|
79
|
+
- lib/fluffery/helpers/elements.rb
|
80
|
+
- lib/fluffery/helpers/includes.rb
|
81
|
+
- lib/fluffery/helpers/navigation.rb
|
82
|
+
- lib/fluffery/helpers/page_variables.rb
|
83
|
+
- lib/fluffery/railtie.rb
|
84
|
+
- lib/fluffery/utils/configurator.rb
|
85
|
+
- lib/fluffery/utils/internal.rb
|
86
|
+
- lib/fluffery/version.rb
|
87
|
+
has_rdoc: true
|
88
|
+
homepage: http://github.com/kurbmedia/fluffery
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: "0"
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project: fluffery
|
111
|
+
rubygems_version: 1.6.1
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: ummm.. Adds misc fluffery to yer apps.
|
115
|
+
test_files: []
|
116
|
+
|