storefront 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/Rakefile +76 -0
  2. data/init.rb +1 -0
  3. data/lib/storefront.rb +21 -0
  4. data/lib/storefront/dashboard.rb +184 -0
  5. data/lib/storefront/form.rb +65 -0
  6. data/lib/storefront/form/elements.rb +101 -0
  7. data/lib/storefront/form/errors.rb +57 -0
  8. data/lib/storefront/form/fields.rb +420 -0
  9. data/lib/storefront/form/hints.rb +18 -0
  10. data/lib/storefront/form/inputs.rb +254 -0
  11. data/lib/storefront/form/labels.rb +81 -0
  12. data/lib/storefront/form/model.rb +84 -0
  13. data/lib/storefront/helpers/attribute_helper.rb +60 -0
  14. data/lib/storefront/helpers/body_helper.rb +18 -0
  15. data/lib/storefront/helpers/browser_helper.rb +54 -0
  16. data/lib/storefront/helpers/cache_helper.rb +25 -0
  17. data/lib/storefront/helpers/collection_helper.rb +14 -0
  18. data/lib/storefront/helpers/component_helper.rb +35 -0
  19. data/lib/storefront/helpers/dashboard_helper.rb +37 -0
  20. data/lib/storefront/helpers/debug_helper.rb +11 -0
  21. data/lib/storefront/helpers/definition_list_helper.rb +29 -0
  22. data/lib/storefront/helpers/error_helper.rb +11 -0
  23. data/lib/storefront/helpers/flash_helper.rb +14 -0
  24. data/lib/storefront/helpers/form_helper.rb +8 -0
  25. data/lib/storefront/helpers/format_helper.rb +59 -0
  26. data/lib/storefront/helpers/head_helper.rb +229 -0
  27. data/lib/storefront/helpers/image_helper.rb +54 -0
  28. data/lib/storefront/helpers/list_helper.rb +47 -0
  29. data/lib/storefront/helpers/locale_helper.rb +175 -0
  30. data/lib/storefront/helpers/open_graph_helper.rb +5 -0
  31. data/lib/storefront/helpers/page_helper.rb +73 -0
  32. data/lib/storefront/helpers/presenter_helper.rb +5 -0
  33. data/lib/storefront/helpers/request_helper.rb +66 -0
  34. data/lib/storefront/helpers/semantic/location_helper.rb +18 -0
  35. data/lib/storefront/helpers/semantic/time_helper.rb +14 -0
  36. data/lib/storefront/helpers/sidebar_helper.rb +27 -0
  37. data/lib/storefront/helpers/system_helper.rb +18 -0
  38. data/lib/storefront/helpers/table_helper.rb +38 -0
  39. data/lib/storefront/helpers/time_helper.rb +20 -0
  40. data/lib/storefront/helpers/url_helper.rb +32 -0
  41. data/lib/storefront/helpers/user_helper.rb +15 -0
  42. data/lib/storefront/helpers/widget_helper.rb +10 -0
  43. data/lib/storefront/helpers/workflow_helper.rb +50 -0
  44. data/lib/storefront/microdata/address.rb +7 -0
  45. data/lib/storefront/microdata/event.rb +13 -0
  46. data/lib/storefront/microdata/geo.rb +7 -0
  47. data/lib/storefront/microdata/organization.rb +7 -0
  48. data/lib/storefront/microdata/person.rb +7 -0
  49. data/lib/storefront/microformat/event.rb +7 -0
  50. data/lib/storefront/microformat/person.rb +7 -0
  51. data/lib/storefront/railtie.rb +28 -0
  52. data/lib/storefront/table.rb +191 -0
  53. data/rails/init.rb +1 -0
  54. data/test/form_helper_test.rb +5 -0
  55. data/test/support/mock_controller.rb +15 -0
  56. data/test/support/mock_response.rb +14 -0
  57. data/test/support/models.rb +0 -0
  58. data/test/test_helper.rb +34 -0
  59. metadata +111 -0
