storefront 0.2.1 → 0.2.7

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