storefront 0.2.1 → 0.2.7

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 (73) hide show
  1. data/Rakefile +2 -2
  2. data/lib/storefront.rb +21 -3
  3. data/lib/storefront/configuration.rb +154 -0
  4. data/lib/storefront/dashboard.rb +8 -7
  5. data/lib/storefront/form.rb +70 -53
  6. data/lib/storefront/form/base.rb +50 -0
  7. data/lib/storefront/form/builder.rb +124 -0
  8. data/lib/storefront/form/errors.rb +20 -46
  9. data/lib/storefront/form/field.rb +102 -0
  10. data/lib/storefront/form/fieldset.rb +44 -0
  11. data/lib/storefront/form/hint.rb +34 -0
  12. data/lib/storefront/form/input.rb +191 -0
  13. data/lib/storefront/form/inputs/checkbox.rb +12 -0
  14. data/lib/storefront/form/inputs/date.rb +16 -0
  15. data/lib/storefront/form/inputs/file.rb +12 -0
  16. data/lib/storefront/form/inputs/hidden.rb +11 -0
  17. data/lib/storefront/form/inputs/radio.rb +11 -0
  18. data/lib/storefront/form/inputs/range.rb +15 -0
  19. data/lib/storefront/form/inputs/select.rb +68 -0
  20. data/lib/storefront/form/inputs/string.rb +89 -0
  21. data/lib/storefront/form/inputs/submit.rb +24 -0
  22. data/lib/storefront/form/inputs/textarea.rb +19 -0
  23. data/lib/storefront/form/inputs/value.rb +16 -0
  24. data/lib/storefront/form/label.rb +35 -0
  25. data/lib/storefront/helpers/attribute_helper.rb +29 -8
  26. data/lib/storefront/helpers/body_helper.rb +1 -1
  27. data/lib/storefront/helpers/cache_helper.rb +2 -2
  28. data/lib/storefront/helpers/component_helper.rb +34 -19
  29. data/lib/storefront/helpers/dashboard_helper.rb +9 -17
  30. data/lib/storefront/helpers/definition_list_helper.rb +82 -14
  31. data/lib/storefront/helpers/form_helper.rb +7 -2
  32. data/lib/storefront/helpers/head_helper.rb +2 -2
  33. data/lib/storefront/helpers/image_helper.rb +1 -25
  34. data/lib/storefront/helpers/list_helper.rb +120 -32
  35. data/lib/storefront/helpers/locale_helper.rb +98 -28
  36. data/lib/storefront/helpers/page_helper.rb +78 -60
  37. data/lib/storefront/helpers/request_helper.rb +3 -0
  38. data/lib/storefront/helpers/table_helper.rb +1 -1
  39. data/lib/storefront/helpers/template_helper.rb +11 -0
  40. data/lib/storefront/helpers/time_helper.rb +2 -2
  41. data/lib/storefront/helpers/widget_helper.rb +153 -2
  42. data/lib/storefront/model_helper.rb +17 -0
  43. data/lib/storefront/proxies/attribute_proxy.rb +150 -0
  44. data/lib/storefront/proxies/model_proxy.rb +249 -0
  45. data/lib/storefront/railtie.rb +4 -2
  46. data/lib/storefront/table.rb +7 -0
  47. data/test/form_helper_test.rb +1 -1
  48. metadata +25 -27
  49. data/init.rb +0 -1
  50. data/lib/storefront/form/elements.rb +0 -101
  51. data/lib/storefront/form/fields.rb +0 -420
  52. data/lib/storefront/form/hints.rb +0 -18
  53. data/lib/storefront/form/inputs.rb +0 -258
  54. data/lib/storefront/form/labels.rb +0 -81
  55. data/lib/storefront/form/model.rb +0 -84
  56. data/lib/storefront/microdata/address.rb +0 -7
  57. data/lib/storefront/microdata/event.rb +0 -13
  58. data/lib/storefront/microdata/geo.rb +0 -7
  59. data/lib/storefront/microdata/organization.rb +0 -7
  60. data/lib/storefront/microdata/person.rb +0 -7
  61. data/lib/storefront/microformat/event.rb +0 -7
  62. data/lib/storefront/microformat/person.rb +0 -7
  63. data/lib/storefront/models/container.rb +0 -0
  64. data/lib/storefront/models/element.rb +0 -15
  65. data/lib/storefront/models/form.rb +0 -0
  66. data/lib/storefront/models/form_element.rb +0 -0
  67. data/lib/storefront/models/link.rb +0 -0
  68. data/lib/storefront/models/list.rb +0 -0
  69. data/lib/storefront/models/list_element.rb +0 -0
  70. data/lib/storefront/models/table.rb +0 -0
  71. data/lib/storefront/models/term.rb +0 -0
  72. data/lib/storefront/models/term_list.rb +0 -0
  73. data/lib/storefront/models/validation.rb +0 -0
