simple-navigation 3.13.0 → 4.0.5

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. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +10 -3
  5. data/CHANGELOG.md +420 -0
  6. data/Guardfile +4 -2
  7. data/LICENSE +1 -1
  8. data/README.md +30 -7
  9. data/Rakefile +25 -2
  10. data/gemfiles/.bundle/config +2 -0
  11. data/gemfiles/rails-3-2-stable.gemfile +11 -0
  12. data/gemfiles/rails-4-1-stable.gemfile +7 -0
  13. data/gemfiles/rails-4-2-stable.gemfile +7 -0
  14. data/generators/navigation_config/navigation_config_generator.rb +0 -1
  15. data/generators/navigation_config/templates/config/navigation.rb +18 -15
  16. data/lib/simple_navigation.rb +25 -42
  17. data/lib/simple_navigation/adapters/padrino.rb +2 -2
  18. data/lib/simple_navigation/adapters/rails.rb +1 -24
  19. data/lib/simple_navigation/adapters/sinatra.rb +2 -11
  20. data/lib/simple_navigation/config_file.rb +36 -0
  21. data/lib/simple_navigation/config_file_finder.rb +42 -0
  22. data/lib/simple_navigation/{core/configuration.rb → configuration.rb} +7 -1
  23. data/lib/simple_navigation/{rendering/helpers.rb → helpers.rb} +0 -4
  24. data/lib/simple_navigation/{core/item.rb → item.rb} +76 -83
  25. data/lib/simple_navigation/{core/item_adapter.rb → item_adapter.rb} +3 -17
  26. data/lib/simple_navigation/{core/item_container.rb → item_container.rb} +23 -14
  27. data/lib/simple_navigation/{core/items_provider.rb → items_provider.rb} +0 -0
  28. data/lib/simple_navigation/railtie.rb +7 -0
  29. data/lib/simple_navigation/renderer.rb +12 -0
  30. data/lib/simple_navigation/{rendering/renderer → renderer}/base.rb +1 -1
  31. data/lib/simple_navigation/{rendering/renderer → renderer}/breadcrumbs.rb +0 -0
  32. data/lib/simple_navigation/{rendering/renderer → renderer}/json.rb +2 -0
  33. data/lib/simple_navigation/{rendering/renderer → renderer}/links.rb +0 -0
  34. data/lib/simple_navigation/{rendering/renderer → renderer}/list.rb +0 -0
  35. data/lib/simple_navigation/{rendering/renderer → renderer}/text.rb +0 -0
  36. data/lib/simple_navigation/version.rb +1 -1
  37. data/simple-navigation.gemspec +6 -5
  38. data/spec/fake_app/config/navigation.rb +6 -0
  39. data/spec/fake_app/rails_app.rb +35 -0
  40. data/spec/initializers/coveralls.rb +3 -0
  41. data/spec/initializers/have_css_matcher.rb +8 -3
  42. data/spec/initializers/memfs.rb +7 -0
  43. data/spec/initializers/rails.rb +4 -0
  44. data/spec/initializers/rspec.rb +7 -0
  45. data/spec/integration/rendering_navigation_spec.rb +14 -0
  46. data/spec/{lib/simple_navigation → simple_navigation}/adapters/padrino_spec.rb +0 -2
  47. data/spec/{lib/simple_navigation → simple_navigation}/adapters/rails_spec.rb +43 -94
  48. data/spec/{lib/simple_navigation → simple_navigation}/adapters/sinatra_spec.rb +4 -6
  49. data/spec/simple_navigation/config_file_finder_spec.rb +50 -0
  50. data/spec/simple_navigation/config_file_spec.rb +25 -0
  51. data/spec/{lib/simple_navigation/core → simple_navigation}/configuration_spec.rb +29 -19
  52. data/spec/{lib/simple_navigation/rendering → simple_navigation}/helpers_spec.rb +10 -13
  53. data/spec/{lib/simple_navigation/core → simple_navigation}/item_adapter_spec.rb +14 -11
  54. data/spec/{lib/simple_navigation/core → simple_navigation}/item_container_spec.rb +130 -42
  55. data/spec/simple_navigation/item_spec.rb +475 -0
  56. data/spec/{lib/simple_navigation/core → simple_navigation}/items_provider_spec.rb +1 -3
  57. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/base_spec.rb +34 -36
  58. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/breadcrumbs_spec.rb +4 -7
  59. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/json_spec.rb +5 -11
  60. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/links_spec.rb +5 -8
  61. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/list_spec.rb +4 -7
  62. data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/text_spec.rb +4 -7
  63. data/spec/simple_navigation_spec.rb +190 -0
  64. data/spec/spec_helper.rb +29 -35
  65. metadata +128 -113
  66. data/CHANGELOG +0 -288
  67. data/lib/simple_navigation/core.rb +0 -5
  68. data/lib/simple_navigation/rails_controller_methods.rb +0 -164
  69. data/lib/simple_navigation/rendering.rb +0 -12
  70. data/rails/init.rb +0 -1
  71. data/spec/lib/simple_navigation/core/item_spec.rb +0 -703
  72. data/spec/lib/simple_navigation/rails_controller_methods_spec.rb +0 -270
  73. data/spec/lib/simple_navigation_spec.rb +0 -300