@@ -0,0 +1,18 @@
1
+ module Storefront
2
+ class Form
3
+ module Hints
4
+ def hints_for(key, options = {})
5
+ value = options[:hint_attributes].delete(:value)
6
+ template.capture_haml do
7
+ if value.present?
8
+ template.haml_tag :figure, options[:hint_attributes] do
9
+ template.haml_concat value.html_safe
10
+ end
11
+ else
12
+ template.haml_tag :figure, options[:hint_attributes]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,254 @@
1
+ module Storefront
2
+ class Form
3
+ module Inputs
4
+ def input_for(key, options)
5
+ send(:"#{options.delete(:as)}_input", key, options)
6
+ end
7
+
8
+ def default_input_type(method, options = {}) #:nodoc:
9
+ if column = column_for(method)
10
+ # Special cases where the column type doesn't map to an input method.
11
+ case column.type
12
+ when :string
13
+ return :password if method.to_s =~ /password/
14
+ return :country if method.to_s =~ /country$/
15
+ return :time_zone if method.to_s =~ /time_zone/
16
+ return :email if method.to_s =~ /email/
17
+ return :url if method.to_s =~ /^url$|^website$|_url$/
18
+ return :phone if method.to_s =~ /(phone|fax)/
19
+ return :search if method.to_s =~ /^search$/
20
+ when :integer
21
+ return :select if reflection_for(method)
22
+ return :numeric
23
+ when :float, :decimal
24
+ return :numeric
25
+ when :timestamp
26
+ return :datetime
27
+ end
28
+
29
+ # Try look for hints in options hash. Quite common senario: Enum keys stored as string in the database.
30
+ return :select if column.type == :string && options.key?(:collection)
31
+ # Try 3: Assume the input name will be the same as the column type (e.g. string_input).
32
+ return column.type
33
+ else
34
+ if @object
35
+ return :select if reflection_for(method)
36
+
37
+ return :file if is_file?(method, options)
38
+ end
39
+
40
+ return :select if options.key?(:collection)
41
+ return :password if method.to_s =~ /password/
42
+ return :string
43
+ end
44
+ end
45
+
46
+ def is_file?(method, options = {})
47
+ @files ||= {}
48
+ @files[method] ||= (options[:as].present? && options[:as] == :file) || begin
49
+ file = @object.send(method) if @object && @object.respond_to?(method)
50
+ file && file_methods.any?{|m| file.respond_to?(m)}
51
+ end
52
+ end
53
+
54
+ def file_methods
55
+ [:file?, :public_filename, :filename]
56
+ end
57
+
58
+
59
+ def base_input(input_type, key, options = {}, &block)
60
+ if [:numeric, :string, :password, :text, :phone, :url, :email].include?(input_type)
61
+ attributes = default_string_options(key, input_type).merge(options[:input_attributes])
62
+ else
63
+ attributes = options[:input_attributes]
64
+ end
65
+ merge_class! attributes, key.to_s, options[:class]
66
+ attributes[:required] = true if options[:required] == true
67
+ template.capture_haml do
68
+ template.haml_tag :input, attributes.merge(:type => input_type), &block
69
+ end
70
+ end
71
+
72
+ def core_input(tag, *args, &block)
73
+ options = args.extract_options!
74
+ attributes = options[:input_attributes]
75
+ attributes[:required] = true if options[:required] == true
76
+ template.capture_haml do
77
+ if block_given?
78
+ template.haml_tag tag, attributes, &block
79
+ else
80
+ template.haml_tag tag, args.shift, attributes
81
+ end
82
+ end
83
+ end
84
+
85
+ # autofocus, pattern, placeholder, title, size, data-validate, data-validate-match, data-validate-unique
86
+ def string_input(key, attributes = {})
87
+ base_input :string, key, attributes
88
+ end
89
+
90
+ # maxlength, placeholder, required, wrap, readonly
91
+ def textarea_input(key, options = {})
92
+ attributes = options[:input_attributes]
93
+ value = attributes.delete(:value)
94
+ core_input :textarea, value, attributes
95
+ end
96
+ alias_method :text_input, :textarea_input
97
+
98
+ def checkbox_input(key, attributes = {})
99
+ hidden_input key, attributes.merge(:value => 0)
100
+ base_input :checkbox, key, attributes.merge(:value => 1)
101
+ end
102
+
103
+ # prompt, blank, multiple
104
+ def select_input(key, options = {})
105
+ collection = Array(options.delete(:collection) || [])
106
+ if options[:include_blank] != false
107
+ prompt = options[:prompt] || ""
108
+ collection = [[prompt, ""]] + collection
109
+ end
110
+ attributes = options[:input_attributes]
111
+ selected = attributes.delete(:value)
112
+ core_input :select, options do
113
+ collection.map do |item|
114
+ name, options = item, {}
115
+ case item
116
+ when Array
117
+ name = item[0]
118
+ options[:value] = item[1]
119
+ when Hash
120
+ name = item[:name]
121
+ options[:value] = item[:value]
122
+ else
123
+ options[:value] = item
124
+ end
125
+ options[:selected] = "true" if selected.present? && options[:value] == selected
126
+ template.haml_tag :option, name, options
127
+ end
128
+ end
129
+ end
130
+
131
+ def hidden_input(key, attributes = {})
132
+ base_input :hidden, key, attributes
133
+ end
134
+
135
+ def number_input(key, attributes = {})
136
+ base_input :string, key, attributes.merge(:class => "number", "data-type" => "number")
137
+ end
138
+
139
+ def search_input(key, attributes = {})
140
+ base_input :search, key, merge_class(attributes, "search").merge("data-type" => "search")
141
+ end
142
+
143
+ def boolean_input(key, attributes = {})
144
+ select_input(key, attributes.merge(:collection => [["Yes", "1"], ["No", "0"]]))
145
+ #checkbox_input(key, attributes = {})
146
+ end
147
+
148
+ # accept, maxlength="2"
149
+ def file_input(key, attributes = {})
150
+ base_input :file, key, attributes
151
+ end
152
+
153
+ def password_input(key, attributes = {})
154
+ base_input :password, key, attributes
155
+ end
156
+
157
+ def email_input(key, attributes = {})
158
+ base_input :email, key, attributes
159
+ end
160
+
161
+ def url_input(key, attributes = {})
162
+ base_input :url, key, attributes
163
+ end
164
+
165
+ def phone_input(key, attributes = {})
166
+ base_input :tel, key, attributes.merge(:class => "phone")
167
+ end
168
+
169
+ def fax_input(key, attributes = {})
170
+ base_input :tel, key, attributes.merge(:class => "phone fax")
171
+ end
172
+
173
+ def date_input(key, attributes = {})
174
+ base_input :string, key, attributes.merge(:class => "date", "data-type" => "date")
175
+ end
176
+
177
+ def money_input(key, attributes = {})
178
+ base_input :string, key, merge_class(attributes, "money").merge("data-type" => "money")
179
+ end
180
+
181
+ def percent_input(key, attributes = {})
182
+ base_input :string, key, merge_class(attributes, "percent").merge("data-type" => "percent")
183
+ end
184
+
185
+ def watched_input(key, attributes = {})
186
+ text_input key, merge_class(attributes, "watch-characters").merge(:"data-character-count" => (attributes.delete(:count) || 100))
187
+ # haml_tag :figure, :class => "watched"
188
+ end
189
+
190
+ def color_input(key, attributes = {})
191
+
192
+ end
193
+
194
+ def range_input(key, attributes = {})
195
+
196
+ end
197
+
198
+ def autocomplete_input(key, attributes = {})
199
+
200
+ end
201
+
202
+ def date_range_input(key, attributes = {})
203
+
204
+ end
205
+
206
+ def slider_input(key, attributes = {})
207
+
208
+ end
209
+
210
+ def state_input(key, attributes = {})
211
+
212
+ end
213
+
214
+ def partial(key, attributes = {})
215
+
216
+ end
217
+
218
+ def input_id(attribute, name = "input")
219
+ ([keys] + [@index ? @index.to_s : nil, attribute]).compact.join("-").gsub("_", "-") + "-#{name}"
220
+ end
221
+
222
+ def input_name(attribute, options = {})
223
+ param_for([keys] + [@index ? @index.to_s : nil, attribute])
224
+ end
225
+
226
+ def input_value(attribute, default = nil)
227
+ return default if default.present?
228
+
229
+ if object.respond_to?(attribute)
230
+ object.send(attribute)
231
+ else
232
+ nil
233
+ end
234
+ end
235
+
236
+ ## FORMTASTIC STUFF
237
+ def create_boolean_collection(options) #:nodoc:
238
+ options[:true] ||= ::Formtastic::I18n.t(:yes)
239
+ options[:false] ||= ::Formtastic::I18n.t(:no)
240
+ options[:value_as_class] = true unless options.key?(:value_as_class)
241
+
242
+ [ [ options.delete(:true), true], [ options.delete(:false), false ] ]
243
+ end
244
+
245
+ def validation_max_limit
246
+ 255
247
+ end
248
+
249
+ def default_text_field_size
250
+ nil
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,81 @@
1
+ module Storefront
2
+ class Form
3
+ module Labels
4
+ def label_for(key, options = {})
5
+ attributes = options[:label_attributes]
6
+ text = attributes.delete(:value)
7
+ result = template.capture_haml do
8
+ template.haml_tag :label, options[:label_attributes] do
9
+ template.haml_tag :span, text
10
+ if options[:required] == true
11
+ template.haml_tag :abbr, "*", :title => "required"
12
+ else
13
+ template.haml_tag :abbr, "", :title => "optional"
14
+ end
15
+ end
16
+ end
17
+ result
18
+ end
19
+
20
+ def label_method
21
+ :titleize
22
+ end
23
+
24
+ def humanized_attribute_name(method) #:nodoc:
25
+ if @object && @object.class.respond_to?(:human_attribute_name)
26
+ humanized_name = @object.class.human_attribute_name(method.to_s)
27
+ if humanized_name == method.to_s.send(:humanize)
28
+ method.to_s.send(label_method)
29
+ else
30
+ humanized_name
31
+ end
32
+ else
33
+ method.to_s.send(label_method)
34
+ end
35
+ end
36
+
37
+ def localized_string(key, value, type, options = {}) #:nodoc:
38
+ key = value if value.is_a?(::Symbol)
39
+
40
+ if value.is_a?(::String)
41
+ value.html_safe
42
+ else
43
+ use_i18n = true
44
+
45
+ if use_i18n
46
+ model_name, nested_model_name = normalize_model_name(self.model_name.underscore)
47
+ action_name = template.params[:action].to_s rescue ''
48
+ attribute_name = key.to_s
49
+
50
+ defaults = ::Formtastic::I18n::SCOPES.reject do |i18n_scope|
51
+ nested_model_name.nil? && i18n_scope.match(/nested_model/)
52
+ end.collect do |i18n_scope|
53
+ i18n_path = i18n_scope.dup
54
+ i18n_path.gsub!('%{action}', action_name)
55
+ i18n_path.gsub!('%{model}', model_name)
56
+ i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
57
+ i18n_path.gsub!('%{attribute}', attribute_name)
58
+ i18n_path.gsub!('..', '.')
59
+ i18n_path.to_sym
60
+ end
61
+ defaults << ''
62
+
63
+ defaults.uniq!
64
+
65
+ default_key = defaults.shift
66
+ i18n_value = ::Formtastic::I18n.t(default_key,
67
+ options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
68
+ if i18n_value.blank? && type == :label
69
+ # This is effectively what Rails label helper does for i18n lookup
70
+ options[:scope] = [:helpers, type]
71
+ options[:default] = defaults
72
+ i18n_value = ::I18n.t(default_key, options)
73
+ end
74
+
75
+ i18n_value.blank? ? nil : i18n_value
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,84 @@
1
+ module Storefront
2
+ class Form
3
+ module Model
4
+ def model_name
5
+ @object.present? ? @object.class.name : @object_name.to_s.classify
6
+ end
7
+
8
+ def normalize_model_name(name)
9
+ if name =~ /(.+)\[(.+)\]/
10
+ [$1, $2]
11
+ else
12
+ [name]
13
+ end
14
+ end
15
+
16
+ # If an association method is passed in (f.input :author) try to find the
17
+ # reflection object.
18
+ #
19
+ def reflection_for(method) #:nodoc:
20
+ @object.class.reflect_on_association(method) if @object.class.respond_to?(:reflect_on_association)
21
+ end
22
+
23
+ # Get a column object for a specified attribute method - if possible.
24
+ #
25
+ def column_for(method) #:nodoc:
26
+ @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
27
+ end
28
+
29
+ def validations_for(method, mode = :active)
30
+ # ActiveModel?
31
+ validations = if @object && @object.class.respond_to?(:validators_on)
32
+ @object.class.validators_on(method)
33
+ else
34
+ # ValidationReflection plugin?
35
+ if @object && @object.class.respond_to?(:reflect_on_validations_for)
36
+ @object.class.reflect_on_validations_for(method)
37
+ else
38
+ []
39
+ end
40
+ end
41
+
42
+ validations = validations.select do |validation|
43
+ (validation.options.present? ? options_require_validation?(validation.options) : true)
44
+ end unless mode == :all
45
+
46
+ return validations
47
+ end
48
+
49
+ def sanitized_object_name #:nodoc:
50
+ @sanitized_object_name ||= @object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
51
+ end
52
+
53
+ def get_maxlength_for(method)
54
+ validation = validations_for(method).find do |validation|
55
+ (validation.respond_to?(:macro) && validation.macro == :validates_length_of) || # Rails 2 validation
56
+ (validation.respond_to?(:kind) && validation.kind == :length) # Rails 3 validator
57
+ end
58
+
59
+ if validation
60
+ validation.options[:maximum] || (validation.options[:within].present? ? validation.options[:within].max : nil)
61
+ else
62
+ nil
63
+ end
64
+ end
65
+
66
+ def default_string_options(method, type) #:nodoc:
67
+ validation_max_limit = get_maxlength_for(method)
68
+ column = column_for(method)
69
+
70
+ if type == :text
71
+ { :rows => default_text_area_height,
72
+ :cols => default_text_area_width }
73
+ elsif type == :numeric || column.nil? || !column.respond_to?(:limit) || column.limit.nil?
74
+ { :maxlength => validation_max_limit,
75
+ :size => default_text_field_size }
76
+ else
77
+ { :maxlength => validation_max_limit || column.limit,
78
+ :size => default_text_field_size }
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,60 @@
1
+ module Storefront
2
+ module AttributeHelper
3
+ def underscore_class_name(record)
4
+ record.class.base_class.snake_case
5
+ end
6
+
7
+ def datetime_for(time)
8
+ with_time_at time
9
+ end
10
+
11
+ def record_method(method, *args, &block)
12
+ record = args.shift
13
+ sent_method = "#{method}_#{underscore_class_name(record)}"
14
+ if args.length > 0 && options = args.pop
15
+ send(sent_method, record, options, &block)
16
+ else
17
+ send(sent_method, record, &block)
18
+ end
19
+ end
20
+
21
+ def merge_class!(attributes, *values)
22
+ return {} if attributes.nil?
23
+ hash = merge_class(attributes, *values)
24
+ attributes[:class] = hash[:class] if hash
25
+ attributes
26
+ end
27
+
28
+ def merge_class(attributes, *values)
29
+ if values.present?
30
+ if attributes[:class].present?
31
+ classes = attributes[:class].split(/\s+/)
32
+ else
33
+ classes = []
34
+ end
35
+ classes += values.compact.map(&:to_s)
36
+ classes.uniq!
37
+ else
38
+ classes = nil
39
+ end
40
+
41
+ if classes.present?
42
+ attributes.merge(:class => classes.join(" "))
43
+ else
44
+ attributes
45
+ end
46
+ end
47
+
48
+ def index_class(index, length)
49
+ return nil if length.blank? || (length <= 1)
50
+
51
+ if index == 0
52
+ "first"
53
+ elsif index == length - 1
54
+ "last"
55
+ else
56
+ nil
57
+ end
58
+ end
59
+ end
60
+ end