fluffery 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|