@@ -7,7 +7,10 @@ module SimpleNavigation
7
7
 
8
8
  attr_accessor :autogenerate_item_ids,
9
9
  :auto_highlight,
10
- :consider_item_names_as_safe
10
+ :consider_item_names_as_safe,
11
+ :highlight_on_subpath,
12
+ :ignore_query_params_on_auto_highlight,
13
+ :ignore_anchors_on_auto_highlight
11
14
 
12
15
  attr_reader :primary_navigation
13
16
 
@@ -33,6 +36,9 @@ module SimpleNavigation
33
36
  @autogenerate_item_ids = true
34
37
  @auto_highlight = true
35
38
  @consider_item_names_as_safe = false
39
+ @highlight_on_subpath = false
40
+ @ignore_anchors_on_auto_highlight = true
41
+ @ignore_query_params_on_auto_highlight = true
36
42
  end
37
43
 
38
44
  def active_leaf_class
@@ -34,10 +34,6 @@ module SimpleNavigation
34
34
  SimpleNavigation.config.items(options[:items], &block)
35
35
  end
36
36
 
37
- if SimpleNavigation.respond_to?(:handle_explicit_navigation)
38
- SimpleNavigation.handle_explicit_navigation
39
- end
40
-
41
37
  unless SimpleNavigation.primary_navigation
42
38
  fail 'no primary navigation defined, either use a navigation config ' \
43
39
  'file or pass items directly to render_navigation'
@@ -2,27 +2,23 @@ module SimpleNavigation
2
2
  # Represents an item in your navigation.
3
3
  # Gets generated by the item method in the config-file.
4
4
  class Item
5
- attr_reader :highlights_on,
6
- :key,
7
- :method,
5
+ attr_reader :key,
6
+ :name,
8
7
  :sub_navigation,
9
8
  :url
10
9
 
11
- attr_writer :html_options
12
-
13
10
  # see ItemContainer#item
14
11
  #
15
12
  # The subnavigation (if any) is either provided by a block or
16
13
  # passed in directly as <tt>items</tt>
17
- def initialize(container, key, name, url_or_options = {}, options_or_nil = {}, items = nil, &sub_nav_block)
18
- options = setup_url_and_options(url_or_options, options_or_nil)
19
-
20
- @key = key
21
- @method = options.delete(:method)
22
- @name = name
23
- @container = setup_container(container, options)
14
+ def initialize(container, key, name, url = nil, opts = {}, &sub_nav_block)
15
+ self.container = container
16
+ self.key = key
17
+ self.name = name.respond_to?(:call) ? name.call : name
18
+ self.url = url.respond_to?(:call) ? url.call : url
19
+ self.options = opts
24
20
 
25
- setup_sub_navigation(items, &sub_nav_block)
21
+ setup_sub_navigation(options[:items], &sub_nav_block)
26
22
  end
27
23
 
28
24
  # Returns the item's name.
@@ -32,8 +28,8 @@ module SimpleNavigation
32
28
  #
33
29
  def name(options = {})
34
30
  options = { apply_generator: true }.merge(options)
35
- if (options[:apply_generator])
36
- SimpleNavigation.config.name_generator.call(@name, self)
31
+ if options[:apply_generator]
32
+ config.name_generator.call(@name, self)
37
33
  else
38
34
  @name
39
35
  end
@@ -42,35 +38,32 @@ module SimpleNavigation
42
38
  # Returns true if this navigation item should be rendered as 'selected'.
43
39
  # An item is selected if
44
40
  #