@@ -1,73 +1,91 @@
1
1
  module Storefront
2
2
  module PageHelper
3
- def page(options = {})
4
- page_title(options)
5
- end
6
-
7
- # = page #=> figures out name from resource (inherited_resources)
8
- # = page :step => 1 #=> prepends "Step 1: " to the title
9
- # options => [:browser_title, :title, :clazz]
10
- def page_title(options = {})
11
- if options.is_a?(String)
12
- title = browser_title = options
13
- title_class = ""
14
- elsif options.is_a?(Symbol)
15
- title = browser_title = t("page.titles.#{options}")
16
- else
17
- title_class = options.delete(:title_class)
18
- title = options.delete(:title)
19
- browser_title = options.delete(:browser_title)
20
- end
21
- if title
22
- capture_page_title((browser_title || title), (title || browser_title), :class => title_class)
3
+
4
+ # calls <tt>model.to_title</tt>
5
+ def page(*args)
6
+ options = args.extract_options!
7
+ title = args.shift
8
+ options[:action] ||= storefront_config.current_scope[:action]
9
+
10
+ title = case title
11
+ when ::String
12
+ title
13
+ when ::Symbol
14
+ t?(title, :scope => :"pages.titles")
23
15
  else
24
- return "" unless (defined?(:resource) || defined?(:collection))
25
- cont = params[:controller].split("/").last
26
- key = "#{cont}.#{params[:action]}"
27
- default_key = "resource.#{params[:action]}"
28
- parent_exists = parent rescue nil
29
- parent_exists = !parent_exists.nil?
30
- options[:name] = resource.name if resource rescue nil
31
- options[:name] ||= parent.name if parent_exists
32
- if parent_exists
33
- key << ".nested"
34
- default_key << ".nested"
35
- end
36
- if options[:clazz].blank?
37
- options[:clazz] = cont.singularize.titleize
38
- options[:clazz] = options[:clazz].pluralize if params[:action] == "index"
16
+ if options[:title].present?
17
+ options[:title]
18
+ else
19
+ parent_name = !!options[:parent] ? resource_to_title(storefront_config.current_scope[:parent], options) : nil
20
+ resource_name = resource_to_title(storefront_config.current_scope[:resource], options)
21
+ [parent_name, resource_name].compact.join(storefront_config.breadcrumb)
39
22
  end
40
- browser_title_options = options.merge(:default => t("#{default_key}.browser_title"))
41
- page_title_options = options.merge(:default => t("#{default_key}.page_title"))
42
- capture_page_title(
43
- t("#{key}.browser_title", browser_title_options).strip.squeeze(" "),
44
- t(key, page_title_options).strip.squeeze(" "),
45
- :class => title_class
46
- )
47
23
  end
