gretel-lite 3.0.10

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.travis.yml +16 -0
  4. data/CHANGELOG.md +56 -0
  5. data/Gemfile +23 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +376 -0
  8. data/Rakefile +10 -0
  9. data/gemfiles/Gemfile-rails.3.1.x +19 -0
  10. data/gemfiles/Gemfile-rails.3.2.x +19 -0
  11. data/gemfiles/Gemfile-rails.4.0.x +19 -0
  12. data/gemfiles/Gemfile-rails.4.1.x +19 -0
  13. data/gretel.gemspec +23 -0
  14. data/lib/generators/gretel/USAGE +8 -0
  15. data/lib/generators/gretel/install_generator.rb +12 -0
  16. data/lib/generators/gretel/templates/breadcrumbs.rb +28 -0
  17. data/lib/gretel.rb +82 -0
  18. data/lib/gretel/crumb.rb +68 -0
  19. data/lib/gretel/crumbs.rb +64 -0
  20. data/lib/gretel/deprecated.rb +1 -0
  21. data/lib/gretel/deprecated/default_style_key.rb +25 -0
  22. data/lib/gretel/deprecated/layout.rb +15 -0
  23. data/lib/gretel/deprecated/show_root_alone.rb +27 -0
  24. data/lib/gretel/deprecated/yield_links.rb +44 -0
  25. data/lib/gretel/link.rb +39 -0
  26. data/lib/gretel/renderer.rb +254 -0
  27. data/lib/gretel/resettable.rb +13 -0
  28. data/lib/gretel/version.rb +3 -0
  29. data/lib/gretel/view_helpers.rb +71 -0
  30. data/test/deprecated_test.rb +45 -0
  31. data/test/dummy/Rakefile +7 -0
  32. data/test/dummy/app/assets/javascripts/application.js +15 -0
  33. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  34. data/test/dummy/app/controllers/application_controller.rb +3 -0
  35. data/test/dummy/app/helpers/application_helper.rb +5 -0
  36. data/test/dummy/app/mailers/.gitkeep +0 -0
  37. data/test/dummy/app/models/.gitkeep +0 -0
  38. data/test/dummy/app/models/issue.rb +3 -0
  39. data/test/dummy/app/models/project.rb +3 -0
  40. data/test/dummy/app/views/breadcrumbs/site.rb +3 -0
  41. data/test/dummy/config.ru +4 -0
  42. data/test/dummy/config/application.rb +14 -0
  43. data/test/dummy/config/boot.rb +10 -0
  44. data/test/dummy/config/breadcrumbs.rb +3 -0
  45. data/test/dummy/config/breadcrumbs/test_crumbs.rb +85 -0
  46. data/test/dummy/config/database.yml +25 -0
  47. data/test/dummy/config/environment.rb +5 -0
  48. data/test/dummy/config/environments/development.rb +2 -0
  49. data/test/dummy/config/environments/production.rb +2 -0
  50. data/test/dummy/config/environments/test.rb +2 -0
  51. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/test/dummy/config/initializers/inflections.rb +15 -0
  53. data/test/dummy/config/initializers/mime_types.rb +5 -0
  54. data/test/dummy/config/initializers/secret_token.rb +7 -0
  55. data/test/dummy/config/initializers/session_store.rb +8 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +5 -0
  58. data/test/dummy/config/routes.rb +11 -0
  59. data/test/dummy/db/migrate/20130122163007_create_projects.rb +9 -0
  60. data/test/dummy/db/migrate/20130122163051_create_issues.rb +10 -0
  61. data/test/dummy/db/schema.rb +29 -0
  62. data/test/dummy/lib/assets/.gitkeep +0 -0
  63. data/test/dummy/log/.gitkeep +0 -0
  64. data/test/dummy/public/404.html +26 -0
  65. data/test/dummy/public/422.html +26 -0
  66. data/test/dummy/public/500.html +25 -0
  67. data/test/dummy/public/favicon.ico +0 -0
  68. data/test/dummy/script/rails +6 -0
  69. data/test/fixtures/issues.yml +4 -0
  70. data/test/fixtures/projects.yml +3 -0
  71. data/test/gretel_test.rb +24 -0
  72. data/test/helper_methods_test.rb +546 -0
  73. data/test/test_helper.rb +15 -0
  74. metadata +205 -0
