storefront 0.2.0

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