24
+
25
+ self.title options[:browser] || title
26
+
27
+ options = header_widget_options(options.merge(:title => title))
28
+
29
+ options[:header_html].reverse_merge!(:id => storefront_config.page_header_id) if storefront_config.page_header_id.present?
30
+ options[:title_html].reverse_merge!(:id => storefront_config.page_title_id) if storefront_config.page_title_id.present?
31
+ options[:subtitle_html].reverse_merge!(:id => storefront_config.page_subtitle_id) if storefront_config.page_subtitle_id.present?
32
+ options[:level] = 1
33
+ options[:skip_format] = true
34
+
35
+ header_widget options.merge(:title => title)
48
36
  end
37
+
38
+ def title_widget(*args, &block)
39
+ options = args.extract_options!
40
+ options[:header_html] ||= {}
41
+ merge_class! options[:header_html], *widget_classes("title-widget")
42
+ header_widget(options, &block)
43
+ end
44
+
45
+ def title_widget_options
49
46
 
50
- def resource_title(options = {})
51
- return options[:default] if options[:default]
52
- return "" unless (defined?(:resource) || defined?(:collection))
53
- options[:action] ||= params[:action]
54
- cont = params[:controller].split("/").last
55
- key = "#{cont}.#{options[:action]}"
56
- options[:name] = resource.name if resource rescue nil
57
- options[:clazz] = cont.singularize.camelize
58
- options[:clazz] = options[:clazz].pluralize if options[:action] == "index"
59
- page_title_options = options.merge(:default => t("resource.#{options[:action]}.page_title"))
60
- t(key, page_title_options).strip.squeeze(" ")
61
47
  end
62
48
 
63
- def capture_page_title(browser_title, the_page_title, options = {})
64
- result = capture_haml do
65
- title browser_title
66
- haml_tag :header, options.reject{|k,v| v.blank?}.merge(:id => "page-title") do
67
- haml_tag :h1, the_page_title
68
- end
49
+ def resource_to_title(resource, options = {})
50
+ if resource.present?
51
+
52
+ # handle subdomains as well
53
+ t?(options[:action].to_sym,
54
+ :scope => :"pages.titles",
55
+ :model => Storefront::ModelProxy.model_names(resource),
56
+ :namespaces => storefront_config.current_scope[:namespace],
57
+ :allow_blank => true,
58
+ :action => "",
59
+ :name => resource.is_a?(::Class) ? resource.name.titleize.pluralize : resource.to_title,
60
+ :clazz => resource.is_a?(::Class) ? resource.name.titleize : resource.class.name
61
+ )
62
+ end
63
+ end
64
+
65
+ def configure_storefront
66
+ action = params[:action].to_s
67
+
68
+ begin
69
+ resource_object = self.resource rescue nil
70
+ resource_object = resource_instance_name.to_s.camelize.constantize rescue nil
71
+ rescue Exception => e
72
+ resource_object = nil
73
+ end
74
+ begin
75
+ parent_object = self.parent rescue nil
76
+ rescue Exception => e
77
+ parent_object = nil
69
78
  end
70
- result
79
+
80
+ namespace = params[:controller].split("/")
81
+ namespace = namespace.length > 1 ? namespace[0..-2].join(".") : nil
82
+
83
+ storefront_config.current_scope = {
84
+ :action => action,
85
+ :resource => resource_object,
86
+ :parent => parent_object,
87
+ :namespace => namespace
88
+ }
71
89
  end
72
90
  end
73
91
  end
@@ -1,5 +1,8 @@
1
1
  module Storefront
2
2
  module RequestHelper
3
+ # requesting?(:deals)
4
+ # requesting?("/admin/users/:id")
5
+ # requesting?("admin/users#show")
3
6
  def requesting?(expression)
4
7
  case expression
5
8
  when ::Symbol
@@ -16,7 +16,7 @@ module Storefront
16
16
  def next_page_path(collection)
17
17
  with_params(request.path, :page => collection.next_page)
18
18
  end
19
-
19
+
20
20
  def prev_page_path(collection)
21
21
  with_params(request.path, :page => collection.prev_page)
22
22
  end