@@ -0,0 +1,44 @@
1
+ if RUBY_VERSION < "2.0"
2
+ Gretel::ViewHelpers.class_eval do
3
+
4
+ def breadcrumbs_with_yield_links(options = {})
5
+ if block_given?
6
+ Gretel.show_deprecation_warning(
7
+ "Calling `breadcrumbs` with a block has been deprecated and will be removed in Gretel version 4.0. Please use `tap` instead. Example:\n" +
8
+ "\n" +
9
+ " breadcrumbs(autoroot: false).tap do |links|\n" +
10
+ " if links.any?\n" +
11
+ " # process links here\n" +
12
+ " end\n" +
13
+ " end\n"
14
+ )
15
+ yield gretel_renderer.render(options)
16
+ else
17
+ breadcrumbs_without_yield_links(options)
18
+ end
19
+ end
20
+
21
+ alias_method_chain :breadcrumbs, :yield_links
22
+ end
23
+ else
24
+ module DeprecatedYieldLinks
25
+ def breadcrumbs(options = {})
26
+ if block_given?
27
+ Gretel.show_deprecation_warning(
28
+ "Calling `breadcrumbs` with a block has been deprecated and will be removed in Gretel version 4.0. Please use `tap` instead. Example:\n" +
29
+ "\n" +
30
+ " breadcrumbs(autoroot: false).tap do |links|\n" +
31
+ " if links.any?\n" +
32
+ " # process links here\n" +
33
+ " end\n" +
34
+ " end\n"
35
+ )
36
+ yield gretel_renderer.render(options)
37
+ else
38
+ super(options)
39
+ end
40
+ end
41
+ end
42
+
43
+ Gretel::ViewHelpers.send :prepend, DeprecatedYieldLinks
44
+ end
@@ -0,0 +1,39 @@
1
+ module Gretel
2
+ class Link
3
+ attr_accessor :key, :text, :url, :options
4
+
5
+ def initialize(key, text, url, options = {})
6
+ # Use accessors so plugins can override their behavior
7
+ self.key, self.text, self.url, self.options = key, text, url, options
8
+ end
9
+
10
+ # Sets current so +current?+ will return +true+.
11
+ def current!
12
+ @current = true
13
+ end
14
+
15
+ # Returns +true+ if this is the last link in the breadcrumb trail.
16
+ def current?
17
+ !!@current
18
+ end
19
+
20
+ # Enables accessors and predicate methods for values in the +options+ hash.
21
+ # This can be used to pass information to links when rendering breadcrumbs
22
+ # manually.
23
+ #
24
+ # link = Link.new(:my_crumb, "My Crumb", my_path, title: "Test Title", other_value: "Other")
25
+ # link.title? # => true
26
+ # link.title # => "Test Title"
27
+ # link.other_value? # => true
28
+ # link.other_value # => "Other"
29
+ # link.some_other? # => false
30
+ # link.some_other # => nil
31
+ def method_missing(method, *args, &block)
32
+ if method =~ /(.+)\?$/
33
+ options[$1.to_sym].present?
34
+ else
35
+ options[method]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,254 @@
1
+ module Gretel
2
+ class Renderer
3
+ DEFAULT_OPTIONS = {
4
+ style: :inline,
5
+ pretext: "",
6
+ posttext: "",
7
+ separator: "",
8
+ autoroot: true,
9
+ display_single_fragment: false,
10
+ link_current: false,
11
+ link_current_to_request_path: true,
12
+ semantic: false,
13
+ class: "breadcrumbs",
14
+ current_class: "current",
15
+ pretext_class: "pretext",
16
+ posttext_class: "posttext",
17
+ id: nil
18
+ }
19
+
20
+ DEFAULT_STYLES = {
21
+ inline: { container_tag: :div, separator: " &rsaquo; " },
22
+ ol: { container_tag: :ol, fragment_tag: :li },
23
+ ul: { container_tag: :ul, fragment_tag: :li },
24
+ bootstrap: { container_tag: :ol, fragment_tag: :li, class: "breadcrumb", current_class: "active" },
25
+ foundation5: { container_tag: :ul, fragment_tag: :li, class: "breadcrumbs", current_class: "current" }
26
+ }
27
+
28
+ def initialize(context, breadcrumb_key, *breadcrumb_args)
29
+ @context = context
30
+ @breadcrumb_key = breadcrumb_key
31
+ @breadcrumb_args = breadcrumb_args
32
+ end
33
+
34
+ # Renders the breadcrumbs HTML.
35
+ def render(options)
36
+ options = options_for_render(options)
37
+ links = links_for_render(options)
38
+
39
+ LinkCollection.new(context, links, options)
40
+ end
41
+
42
+ # Yields links with applied options.
43
+ def yield_links(options = {})
44
+ yield render(options)
45
+ end
46
+
47
+ # Returns the parent breadcrumb.
48
+ def parent_breadcrumb(options = {})
49
+ render(options)[-2]
50
+ end
51
+
52
+ # Yields the parent breadcrumb if any.
53
+ def yield_parent_breadcrumb(options = {})
54
+ if parent = parent_breadcrumb(options)
55
+ yield parent
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ attr_reader :context, :breadcrumb_key, :breadcrumb_args
62
+
63
+ # Returns merged options for rendering breadcrumbs.
64
+ def options_for_render(options = {})
65
+ style = options_for_style(options[:style] || DEFAULT_OPTIONS[:style])
66
+ DEFAULT_OPTIONS.merge(style).merge(options)
67
+ end
68
+
69
+ # Returns options for the given +style_key+ and raises an exception if it's not found.
70
+ def options_for_style(style_key)
71
+ if style = self.class.styles[style_key]
72
+ style
73
+ else
74
+ raise ArgumentError, "Breadcrumbs style #{style_key.inspect} not found. Use any of #{self.class.styles.keys.inspect}."
75
+ end
76
+ end
77
+
78
+ # Array of links with applied options.
79
+ def links_for_render(options = {})
80
+ out = links.dup
81
+
82
+ # Handle autoroot
83
+ if options[:autoroot] && out.map(&:key).exclude?(:root) && Gretel::Crumbs.crumb_defined?(:root)
84
+ out.unshift *Gretel::Crumb.new(context, :root).links
85
+ end
86
+
87
+ # Set current link to actual path
88
+ if options[:link_current_to_request_path] && out.any? && request
89
+ out.last.url = request.fullpath
90
+ end
91
+
92
+ # Handle show root alone
93
+ if out.size == 1 && !options[:display_single_fragment]
94
+ out.shift
95
+ end
96
+
97
+ # Set last link to current
98
+ out.last.try(:current!)
99
+
100
+ out
101
+ end
102
+
103
+ # Array of links for the path of the breadcrumb.
104
+ # Also reloads the breadcrumb configuration if needed.
105
+ def links
106
+ @links ||= if @breadcrumb_key.present?
107
+ # Reload breadcrumbs configuration if needed
108
+ Gretel::Crumbs.reload_if_needed
109
+
110
+ # Get breadcrumb set by the `breadcrumb` method
111
+ crumb = Gretel::Crumb.new(context, breadcrumb_key, *breadcrumb_args)
112
+
113
+ # Links of first crumb
114
+ links = crumb.links.dup
115
+
116
+ # Get parent links
117
+ links.unshift *parent_links_for(crumb)
118
+
119
+ links
120
+ else
121
+ []
122
+ end
123
+ end
124
+
125
+ # Returns parent links for the crumb.
126
+ def parent_links_for(crumb)
127
+ links = []
128
+ while crumb = crumb.parent
129
+ links.unshift *crumb.links
130
+ end
131
+ links
132
+ end
133
+
134
+ # Proxy to view context.
135
+ def method_missing(method, *args, &block)
136
+ context.send(method, *args, &block)
137
+ end
138
+
139
+ class << self
140
+ include Resettable
141
+
142
+ # Registers a style for later use.
143
+ #
144
+ # Gretel::Renderer.register_style :ul, { container_tag: :ul, fragment_tag: :li }
145
+ def register_style(style_key, options)
146
+ styles[style_key] = options
147
+ end
148
+
149
+ # Hash of registered styles.
150
+ def styles
151
+ @styles ||= DEFAULT_STYLES
152
+ end
153
+ end
154
+
155
+ class LinkCollection < Array
156
+ attr_reader :context, :links, :options
157
+
158
+ def initialize(context, links, options = {})
159
+ @context, @links, @options = context, links, options
160
+ concat links
161
+ end
162
+
163
+ # Helper for returning all link keys to allow for simple testing.
164
+ def keys
165
+ map(&:key)
166
+ end
167
+
168
+ # Renders the links into breadcrumbs.
169
+ def render
170
+ return "" if links.empty?
171
+
172
+ # Loop through all but the last (current) link and build HTML of the fragments
173
+ fragments = links[0..-2].map do |link|
174
+ render_fragment(options[:fragment_tag], link.text, link.url, options[:semantic])
175
+ end
176
+
177
+ # The current link is handled a little differently, and is only linked if specified in the options
178
+ current_link = links.last
179
+ fragments << render_fragment(options[:fragment_tag], current_link.text, (options[:link_current] ? current_link.url : nil), options[:semantic], class: options[:current_class], current_link: current_link.url)
180
+
181
+ # Build the final HTML
182
+ html_fragments = []
183
+
184
+ if options[:pretext].present?
185
+ html_fragments << content_tag(:span, options[:pretext], class: options[:pretext_class])
186
+ end
187
+
188
+ html_fragments << fragments.join(options[:separator])
189
+
190
+ if options[:posttext].present?
191
+ html_fragments << content_tag(:span, options[:posttext], class: options[:posttext_class])
192
+ end
193
+
194
+ html = html_fragments.join(" ").html_safe
195
+ content_tag(options[:container_tag], html, id: options[:id], class: options[:class])
196
+ end
197
+
198
+ alias :to_s :render
199
+
200
+ # Renders HTML for a breadcrumb fragment, i.e. a breadcrumb link.
201
+ def render_fragment(fragment_tag, text, url, semantic, options = {})
202
+ if semantic
203
+ render_semantic_fragment(fragment_tag, text, url, options)
204
+ else
205
+ render_nonsemantic_fragment(fragment_tag, text, url, options)
206
+ end
207
+ end
208
+
209
+ # Renders semantic fragment HTML.
210
+ def render_semantic_fragment(fragment_tag, text, url, options = {})
211
+ if fragment_tag
212
+ text = content_tag(:span, text, itemprop: "title")
213
+
214
+ if url.present?
215
+ text = breadcrumb_link_to(text, url, itemprop: "url")
216
+ elsif options[:current_link].present?
217
+ current_url = "#{root_url}#{options[:current_link].gsub(/^\//, '')}"
218
+ text = text + tag(:meta, itemprop: "url", content: current_url)
219
+ end
220
+
221
+ content_tag(fragment_tag, text, class: options[:class], itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
222
+ elsif url.present?
223
+ content_tag(:span, breadcrumb_link_to(content_tag(:span, text, itemprop: "title"), url, class: options[:class], itemprop: "url"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
224
+ else
225
+ content_tag(:span, content_tag(:span, text, class: options[:class], itemprop: "title"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
226
+ end
227
+ end
228
+
229
+ # Renders regular, non-semantic fragment HTML.
230
+ def render_nonsemantic_fragment(fragment_tag, text, url, options = {})
231
+ if fragment_tag
232
+ text = breadcrumb_link_to(text, url) if url.present?
233
+ content_tag(fragment_tag, text, class: options[:class])
234
+ elsif url.present?
235
+ breadcrumb_link_to(text, url, class: options[:class])
236
+ elsif options[:class].present?
237
+ content_tag(:span, text, class: options[:class])
238
+ else
239
+ text
240
+ end
241
+ end
242
+
243
+ # Proxy for +context.link_to+ that can be overridden by plugins.
244
+ def breadcrumb_link_to(name, url, options = {})
245
+ context.link_to(name, url, options)
246
+ end
247
+
248
+ # Proxy to view context.
249
+ def method_missing(method, *args, &block)
250
+ context.send(method, *args, &block)
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,13 @@
1
+ module Gretel
2
+ module Resettable
3
+ # Resets all instance variables and calls +reset!+ on all child modules and
4
+ # classes. Used for testing.
5
+ def reset!
6
+ instance_variables.each { |var| remove_instance_variable var }
7
+ constants.each do |c|
8
+ c = const_get(c)
9
+ c.reset! if c.respond_to?(:reset!)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Gretel
2
+ VERSION = "3.0.10"
3
+ end
@@ -0,0 +1,71 @@
1
+ module Gretel
2
+ module ViewHelpers
3
+ # Sets the current breadcrumb to be rendered elsewhere. Put it somewhere in the view, preferably in the top, before you render any breadcrumbs HTML:
4
+ #
5
+ # <% breadcrumb :category, @category %>
6
+ #
7
+ # If you pass an instance of an object that responds to +model_name+ (like an ActiveRecord model instance), the breadcrumb can be automatically inferred, so a shortcut for the above would be:
8
+ #
9
+ # <% breadcrumb @category %>
10
+ def breadcrumb(key = nil, *args)
11
+ if key.nil? || key.is_a?(Hash)
12
+ raise ArgumentError, "The `breadcrumb` method was called with #{key.inspect} as the key. This method is used to set the breadcrumb. Maybe you meant to call the `breadcrumbs` method (with an 's' in the end) which is used to render the breadcrumbs?"
13
+ end
14
+ @_gretel_renderer = Gretel::Renderer.new(self, key, *args)
15
+ end
16
+
17
+ # Yields a block where inside the block you have a different breadcrumb than outside.
18
+ #
19
+ # <% breadcrumb :about %>
20
+ #
21
+ # <%= breadcrumbs # shows the :about breadcrumb %>
22
+ #
23
+ # <% with_breadcrumb :product, Product.first do %>
24
+ # <%= breadcrumbs # shows the :product breadcrumb %>
25
+ # <% end %>
26
+ #
27
+ # <%= breadcrumbs # shows the :about breadcrumb %>
28
+ def with_breadcrumb(key, *args, &block)
29
+ original_renderer = @_gretel_renderer
30
+ @_gretel_renderer = Gretel::Renderer.new(self, key, *args)
31
+ yield
32
+ @_gretel_renderer = original_renderer
33
+ end
34
+
35
+ # Renders the breadcrumbs HTML, for example in your layout. See the readme for default options.
36
+ # <%= breadcrumbs pretext: "You are here: " %>
37
+ #
38
+ # If you supply a block, it will yield an array with the breadcrumb links so you can build the breadcrumbs HTML manually:
39
+ # <% breadcrumbs do |links| %>
40
+ # <% if links.any? %>
41
+ # You are here:
42
+ # <% links.each do |link| %>
43
+ # <%= link_to link.text, link.url %> (<%= link.key %>)
44
+ # <% end %>
45
+ # <% end %>
46
+ # <% end %>
47
+ def breadcrumbs(options = {})
48
+ gretel_renderer.render(options)
49
+ end
50
+
51
+ # Returns or yields parent breadcrumb (second-to-last in the trail) if it is present.
52
+ #
53
+ # <% parent_breadcrumb do |link| %>
54
+ # <%= link_to link.text, link.url %> (<%= link.key %>)
55
+ # <% end %>
56
+ def parent_breadcrumb(options = {}, &block)
57
+ if block_given?
58
+ gretel_renderer.yield_parent_breadcrumb(options, &block)
59
+ else
60
+ gretel_renderer.parent_breadcrumb(options)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # Reference to the Gretel breadcrumbs renderer.
67
+ def gretel_renderer
68
+ @_gretel_renderer ||= Gretel::Renderer.new(self, nil)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ class DeprecatedTest < ActionView::TestCase
4
+ include Gretel::ViewHelpers
5
+ fixtures :all
6
+ helper :application
7
+
8
+ setup do
9
+ Gretel.reset!
10
+ Gretel.suppress_deprecation_warnings = true
11
+ end
12
+
13
+ test "show root alone" do
14
+ breadcrumb :root
15
+ assert_equal %{<div class="breadcrumbs"><span class="current">Home</span></div>},
16
+ breadcrumbs(show_root_alone: true).to_s
17
+ end
18
+
19
+ test "deprecated configuration block" do
20
+ assert_raises RuntimeError do
21
+ Gretel::Crumbs.layout do
22
+ end
23
+ end
24
+ end
25
+
26
+ test ":default style key" do
27
+ breadcrumb :basic
28
+
29
+ assert_equal %{<div class="breadcrumbs"><a href="/">Home</a> &rsaquo; <span class="current">About</span></div>},
30
+ breadcrumbs(style: :default).to_s
31
+ end
32
+
33
+ test "yield links" do
34
+ breadcrumb :multiple_links_with_parent
35
+
36
+ out = breadcrumbs do |links|
37
+ links.map { |link| [link.key, link.text, link.url] }
38
+ end
39
+
40
+ assert_equal [[:root, "Home", "/"],
41
+ [:basic, "About", "/about"],
42
+ [:multiple_links_with_parent, "Contact", "/about/contact"],
43
+ [:multiple_links_with_parent, "Contact form", "/about/contact/form"]], out
44
+ end
45
+ end