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,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