45
- # * it has been explicitly selected in a controller or
46
41
  # * it has a subnavigation and one of its subnavigation items is selected or
47
42
  # * its url matches the url of the current request (auto highlighting)
48
43
  #
49
44
  def selected?
50
- @selected ||= selected_by_config? ||
51
- selected_by_subnav? ||
52
- selected_by_condition?
45
+ @selected ||= selected_by_subnav? || selected_by_condition?
53
46
  end
54
47
 
55
48
  # Returns the html-options hash for the item, i.e. the options specified
56
49
  # for this item in the config-file.
57
50
  # It also adds the 'selected' class to the list of classes if necessary.
58
51
  def html_options
59
- options = @html_options
60
- options[:id] ||= autogenerated_item_id if autogenerate_item_ids?
52
+ html_opts = options.fetch(:html) { Hash.new }
53
+ html_opts[:id] ||= autogenerated_item_id
61
54
 
62
- classes = [@html_options[:class], selected_class, active_leaf_class]
55
+ classes = [html_opts[:class], selected_class, active_leaf_class]
63
56
  classes = classes.flatten.compact.join(' ')
64
- options[:class] = classes unless classes.nil? || classes.empty?
57
+ html_opts[:class] = classes if classes && !classes.empty?
65
58
 
66
- options
59
+ html_opts
67
60
  end
68
61
 
69
62
  # Returns the configured active_leaf_class if the item is the selected leaf,
70
63
  # nil otherwise
71
64
  def active_leaf_class
72
65
  if !selected_by_subnav? && selected_by_condition?
73
- SimpleNavigation.config.active_leaf_class
66
+ config.active_leaf_class
74
67
  end
75
68
  end
76
69
 
@@ -78,10 +71,26 @@ module SimpleNavigation
78
71
  # nil otherwise
79
72
  def selected_class
80
73
  if selected?
81
- container.selected_class || SimpleNavigation.config.selected_class
74
+ container.selected_class || config.selected_class
82
75
  end
83
76
  end
84
77
 
78
+ # Returns the :highlights_on option as set at initialization
79
+ def highlights_on
80
+ @highlights_on ||= options[:highlights_on]
81
+ end
82
+
83
+ # Returns the :method option as set at initialization
84
+ def method
85
+ @method ||= options[:method]
86
+ end
87
+
88
+ # Returns the html attributes for the link as set with the :link_html option
89
+ # at initialization
90
+ def link_html_options
91
+ @link_html_options ||= options[:link_html]
92
+ end
93
+
85
94
  protected
86
95
 
87
96
  # Returns true if item has a subnavigation and
@@ -90,10 +99,6 @@ module SimpleNavigation
90
99
  sub_navigation && sub_navigation.selected?
91
100
  end
92
101
 
93
- def selected_by_config?
94
- false
95
- end
96
-
97
102
  # Returns true if the item's url matches the request's current url.
98
103
  def selected_by_condition?
99
104
  highlights_on ? selected_by_highlights_on? : selected_by_autohighlight?
@@ -104,86 +109,74 @@ module SimpleNavigation
104
109
  url == '/' && SimpleNavigation.request_path == '/'
105
110
  end
106
111
 
107
- # Returns true if the item's id should be added to the rendered output.
108
- def autogenerate_item_ids?
109
- SimpleNavigation.config.autogenerate_item_ids
110
- end
111
-
112
112
  # Returns the item's id which is added to the rendered output.
113
113
  def autogenerated_item_id
114
- SimpleNavigation.config.id_generator.call(key)
114
+ config.id_generator.call(key) if config.autogenerate_item_ids
115
115
  end
116
116
 
117
117
  # Return true if auto_highlight is on for this item.
118
118
  def auto_highlight?
119
- SimpleNavigation.config.auto_highlight && container.auto_highlight
120
- end
121
-
122
- def url_without_anchor
123
- url && url.split('#').first
119
+ config.auto_highlight && container.auto_highlight
124
120
  end
125
121
 
126
122
  private
127
123
 
128
- attr_reader :container
124
+ attr_accessor :container,
125
+ :options
129
126
 
130
- attr_writer :highlights_on,
127
+ attr_writer :key,
128
+ :name,
131
129
  :sub_navigation,
132
130
  :url
133
131
 
134
- def selected_by_autohighlight?
135
- auto_highlight? &&
136
- (root_path_match? ||
137
- (url_without_anchor &&
138
- SimpleNavigation.current_page?(url_without_anchor)))
132
+ def config
133
+ SimpleNavigation.config
139
134
  end
