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,57 @@
1
+ module Storefront
2
+ class Form
3
+ module Errors
4
+ def errors_for(key, options = {})
5
+ value = options[:error_attributes].delete(:value)
6
+ template.capture_haml do
7
+ if value.present?
8
+ template.haml_tag :output, options[:error_attributes] do
9
+ template.haml_concat value.html_safe
10
+ end
11
+ else
12
+ template.haml_tag :output, options[:error_attributes]
13
+ end
14
+ end
15
+ end
16
+
17
+ def errors_on?(attribute, options = {})
18
+
19
+ end
20
+
21
+ def required?(attribute)
22
+ attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
23
+
24
+ if @object && @object.class.respond_to?(:reflect_on_validations_for)
25
+ @object.class.reflect_on_validations_for(attribute_sym).any? do |validation|
26
+ (validation.macro == :validates_presence_of || validation.macro == :validates_inclusion_of) &&
27
+ validation.name == attribute_sym &&
28
+ (validation.options.present? ? options_require_validation?(validation.options) : true)
29
+ end
30
+ else
31
+ if @object && @object.class.respond_to?(:validators_on)
32
+ !@object.class.validators_on(attribute_sym).find{|validator| (validator.kind == :presence || validator.kind == :inclusion) && (validator.options.present? ? options_require_validation?(validator.options) : true)}.nil?
33
+ else
34
+ true
35
+ end
36
+ end
37
+ end
38
+
39
+ def options_require_validation?(options) #nodoc
40
+ allow_blank = options[:allow_blank]
41
+ return !allow_blank unless allow_blank.nil?
42
+ if_condition = !options[:if].nil?
43
+ condition = if_condition ? options[:if] : options[:unless]
44
+
45
+ condition = if condition.respond_to?(:call)
46
+ condition.call(@object)
47
+ elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
48
+ @object.send(condition)
49
+ else
50
+ condition
51
+ end
52
+
53
+ if_condition ? !!condition : !condition
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,420 @@
1
+ # http://stackoverflow.com/questions/2731735/formtastic-own-as-input-type
2
+ # to convert this from formtastic back to normal, remove @template, and pass "form" in as a param
3
+ module Storefront
4
+ module OldFormHelper
5
+ def action(label, options = {})
6
+ self.next label, options
7
+ end
8
+
9
+ def watched_input(method, options = {})
10
+ options[:input_html] ||= {}
11
+ options[:input_html].merge!(:class => "watch-characters", "data-character-count" => (options.delete(:count) || 100))
12
+ text_input method, options
13
+ end
14
+
15
+ def live_update_input(method, options = {})
16
+ options[:input_html] ||= {}
17
+ options[:input_html].merge!(:class => "watch-characters live-update", "data-character-count" => (options.delete(:count) || 100))
18
+ text_input method, options
19
+ end
20
+
21
+ def phone_input(method, options = {})
22
+ options[:input_html] ||= {}
23
+ options[:input_html].merge!(:class => "phone")
24
+ string_input method, options
25
+ end
26
+
27
+ def security_code_input(method, options = {})
28
+ options[:input_html] ||= {}
29
+ options[:input_html].merge!(:class => "security_code")
30
+ string_input method, options
31
+ end
32
+
33
+ def filter_input(method, options = {})
34
+ expression = options.delete(:match)
35
+ expression = expression.source if expression.is_a?(::Regexp)
36
+ filter = options.delete(:filter) || method.to_s
37
+ boolean_input method, options.merge("data-filter-attribute" => filter, "data-filter-expression" => expression)
38
+ end
39
+
40
+ def money_input(method, options = {})
41
+ options[:input_html] ||= {}
42
+ classes = options.delete(:class)
43
+ if classes.blank?
44
+ classes = "money"
45
+ else
46
+ classes = [classes, "money"].join(" ")
47
+ end
48
+ options[:input_html].reverse_merge!("data-accepts" => Regexp.new('^[\d\.\,\$]$').source, :class => classes, :maxlength => nil, :size => nil, :value => @template.format_money(object.send(method)))
49
+ string_input method, options
50
+ end
51
+
52
+ def percent_input(method, options = {})
53
+ options[:input_html] ||= {}
54
+ options[:input_html].reverse_merge!(:class => "percent", :maxlength => nil, :size => nil, :value => @template.format_percent(object.send(method)))
55
+ string_input method, options
56
+ end
57
+
58
+ def card_input(method, options = {})
59
+ options[:input_html] ||= {}
60
+ options[:input_html].merge!(:class => "card", :maxlength => nil, :size => nil)
61
+ string_input method, options
62
+ end
63
+
64
+ def birthday_input(method, options = {})
65
+ string_input method, options.reverse_merge(:hint => "MM/DD/YYYY", :input_html => {:class => "birthday"})
66
+ end
67
+
68
+ def datepicker_input(method, options = {})
69
+ input_html = options[:input_html] || {}
70
+ input_html[:class] = ["string", input_html[:class], "datepicker-deal", method.to_s.gsub("_", "-")].compact.uniq.join(" ")
71
+ input_html[:value] = I18n.l(object.send(method), :format => options.delete(:format) || :pretty)
72
+ options[:input_html] = input_html
73
+ string_input method, options
74
+ end
75
+
76
+ def simple_datepicker_input(method, options = {})
77
+ string_input method, options.reverse_merge(:input_html => {:class => 'datepicker'})
78
+ end
79
+
80
+ # merge with above
81
+ def deal_datepicker_input(method, options = {})
82
+ options[:input_html] ||= {}
83
+ classes = ["datepicker-deal", options.delete(:class)].flatten.join(" ")
84
+ options[:input_html].merge!(:readonly => true, :class => classes, :value => options.delete(:value))
85
+ string_input method, options
86
+ end
87
+
88
+ def slider_input(method, options = {})
89
+ slider_classes = options.delete(:class) || method.to_s
90
+ inner_html = options.delete(:input_html) || {}
91
+ inner_html[:class] = "with-slider #{slider_classes}"
92
+ inner_html["data-meter"] = slider_classes.split(/\s+/).first
93
+ inner_html["data-min"] = options[:min] if options[:min].present?
94
+ inner_html["data-max"] = options[:max] if options[:max].present?
95
+ inner_html["data-step"] = options[:step] if options[:step].present?
96
+ inner_html["data-accepts"] = Regexp.new('\\d').source
97
+ inner_html[:value] = options[:value]
98
+ string_input method, :input_html => inner_html
99
+ end
100
+
101
+ def cash_input(method, options = {})
102
+ string_input method, options.reverse_merge(:input_html => {:class => "money"})
103
+ end
104
+
105
+ def image_input(method, options = {})
106
+ label = options[:label] || method.to_s.humanize
107
+ image = options[:resource] || object
108
+ @template.render :partial => "shared/forms/image", :locals => {:form => self, :brandable => image, :as => method, :label => label, :required => (options[:required] || false)}
109
+ end
110
+
111
+ def submit_center(label, options = {})
112
+ @template.render :partial => "shared/forms/submit_center", :locals => options.reverse_merge(:reject => nil, :form => self, :label => label)
113
+ end
114
+
115
+ def state(name)
116
+ @template.hidden_field_tag :event, @template.workflow_state(name)
117
+ end
118
+
119
+ def hours
120
+ @template.render :partial => "shared/forms/hours", :locals => {:form => self}
121
+ end
122
+
123
+ def group(name, options = {})
124
+ scoping = options.delete(:scoped)
125
+ scoping = nil if scoping.is_a?(User) && scoping.admin?
126
+ as = options.delete(:as) || :parent
127
+ if scoping.blank?
128
+ items = @template.select_attributes(name, :name, :id)
129
+ else
130
+ items = scoping.send(name.to_s.pluralize)
131
+ end
132
+ hidden = options.delete(:hidden) || false
133
+ if hidden
134
+ input "#{name}_id".to_sym, :as => :hidden, :input_html => {:value => default_id_for(@template.parent)}
135
+ else
136
+ input_select "#{name}_id".to_sym, :collection => items, :label_method => :name, :default => @template.parent, :required => true
137
+ end
138
+ end
139
+
140
+ def has_one(model, options = {})
141
+ conditions = (options.delete(:conditions) || {})
142
+ collection = options.delete(:collection) || @template.select_attributes(model, :name, :id, conditions)
143
+ default = options.delete(:default) || @template.params["#{model}_id".to_sym] || @template.resource.send(model)
144
+ input_select "#{model}_id", {:collection => collection, :default => default}.merge(options)
145
+ end
146
+
147
+ def logo(brandable)
148
+ image(brandable, :logo)
149
+ end
150
+
151
+ def image(attribute, brandable, options = {})
152
+ label = options[:label] || attribute.to_s.humanize
153
+ has_one_association = brandable.class.belongs_to?(attribute) || brandable.class.has_one?(attribute)
154
+ single_file = (options[:single_file] == true) || (has_one_association == true)
155
+ @template.render :partial => "shared/forms/image", :locals => {:form => self, :brandable => brandable, :as => attribute, :label => label, :required => (options[:required] || false), :single_file => single_file, :hint => options[:hint]}
156
+ end
157
+
158
+ def images(attribute, brandable, options = {})
159
+ label = options[:label] || attribute.to_s.humanize
160
+ @template.render :partial => "shared/forms/images", :locals => {:form => self, :brandable => brandable, :as => attribute, :label => label}
161
+ end
162
+
163
+ def store(options = {})
164
+ label = options[:label] || "Primary Location"
165
+ defaults = (options[:defaults] || []).flatten.compact
166
+ @template.render :partial => "shared/forms/store", :locals => {:form => self, :label => label, :defaults => defaults}
167
+ end
168
+
169
+ def address(options = {})
170
+ label = options[:label] || "Address"
171
+ defaults = (options[:defaults] || []).flatten.compact
172
+ include_details = options[:include_details] == true
173
+ @template.render :partial => "shared/forms/address", :locals => {:form => self, :label => label, :defaults => defaults, :include_details => include_details}
174
+ end
175
+
176
+ def inline_address(options = {})
177
+ default = options[:default]
178
+ @template.render :partial => "shared/forms/inline_address", :locals => {:form => self, :default => default}
179
+ end
180
+
181
+ def location(attribute, options = {})
182
+ label = options[:label] || "Address"
183
+ defaults = (options[:defaults] || []).flatten.compact
184
+ options[:input_html] = {}
185
+ if options.delete(:read_only) == true
186
+ options[:input_html][:readonly] = true
187
+ end
188
+ if options.delete(:disabled) == true
189
+ options[:input_html][:disabled] = true
190
+ end
191
+ id = options.delete(:id)
192
+ @template.render :partial => "shared/forms/location", :locals => {:form => self, :id => id, :label => label, :defaults => defaults, :attribute => attribute, :input_html => options[:input_html], :disabled => (options[:disabled] == false)}
193
+ end
194
+
195
+ def default(model, attribute, object = nil)
196
+ (object || self.object).send(attribute) || model.to_s.camelize.constantize.new
197
+ end
198
+
199
+ def submit_to(path, options = {})
200
+ label = options[:label] || "Save Changes"
201
+ cancel = options[:cancel] || "Cancel"
202
+ label_class = (Array(options[:class]) + ["round-green"]).compact.join(" ")
203
+ @template.render :partial => "shared/forms/submit", :locals => {:form => self, :path => path, :label => label, :cancel => cancel, :label_class => label_class, :agree => options[:agree]}
204
+ end
205
+
206
+ def contact(attribute, options = {})
207
+ label = options[:label] || attribute.to_s.humanize
208
+ include_logo = options[:include_logo] == true
209
+ @template.render :partial => "shared/forms/contact", :locals => {:form => self, :label => label, :attribute => attribute, :no_address => options[:no_address] == true ? true : false, :include_logo => include_logo}
210
+ end
211
+
212
+ def hidden_select(attribute, options = {})
213
+ options[:value] = default_id_for(options[:default])
214
+ if !options[:value].blank?
215
+ input attribute.to_sym, :as => :hidden, :input_html => {:value => options[:value]}
216
+ else
217
+ input_select attribute.to_sym, options.except(:value)
218
+ end
219
+ end
220
+
221
+ def next(label, options = {})
222
+ submit_to(@template.send(:root_path), options.reverse_merge(:label => label, :deal => nil, :agree => nil))
223
+ end
224
+
225
+ def input_select(attribute, options = {})
226
+ default = default_id_for(options.delete(:default))
227
+ result = input(attribute, options.reverse_merge(:as => :select, :include_blank => true))
228
+ result.gsub!(/selected=['"]([^'"]+)['"]/, "")
229
+ result.gsub!(/(value=['"]#{default.to_s}['"])/, "\\1 selected='selected'") unless default.blank?
230
+ result
231
+ end
232
+
233
+ def default_password
234
+ result = input :password, :as => :hidden, :value => "biglobby"
235
+ result += input :password_confirmation, :as => :hidden, :value => "biglobby"
236
+ result
237
+ end
238
+
239
+ def autocomplete(attribute, options = {})
240
+ value = object.work_name.to_s if object.respond_to?(:work_name)
241
+ value ||= object.group_name.to_s if object.respond_to?(:group_name)
242
+ result = input(attribute, :as => :hidden, :input_html => {:class => "hidden-autocomplete", :value => value})
243
+ result += input(attribute, options.merge(:input_html => {:class => "autocomplete", :maxlength => nil, :size => nil}, :as => :string))
244
+ result
245
+ end
246
+
247
+ def autocomplete_two(attribute, options = {})
248
+ default = options[:default]
249
+ #attribute = "#{attribute}".gsub(/_id$/, "")
250
+ if default.present?
251
+ name = default.name rescue ""
252
+ else
253
+ name = (object.send("#{attribute}".gsub(/_id$/, "")).name || "") rescue ""
254
+ end
255
+
256
+ options[:value] ||= default_id_for(default || object.send("#{attribute}".gsub(/_id$/, "")))
257
+ value = User.find_by_id(options[:value])
258
+ name = value.name if value
259
+ value = value.id if value
260
+
261
+ result = input(attribute, :as => :hidden, :input_html => {:class => "hidden-autocomplete", :value => value})
262
+
263
+ options[:input_html] ||= {}
264
+ options[:input_html].merge!(:class => "autocomplete-two", :value => name)
265
+
266
+ result += input("#{attribute}".gsub(/_id$/, ""), options)
267
+
268
+ result
269
+ end
270
+
271
+ def partial(method, options = {})
272
+ send(:"#{options.delete(:as)}_partial", method, options)
273
+ end
274
+
275
+ def select_partial(method, options = {})
276
+ default = default_id_for(options.delete(:default))
277
+ result = input(method, options.merge(:as => :select, :include_blank => true))
278
+ result.gsub!(/selected=['"]([^'"]+)['"]/, "")
279
+ result.gsub!(/(value=['"]#{default.to_s}['"])/, "\\1 selected='selected'") unless default.blank?
280
+ result
281
+ end
282
+
283
+ def partial_for(method, options = {})
284
+ send("#{method}_partial", options)
285
+ end
286
+
287
+ def conditions_partial(options = {})
288
+ @template.render :partial => "shared/forms/conditions", :locals => options.reverse_merge(:form => self, :locale => "group")
289
+ end
290
+
291
+ def cost_partial(options = {})
292
+ @template.render :partial => "shared/forms/cost", :locals => {:form => self}
293
+ end
294
+
295
+ def value_input(method, options)
296
+ basic_input_helper(:value_field, :string, method, options.merge(:input_html => options))
297
+ end
298
+
299
+ def value_field(method, html_options = {})
300
+ @template.content_tag(:output, html_options.delete(:value) || object.send(method), html_options)
301
+ end
302
+
303
+ private
304
+ def default_id_for(value)
305
+ result = (value && !value.is_a?(Fixnum) && !value.is_a?(String)) ? value.id : value
306
+ result.blank? ? nil : result
307
+ end
308
+ end
309
+ end
310
+
311
+
312
+ Dir["#{File.expand_path(File.dirname(__FILE__))}/**/*"].each do |path|
313
+ require path unless ::File.directory?(path) || File.basename(path) =~ /fields/
314
+ end
315
+
316
+ module Storefront
317
+ class Form
318
+ class Fields
319
+ include Haml::Helpers
320
+ include Storefront::Form::Model
321
+ include Storefront::Form::Elements
322
+ include Storefront::Form::Inputs
323
+ include Storefront::Form::Labels
324
+ include Storefront::Form::Errors
325
+ include Storefront::Form::Hints
326
+ include Storefront::AttributeHelper
327
+ include Storefront::OldFormHelper
328
+
329
+ attr_accessor :template, :object, :parent, :tabindex, :access_keys, :keys
330
+
331
+ def initialize(template, options, &block)
332
+ @template = template
333
+ @tabindex = options[:tabindex]
334
+ @access_keys = options[:access_keys]
335
+ @parent = options[:parent]
336
+ @object = options[:object]
337
+ @attribute = options[:attribute]
338
+ @macro = options[:macro]
339
+ if @attribute.present?
340
+ @keys = [options[:keys], "#{@attribute}_attributes"]
341
+ else
342
+ @keys = Array(options[:keys])
343
+ end
344
+ if @parent && @object.blank? && @attribute.present? && @parent.respond_to?("#{@attribute}_or_default")
345
+ @object = @parent.send("#{@attribute}_or_default")
346
+ end
347
+
348
+ if @macro == :has_many && @object
349
+ @index = @parent.send(@attribute).index(@object)
350
+ end
351
+ yield(self)
352
+ end
353
+
354
+ def inputs(*args, &block)
355
+ options = args.extract_options!
356
+ label = args.shift
357
+ template.capture_haml do
358
+ template.haml_tag :fieldset, options do
359
+ template.haml_tag :legend do
360
+ template.haml_tag :span, label
361
+ end if label.present?
362
+ template.haml_tag :ol, &block
363
+ end
364
+ end
365
+ end
366
+ alias fieldset inputs
367
+
368
+ def fields_for(*args, &block)
369
+ options = args.extract_options!
370
+ attribute = args.shift
371
+ if block_given?
372
+ reflection = reflection_for(attribute)
373
+ fields = Storefront::Form::Fields.new(@template, options.merge(
374
+ :access_keys => @access_keys,
375
+ :tabindex => @tabindex,
376
+ :parent => @object,
377
+ :object => args.shift || @object.send(attribute),
378
+ :keys => @keys,
379
+ :macro => reflection ? reflection.macro : nil,
380
+ :attribute => attribute
381
+ ), &block)
382
+
383
+ @tabindex = fields.tabindex
384
+ end
385
+ nil
386
+ end
387
+ alias semantic_fields_for fields_for
388
+
389
+ def access_key_for(attribute)
390
+ attribute.to_s[0,1]
391
+ end
392
+
393
+ def options
394
+
395
+ end
396
+
397
+ def options_for(attribute, options = {})
398
+
399
+ end
400
+
401
+ def param_for(*parts)
402
+ parts = parts.flatten.compact
403
+ parts[0].to_s + parts[1..-1].map {|i| "[#{i}]"}.join("")
404
+ end
405
+
406
+ # get rid of this, don't like
407
+ def default(model, attribute, object = nil)
408
+ (object || self.object).send(attribute) || model.to_s.camelize.constantize.new
409
+ end
410
+
411
+ def method_missing(method, *args, &block)
412
+ if @template.respond_to?(method)
413
+ @template.send(method, *args, &block)
414
+ else
415
+ super
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end