@@ -0,0 +1,11 @@
1
+ module Storefront
2
+ module TemplateHelper
3
+ def javascript_template(name, options = {}, &block)
4
+ options[:id] ||= name.to_s.strip.gsub(/[\s|_]+/, storefront_config.separator).squeeze(storefront_config.separator)
5
+ options[:type] ||= "text/html"
6
+ capture_haml do
7
+ haml_tag :script, options, &block
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,10 +5,10 @@ module Storefront
5
5
  def set_time_zone
6
6
  offset = (cookies[:timezone] || 0).to_i * -60
7
7
  # User.time_zone = ActiveSupport::TimeZone[-min.minutes]
8
- User.time_zone = ActiveSupport::TimeZone.us_zones.find { |z| z.utc_offset == offset.to_i }
8
+ ::User.time_zone = ActiveSupport::TimeZone.us_zones.find { |z| z.utc_offset == offset.to_i }
9
9
  true
10
10
  end
11
-
11
+
12
12
  def record_post_time
13
13
  cookies[:last_post_at] = Time.zone.now.to_i
14
14
  end
@@ -1,10 +1,161 @@
1
1
  module Storefront
2
2
  module WidgetHelper
3
+ WIDGETS = {} unless defined?(WIDGETS)
4
+
3
5
  def widget(*args, &block)
4
6
  options = args.extract_options!
5
- options.reverse_merge(:locale => nil)
7
+ name = args.shift
8
+
9
+ if widget_partial?(name)
10
+ widget_partial(name, options, &block)
11
+ elsif self.respond_to?("#{name}_widget")
12
+ send("#{name}_widget", name, options, &block)
13
+ else
14
+ raise "Please either define a helper method '#{name}_widget' or create a partial such as 'shared/widgets/#{name}.haml'"
15
+ end
16
+ end
17
+
18
+ def text_widget(*args, &block)
19
+ options = text_widget_options(*args)
20
+
21
+ capture_haml do
22
+ haml_tag :article, options[:outer_html] do
23
+ header = header_widget(options)
24
+ haml_concat header if header.present?
25
+ content = options[:content]
26
+ content ||= capture(&block) if block_given?
27
+ haml_tag options[:tag], content, options[:content_html]
28
+ end
29
+ end
30
+ end
31
+
32
+ def text_widget_options(*args)
33
+ options = widget_options(*args)
34
+
35
+ options[:content_html] ||= {}
36
+
37
+ merge_class! options, storefront_config.content_class
38
+
39
+ options[:tag] ||= :p
40
+
41
+ options
42
+ end
43
+
44
+ def buttons_widget(*args, &block)
45
+ options = args.extract_options!
46
+ options = widget_options("buttons-widget", options.reverse_merge(:title => false))
47
+
48
+ capture_haml do
49
+ haml_tag :nav, options[:outer_html], &block
50
+ end
51
+ end
52
+
53
+ def footer_widget(*args, &block)
54
+ options = args.extract_options!
55
+ options = widget_options("footer-widget", options.reverse_merge(:title => false))
56
+
57
+ capture_haml do
58
+ haml_tag :footer, options[:outer_html], &block
59
+ end
60
+ end
61
+
62
+ def header_widget(options = {}, &block)
63
+ options = header_widget_options(options) unless options[:skip_format]
64
+
65
+ return nil if options.blank?
66
+
67
+ capture_haml do
68
+ if block_given?
69
+ haml_tag :header, options[:header_html], &block
70
+ else
71
+ level = options[:level] || 3
72
+ title = options[:title_html].delete(:value)
73
+ subtitle = options[:subtitle_html].delete(:value)
74
+
75
+ haml_tag :header, options[:header_html] do
76
+ if subtitle.present?
77
+ haml_tag :hgroup do
78
+ haml_tag :"h#{level}", title, options[:title_html]
79
+ haml_tag :"h#{level + 1}", subtitle, options[:subtitle_html]
80
+ end
81
+ else
82
+ haml_tag :"h#{level}", title, options[:title_html]
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def header_widget_options(options = {})
90
+ return {} if options[:title] == false
91
+
92
+ header_html = options[:header_html] || {}
93
+ header_html.reverse_merge!(:class => storefront_config.header_class)
94
+
95
+ title_html = options[:title_html] || {}
96
+ title_html.reverse_merge!(:class => storefront_config.title_class)
97
+ title_html[:value] = options.delete(:title)
98
+
99
+ title_html[:value] = t?(title_html[:value], :scope => :"widgets.headers") if title_html[:value].is_a?(::Symbol)
100
+
101
+ subtitle_html = options[:subtitle_html] || {}
102
+ if subtitle_html.present? || options[:subtitle].present?
103
+ subtitle_html.reverse_merge!(:class => storefront_config.subtitle_class)
104
+ subtitle_html[:value] = options.delete(:subtitle)
105
+ subtitle_html[:value] = t?(subtitle_html[:value], :scope => :"widgets.headers") if subtitle_html[:value].is_a?(::Symbol)
106
+ end
107
+
108
+ options.merge! :header_html => header_html, :title_html => title_html, :subtitle_html => subtitle_html
109
+
110
+ options
111
+ end
112
+
113
+ protected
114
+ def widget_partial(name, options = {}, &block)
6
115
  options.merge!(:yielded_content => block_given? ? capture(&block) : nil)