140
135
 
141
- def selected_by_highlights_on?
142
- return false unless highlights_on
136
+ def request_uri
137
+ SimpleNavigation.request_uri
138
+ end
143
139
 
144
- case highlights_on
145
- when Regexp then SimpleNavigation.request_uri =~ highlights_on
146
- when Proc then highlights_on.call
147
- when :subpath
148
- escaped_url = Regexp.escape(url_without_anchor)
149
- !!(SimpleNavigation.request_uri =~ /^#{escaped_url}(\/|$|\?)/i)
150
- else
151
- fail ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath'
152
- end
140
+ def remove_anchors(url_with_anchors)
141
+ url_with_anchors && url_with_anchors.split('#').first
153
142
  end
154
143
 
155
- def setup_container(container, options = {})
156
- if options[:container_class]
157
- container.dom_class = options.delete(:container_class)
158
- end
144
+ def remove_query_params(url_with_params)
145
+ url_with_params && url_with_params.split('?').first
146
+ end
159
147
 
160
- if options[:container_id]
161
- container.dom_id = options.delete(:container_id)
162
- end
148
+ def url_for_autohighlight
149
+ relevant_url = remove_anchors(self.url) if config.ignore_anchors_on_auto_highlight
150
+ relevant_url = remove_query_params(relevant_url) if config.ignore_query_params_on_auto_highlight
151
+ relevant_url
152
+ end
163
153
 
164
- container.dom_attributes = if options[:container_attributes]
165
- options.delete(:container_attributes)
166
- else
167
- {}
168
- end
154
+ def selected_by_autohighlight?
155
+ return false unless auto_highlight?
156
+ return false unless self.url
169
157
 
170
- if options[:selected_class]
171
- container.selected_class = options.delete(:selected_class)
172
- end
158
+ root_path_match? ||
159
+ (url_for_autohighlight && SimpleNavigation.current_page?(url_for_autohighlight)) ||
160
+ autohighlight_by_subpath?
161
+ end
173
162
 
174
- container
163
+ def autohighlight_by_subpath?
164
+ config.highlight_on_subpath && selected_by_subpath?
175
165
  end
176
166
 
177
- def setup_url_and_options(url_or_options, options_or_nil)
178
- case url_or_options
179
- when Hash then options = url_or_options # there is no url
180
- when Proc then self.url = url_or_options.call
181
- else self.url = url_or_options
167
+ def selected_by_highlights_on?
168
+ case highlights_on
169
+ when Regexp then !!(request_uri =~ highlights_on)
170
+ when Proc then highlights_on.call
171
+ when :subpath then selected_by_subpath?
172
+ else
173
+ fail ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath'
182
174
  end
175
+ end
183
176
 
184
- options ||= options_or_nil
185
- self.highlights_on = options.delete(:highlights_on)
186
- self.html_options = options
177
+ def selected_by_subpath?
178
+ escaped_url = Regexp.escape(url_for_autohighlight)
179
+ !!(request_uri =~ /^#{escaped_url}(\/|$||\?)/i)
187
180
  end
188
181
 
189
182
  def setup_sub_navigation(items = nil, &sub_nav_block)
@@ -1,4 +1,5 @@
1
1
  require 'forwardable'
2
+ require 'ostruct'
2
3
 
3
4
  module SimpleNavigation
4
5
  # This class acts as an adapter to items that are not defined using the DSL
@@ -30,7 +31,7 @@ module SimpleNavigation
30
31
  attr_reader :item
31
32
 
32
33
  def initialize(item)
33
- @item = item.is_a?(Hash) ? to_object(item) : item
34
+ @item = item.is_a?(Hash) ? OpenStruct.new(item) : item
34
35
  end
35
36
 
36
37
  # Returns the options for this item. If the wrapped item does not implement
@@ -47,22 +48,7 @@ module SimpleNavigation
47
48
 
48
49
  # Converts this Item into a SimpleNavigation::Item
49
50
  def to_simple_navigation_item(item_container)
50
- SimpleNavigation::Item.new(item_container, key, name, url, options, items)
51
- end
52
-
53
- protected
54
-
55
- # Converts the specified hash into an object. Each key will be added
56
- # as method.
57
- def to_object(hash)
58
- mod = Module.new do
59
- hash.each_pair do |key, value|
60
- define_method key do
61
- value
62
- end
63
- end
64
- end
65
- Object.new.extend(mod)
51
+ SimpleNavigation::Item.new(item_container, key, name, url, options)
66
52
  end
67
53
  end
68
54
  end
@@ -59,22 +59,18 @@ module SimpleNavigation
59
59
  # set a regexp which is matched againstthe current URI.
60
60
  #
61
61
  # The <tt>block</tt> - if specified - will hold the item's sub_navigation.
62
- def item(key, name, url_or_options = {}, options_or_nil = {}, &block)
63
- options = url_or_options.is_a?(Hash) ? url_or_options : options_or_nil
62
+ def item(key, name, url = nil, options = {}, &block)
64
63
  return unless should_add_item?(options)
65
- items << SimpleNavigation::Item.new(self,
66
- key,
67
- name,
68
- url_or_options,
69
- options_or_nil,
70
- nil,
71
- &block)
64
+ item = Item.new(self, key, name, url, options, &block)
65
+ add_item item, options
72
66
  end
73
67
 
74
68
  def items=(new_items)
75
- @items += new_items.map { |item| ItemAdapter.new(item) }
76
- .keep_if { |item| should_add_item?(item.options) }
77
- .map { |item| item.to_simple_navigation_item(self) }
69
+ new_items.each do |item|
70
+ item_adapter = ItemAdapter.new(item)
71
+ next unless should_add_item?(item_adapter.options)
72
+ add_item item_adapter.to_simple_navigation_item(self), item_adapter.options
73
+ end
78
74
  end
79
75
 
80
76
  # Returns the Item with the specified key, nil otherwise.
@@ -148,6 +144,19 @@ module SimpleNavigation
148
144
 
149
145
  private
150
146
 
147
+ def add_item(item, options)
148
+ items << item
149
+ modify_dom_attributes(options)
150
+ end
151
+
152
+ def modify_dom_attributes(options)
153
+ return unless container_options = options[:container]
154
+ self.dom_attributes = container_options.fetch(:attributes) { dom_attributes }
155
+ self.dom_class = container_options.fetch(:class) { dom_class }
156
+ self.dom_id = container_options.fetch(:id) { dom_id }
157
+ self.selected_class = container_options.fetch(:selected_class) { selected_class }
158
+ end
159
+
151
160
  # FIXME: raise an exception if :rederer is a symbol and it is not registred
152
161
  # in SimpleNavigation.registered_renderers
153
162
  def renderer_instance(options)
@@ -166,8 +175,8 @@ module SimpleNavigation
166
175
  end
167
176
 
168
177
  def should_add_item?(options)
169
- [options.delete(:if)].flatten.compact.all? { |m| evaluate_method(m) } &&
170
- [options.delete(:unless)].flatten.compact.none? { |m| evaluate_method(m) }
178
+ [options[:if]].flatten.compact.all? { |m| evaluate_method(m) } &&
179
+ [options[:unless]].flatten.compact.none? { |m| evaluate_method(m) }
171
180
  end
172
181
 
173
182
  def evaluate_method(method)
@@ -0,0 +1,7 @@
1
+ module SimpleNavigation
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'simple_navigation.register' do |app|
4
+ SimpleNavigation.register
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ require 'simple_navigation/helpers'
2
+ require 'simple_navigation/renderer/base'
3
+
4
+ module SimpleNavigation
5
+ module Renderer
6
+ autoload :List, 'simple_navigation/renderer/list'
7
+ autoload :Links, 'simple_navigation/renderer/links'
8
+ autoload :Breadcrumbs, 'simple_navigation/renderer/breadcrumbs'
9
+ autoload :Text, 'simple_navigation/renderer/text'
10
+ autoload :Json, 'simple_navigation/renderer/json'
11
+ end
12
+ end
@@ -95,7 +95,7 @@ module SimpleNavigation
95
95
  class: item.selected_class
96
96
  }.reject { |_, v| v.nil? }
97
97
 
98
- link_options = item.html_options[:link]
98
+ link_options = item.link_html_options
99
99
 
100
100
  return special_options unless link_options
101
101
 
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module SimpleNavigation
2
4
  module Renderer
3
5
  # Renders the navigation items as a object tree serialized as a json string,
@@ -1,3 +1,3 @@
1
1
  module SimpleNavigation
2
- VERSION = '3.13.0'
2
+ VERSION = '4.0.5'
3
3
  end