7
- render(:partial => "shared/widgets/#{name}", :locals => options)
116
+ dir = name.is_a?(::Symbol) ? ::File.join(storefront_config.widgets_path, name.to_s) : name.to_s
117
+ render(:partial => dir, :locals => options)
118
+ end
119
+
120
+ def widget_partial?(name)
121
+ return WIDGETS[name] if WIDGETS.has_key?(name)
122
+
123
+ dir = name.is_a?(::Symbol) ? ::File.join(storefront_config.widgets_path, name.to_s) : name.to_s
124
+
125
+ path = "#{Rails.root}/app/views/#{dir}"
126
+
127
+ template = "_#{File.basename(path, File.extname(path))}"
128
+
129
+ WIDGETS[name] = Dir.entries(File.dirname(path))[2..-1].detect do |file|
130
+ template == File.basename(file, File.extname(file))
131
+ end.present?
132
+
133
+ WIDGETS[name]
134
+ end
135
+
136
+ def widget_options(type, *args)
137
+ options = args.extract_options!
138
+ key = args.shift
139
+
140
+ options[:title] ||= false
141
+ options[:key] ||= key
142
+ options[:outer_html] ||= {}
143
+
144
+ options[:body_html] ||= {}
145
+ options[:footer_html] ||= {}
146
+
147
+ unless options[:widget_class] == false
148
+ merge_class! options[:outer_html], *widget_classes(type)
149
+ end
150
+
151
+ options
152
+ end
153
+
154
+ def widget_classes(type)
155
+ [
156
+ storefront_config.widget_class,
157
+ type.to_s.strip.gsub(/[\s|_]+/, storefront_config.separator).squeeze(storefront_config.separator)
158
+ ]
8
159
  end
9
160
  end
10
161
  end
@@ -0,0 +1,17 @@
1
+ module Storefront
2
+ module ModelHelper
3
+ def self.included(base)
4
+ base.class_eval do
5
+ def self.to_title
6
+ name
7
+ end unless self.respond_to?(:to_title)
8
+
9
+ def to_title
10
+ return title if respond_to?(:title)
11
+ return name if respond_to?(:name)
12
+ to_s
13
+ end unless self.instance_methods.include?(:to_title)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,150 @@
1
+ module Storefront
2
+ class AttributeProxy
3
+ attr_reader :name, :model, :required, :disabled, :top_level
4
+
5
+ def initialize(options = {})
6
+ @name = options[:name]
7
+ @model = options[:model]
8
+ @required = options[:required].nil? ? model.required?(name) : options[:required]
9
+ @disabled = options[:disabled]
10
+ @top_level = options[:top_level]
11
+ end
12
+
13
+ def config
14
+ Storefront.configuration
15
+ end
16
+
17
+ def required?
18
+ @required
19
+ end
20
+
21
+ def disabled?
22
+ @disabled
23
+ end
24
+
25
+ def humanize
26
+ model.humanized_attribute_name(name)
27
+ end
28
+
29
+ def errors?
30
+ top_level ? false : model.errors_on?(name, {})
31
+ end
32
+
33
+ def value(default = nil)
34
+ return default if default.present?
35
+ return nil if top_level
36
+ if model.object.respond_to?(name)
37
+ model.object.send(name)
38
+ else
39
+ nil
40
+ end
41
+ end
42
+
43
+ def access_key
44
+ name.to_s[0, 1]
45
+ end
46
+
47
+ def param_for(*parts)
48
+ parts = parts.flatten.compact
49
+ parts[0].to_s + parts[1..-1].map {|i| "[#{i}]"}.join("")
50
+ end
51
+
52
+ def to_id(options = {})
53
+ parts = []
54
+ parts << model.keys unless top_level
55
+ parts << options[:parent_index].to_s if options[:parent_index].present?
56
+ parts << name
57
+ parts << options[:index].to_s if options[:index].present?
58
+ parts << (options[:type] || "input").to_s
59
+
60
+ parts.join(config.separator).underscore.strip.gsub(/[_\s]+/, config.separator)
61
+ end
62
+
63
+ def to_param(options = {})
64
+ parts = []
65
+ parts << model.keys unless top_level
66
+ parts << options[:parent_index].to_s if options[:parent_index].present?
67
+ parts << name
68
+ parts << options[:index].to_s if options[:index].present?
69
+
70
+ param_for(parts)
71
+ end
72
+
73
+ def input_type(options = {})
74
+ model.default_input_type(name, options)
75
+ end
76
+
77
+ def to_label
78
+ humanize
79
+ end
80
+
81
+ def validations
82
+ return {} if top_level
83
+
84
+ unless @validations.present?
85
+ return {} unless model.validators?
86
+
87
+ attr_name = humanize
88
+
89
+ kinds = []
90
+
91
+ @validations = model.validators_on(name).inject({}) do |hash, validator|
92
+ key = case validator.kind
93
+ when :presence
94
+ :blank
95
+ when :uniqueness
96
+ :taken
97
+ when :inclusion
98
+ :inclusion
99
+ when :length
100
+ [:min, :max]
101
+ when :format
102
+ :invalid
103
+ when :numericality
104
+ :numericality
105
+ else
106
+ next
107
+ end
108
+
109
+ kinds << validator.kind
110
+
111
+ value = case validator.kind
112
+ when :presence
113
+ true
114
+ when :uniqueness
115
+ true
116
+ when :inclusion
117
+ Array(validator.options[:in]).compact.join(", ")
118
+ when :length
119
+ {:min => validator.options[:minimum], :max => validator.options[:maximum]}
120
+ when :format
121
+ validator.options[:with].is_a?(::Regexp) ? validator.options[:with].source : validator.options[:with].to_s
122
+ when :numericality
123
+ true
124
+ end
125
+
126
+ unless validator.kind == :length
127
+ message = model.validation_message(name, key, :attribute => attr_name)
128
+
129
+ hash[:"data-validates-#{validator.kind}"] = value.to_s
130
+ hash[:"data-validates-#{validator.kind}-message"] = message
131
+ else
132
+ key.each do |length_key|
133
+ next unless value[length_key].present?
134
+ message = model.validation_message(name, length_key == :min ? :too_short : :too_long, :attribute => attr_name, :count => value[length_key])
135
+
136
+ hash[:"data-validates-#{length_key}"] = value[length_key].to_s
137
+ hash[:"data-validates-#{length_key}-message"] = message
138
+ end
139
+ end
140
+
141
+ hash
142
+ end
143
+
144
+ @validations[:"data-validate"] = kinds.uniq.join(",") if kinds.present? && @validations.present?
145
+ end
146
+
147
+ @validations
148
+ end
149
+ end
150
+ end