easycrumbs 0.1.0
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +22 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/easycrumbs.gemspec +60 -0
- data/lib/easycrumbs.rb +4 -0
- data/lib/easycrumbs/breadcrumb.rb +95 -0
- data/lib/easycrumbs/collection.rb +172 -0
- data/lib/easycrumbs/errors.rb +34 -0
- data/lib/easycrumbs/view_helpers.rb +7 -0
- data/test/helper.rb +72 -0
- data/test/routes.rb +9 -0
- data/test/test_easycrumbs.rb +337 -0
- metadata +97 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Stanisław Kolarzowski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
= easycrumbs
|
2
|
+
|
3
|
+
Easy breadcrumbs for your site
|
4
|
+
|
5
|
+
* gemcutter[not yet]
|
6
|
+
* repository[http://github.com/staszek/easycrumbs]
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
|
10
|
+
If you don't have the {Gemcutter sources}[http://gemcutter.org/pages/gem_docs] yet:
|
11
|
+
gem sources -a http://gemcutter.org
|
12
|
+
|
13
|
+
To install the gem type:
|
14
|
+
gem install easycrumbs
|
15
|
+
|
16
|
+
== Usage
|
17
|
+
|
18
|
+
It does not work yet.
|
19
|
+
|
20
|
+
== Copyright
|
21
|
+
|
22
|
+
Copyright (c) 2010 Stanisław Kolarzowski. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "easycrumbs"
|
8
|
+
gem.summary = %Q{Easy breadcrumbs}
|
9
|
+
gem.description = %Q{Easy breadcrumbs for your website}
|
10
|
+
gem.email = "stanislaw.kolarzowski@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/staszek/easycrumbs"
|
12
|
+
gem.authors = ["Stanisław Kolarzowski"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
task :test => :check_dependencies
|
29
|
+
|
30
|
+
task :default => :test
|
31
|
+
|
32
|
+
require 'rake/rdoctask'
|
33
|
+
Rake::RDocTask.new do |rdoc|
|
34
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
35
|
+
|
36
|
+
rdoc.rdoc_dir = 'rdoc'
|
37
|
+
rdoc.title = "easycrumbs #{version}"
|
38
|
+
rdoc.rdoc_files.include('README*')
|
39
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
40
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/easycrumbs.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{easycrumbs}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Stanis\305\202aw Kolarzowski"]
|
12
|
+
s.date = %q{2010-08-08}
|
13
|
+
s.description = %q{Easy breadcrumbs for your website}
|
14
|
+
s.email = %q{stanislaw.kolarzowski@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"easycrumbs.gemspec",
|
27
|
+
"lib/easycrumbs.rb",
|
28
|
+
"lib/easycrumbs/breadcrumb.rb",
|
29
|
+
"lib/easycrumbs/collection.rb",
|
30
|
+
"lib/easycrumbs/errors.rb",
|
31
|
+
"lib/easycrumbs/view_helpers.rb",
|
32
|
+
"test/helper.rb",
|
33
|
+
"test/routes.rb",
|
34
|
+
"test/test_easycrumbs.rb"
|
35
|
+
]
|
36
|
+
s.homepage = %q{http://github.com/staszek/easycrumbs}
|
37
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.7}
|
40
|
+
s.summary = %q{Easy breadcrumbs}
|
41
|
+
s.test_files = [
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/routes.rb",
|
44
|
+
"test/test_easycrumbs.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
55
|
+
end
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/easycrumbs.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module EasyCrumbs
|
2
|
+
class Breadcrumb
|
3
|
+
attr_reader :object, :name, :path
|
4
|
+
|
5
|
+
# Breadcrumb object:
|
6
|
+
# object - just object from application. Could be a model or controller
|
7
|
+
# name - printed name
|
8
|
+
# path - path to this object
|
9
|
+
def initialize(object, options = {})
|
10
|
+
@object = set_object(object)
|
11
|
+
@name = set_name(options)
|
12
|
+
@path = set_path(options[:path], options[:blank_links])
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Object from application must be a model or controller
|
17
|
+
def set_object(object)
|
18
|
+
raise EasyCrumbs::InvalidObject unless object.is_a?(ActionController::Base) || object.is_a?(ActiveRecord::Base)
|
19
|
+
object
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set name for model or controller
|
23
|
+
def set_name(options = {})
|
24
|
+
if object.is_a?(ActiveRecord::Base)
|
25
|
+
options[:name_column] ||= "breadcrumb"
|
26
|
+
name = name_for_model(options[:name_column], options[:i18n])
|
27
|
+
else
|
28
|
+
name = name_for_controller(options[:i18n])
|
29
|
+
end
|
30
|
+
add_prefix(name, options[:action], options[:prefix], options[:i18n])
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set name for model
|
34
|
+
# Model has to have column equal to name_column
|
35
|
+
def name_for_model(name_column, i18n)
|
36
|
+
raise EasyCrumbs::NoName.new(@object.class, name_column) unless @object.respond_to? name_column
|
37
|
+
name = @object.send name_column
|
38
|
+
name.nil? ? name_for_nil(@object, i18n) : name
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set name for object if it is nil
|
42
|
+
def name_for_nil(object, i18n)
|
43
|
+
i18n == true ? I18n.t("breadcrumbs.models.#{object.class.to_s.downcase}") : @object.class.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set name for controller
|
47
|
+
def name_for_controller(i18n)
|
48
|
+
if @object.respond_to? :breadcrumb
|
49
|
+
@object.breadcrumb
|
50
|
+
else
|
51
|
+
i18n == true ? I18n.t("breadcrumbs.controllers.#{@object.controller_name}") : @object.controller_name.titlecase
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add specyfic prefix if action is passed
|
56
|
+
# prefix =
|
57
|
+
# :every - add prefix for every action
|
58
|
+
# :none - do not add prefix
|
59
|
+
# [array of symbols] - add prefix only for actions in array
|
60
|
+
#
|
61
|
+
# Example
|
62
|
+
# [:show, :new] - add prefix only for show and new
|
63
|
+
def add_prefix(object_name, action, prefix, i18n)
|
64
|
+
name = object_name
|
65
|
+
unless action.nil?
|
66
|
+
prefix = case prefix
|
67
|
+
when :every
|
68
|
+
[action.to_sym]
|
69
|
+
when :none
|
70
|
+
[]
|
71
|
+
else
|
72
|
+
prefix || [:new, :edit]
|
73
|
+
end
|
74
|
+
name = action_name(action, i18n, name) if prefix.include?(action.to_sym)
|
75
|
+
end
|
76
|
+
name
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return name of action.
|
80
|
+
def action_name(action, i18n, name)
|
81
|
+
i18n == true ? I18n.t("breadcrumbs.actions.#{action}", :name => name) : "#{action.titlecase} #{name}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set path using hash from ActionController::Routing::Routes.recognize_path
|
85
|
+
# Example looks like:
|
86
|
+
# {:country_id => "1", :movie_id => "1", :id => "1", :action => "show", :controller => "movies"}
|
87
|
+
def set_path(path, blank_links)
|
88
|
+
path.nil? || path.empty? ? "/" : ActionController::Routing::Routes.generate_extras(path).first
|
89
|
+
rescue ActionController::RoutingError => e
|
90
|
+
raise EasyCrumbs::NoPath.new(e.message) unless blank_links == true
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module EasyCrumbs
|
2
|
+
class Collection
|
3
|
+
attr_reader :breadcrumbs, :route, :path
|
4
|
+
|
5
|
+
def initialize(request, options = {})
|
6
|
+
@request = request
|
7
|
+
|
8
|
+
@route = find_route
|
9
|
+
@path = find_path
|
10
|
+
@controller = @path[:controller]
|
11
|
+
@action = @path[:action]
|
12
|
+
|
13
|
+
@pathes = make_pathes
|
14
|
+
@breadcrumbs = make_breadcrumbs(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finding route with given path and method
|
18
|
+
# Return ActionController:Routing::Route object
|
19
|
+
def find_route
|
20
|
+
routes = ActionController::Routing::Routes.routes.select do |route|
|
21
|
+
route.recognize(request_path, :method => request_method) != nil
|
22
|
+
end
|
23
|
+
raise EasyCrumbs::NotRecognized if routes.empty?
|
24
|
+
routes.first
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return hash with path parameter
|
28
|
+
# for example:
|
29
|
+
# { :controller => 'movies', :action => 'show', :country_id => '23', :id => '12' }
|
30
|
+
def find_path
|
31
|
+
@route.recognize(request_path, :method => request_method)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Select only static and dynamic segments from route. Static segments points at controllers and dynamic points at models.
|
35
|
+
# It is given in right order
|
36
|
+
# If last segment is equal to member action then it should be deleted. for example movies/123/edit should not return "edit" segment
|
37
|
+
def segments
|
38
|
+
segments = @route.segments.select do |segment|
|
39
|
+
[ActionController::Routing::DynamicSegment, ActionController::Routing::StaticSegment].include? segment.class
|
40
|
+
end
|
41
|
+
segments.pop if segments.last.is_a?(ActionController::Routing::StaticSegment) && segments.last.value == @action && segments.last.value != 'new'
|
42
|
+
segments
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returning controller object from static segment
|
46
|
+
def pick_controller(segment)
|
47
|
+
segment = last_controller_segment if segment.value == "new"
|
48
|
+
"#{segment.value.titlecase}Controller".constantize.new
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns last controller segment in segments
|
52
|
+
def last_controller_segment
|
53
|
+
segments.select{ |seg| seg.is_a?(ActionController::Routing::StaticSegment) && seg.value != "new"}.last
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrung model object from dynamic segment
|
57
|
+
# If key has not model name then it is taken from current controller(it is taken from path)
|
58
|
+
def pick_model(segment)
|
59
|
+
key = segment.key
|
60
|
+
if key == :id
|
61
|
+
model = @controller.singularize
|
62
|
+
else
|
63
|
+
model = key.to_s[0..-4] # model_id without last 3 signs = model
|
64
|
+
end
|
65
|
+
model = model.titlecase.constantize
|
66
|
+
model.find(@path[key])
|
67
|
+
end
|
68
|
+
|
69
|
+
# Retruning array of controllers and models objects from right segments
|
70
|
+
# for example
|
71
|
+
# [#<CountriesController:0x001>, #<Country:0x001 @name="usa">, #<MoviesController:0x001>, #<Movie:0x001 @name="titanic">]
|
72
|
+
def objects
|
73
|
+
segments.map do |segment|
|
74
|
+
if segment.is_a? ActionController::Routing::DynamicSegment
|
75
|
+
pick_model(segment)
|
76
|
+
else
|
77
|
+
pick_controller(segment)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return array of breadcrumbs object in right order
|
83
|
+
def make_breadcrumbs(options = {})
|
84
|
+
breadcrumbs = [Breadcrumb.new(ApplicationController.new, options)]
|
85
|
+
objects.each_with_index do |object, index|
|
86
|
+
options.merge!({:action => @action}) if index == objects.size - 1
|
87
|
+
options.merge!({:path => @pathes[index]})
|
88
|
+
breadcrumbs << Breadcrumb.new(object, options)
|
89
|
+
end
|
90
|
+
breadcrumbs
|
91
|
+
end
|
92
|
+
|
93
|
+
# Retrurn parameters for path of model
|
94
|
+
# If it is last object then action is equal to request action
|
95
|
+
def path_for_model(segment)
|
96
|
+
key = segment.key
|
97
|
+
if key == :id
|
98
|
+
{:action => @action, :id => @path[key]}
|
99
|
+
else
|
100
|
+
{:action => 'show', key => @path[key]}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Retrun parameters for path of controller
|
105
|
+
def path_for_controller(segment)
|
106
|
+
if segment.value == "new"
|
107
|
+
{:action => "new", :controller => last_controller_segment.value}
|
108
|
+
else
|
109
|
+
{:action => 'index', :controller => segment.value}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# If controller name is connected with object then parameter should be :id instead of :object_id
|
114
|
+
# {:controller => 'movies', :movie_id => 1} will be {:controller => 'movies', :id => 1}
|
115
|
+
def repaired_model_path(path)
|
116
|
+
path = path.dup
|
117
|
+
object_param = "#{path[:controller].singularize}_id".to_sym
|
118
|
+
id = path.delete(object_param)
|
119
|
+
id.nil? ? path : path.merge({:id => id})
|
120
|
+
end
|
121
|
+
|
122
|
+
# Retrun array of pathes for every segment
|
123
|
+
# for example:
|
124
|
+
# countries > 1 > movies > 2 > actors> 3
|
125
|
+
#
|
126
|
+
# {:action => 'index', :controller => 'countries'},
|
127
|
+
# {:action => 'show', :controller => 'countries', :id => 1},
|
128
|
+
# {:action => 'index', :controller => 'movies', :country_id => 1},
|
129
|
+
# {:action => 'show', :controller => 'movies', :country_id => 1, :id => 2},
|
130
|
+
# {:action => 'index', :controller => 'actors', :country_id => 1, :movie_id => 2},
|
131
|
+
# {:action => 'update', :controller => 'actors', :country_id => 1, :movie_id => 2, :id => 3}
|
132
|
+
def make_pathes
|
133
|
+
path = {}
|
134
|
+
segments.map do |segment|
|
135
|
+
if segment.is_a? ActionController::Routing::DynamicSegment
|
136
|
+
path.merge! path_for_model(segment)
|
137
|
+
result = repaired_model_path(path)
|
138
|
+
else
|
139
|
+
result = path.merge! path_for_controller(segment)
|
140
|
+
end
|
141
|
+
result.dup
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def render(options = {})
|
146
|
+
options[:separator] ||= " > "
|
147
|
+
options[:last_link] = true if options[:last_link].nil?
|
148
|
+
|
149
|
+
elements = @breadcrumbs.map do |breadcrumb|
|
150
|
+
if options[:last_link] == false && breadcrumb == @breadcrumbs.last
|
151
|
+
breadcrumb.name
|
152
|
+
else
|
153
|
+
link_to breadcrumb.name, breadcrumb.path
|
154
|
+
end
|
155
|
+
end
|
156
|
+
elements.join(options[:separator])
|
157
|
+
end
|
158
|
+
private
|
159
|
+
|
160
|
+
def request_path
|
161
|
+
@request.path
|
162
|
+
end
|
163
|
+
|
164
|
+
def request_method
|
165
|
+
@request.method
|
166
|
+
end
|
167
|
+
|
168
|
+
def link_to(name, path)
|
169
|
+
"<a href=\"#{path}\">#{name}</a>"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module EasyCrumbs
|
2
|
+
class InvalidObject < StandardError
|
3
|
+
def message
|
4
|
+
"object should be Controller(ActionController::Base) or Model(ActiveRecord::Base)"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class NoName < StandardError
|
9
|
+
def initialize(object_class, column)
|
10
|
+
@object_class = object_class
|
11
|
+
@column = column
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
"Can not set name. Model #{@object_class} does not have column \"#{@column}\". Try change column name or create \"#{@column}\" method"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class NoPath < StandardError
|
20
|
+
def initialize(routing_error)
|
21
|
+
@routing_error = routing_error
|
22
|
+
end
|
23
|
+
|
24
|
+
def message
|
25
|
+
"Can not set path. You can use :blank_links to return nil for no-recognized pathes. RoutingError: #{@routing_error}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class NotRecognized < StandardError
|
30
|
+
def message
|
31
|
+
"Can not recognize main path."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require "mocha"
|
5
|
+
require 'active_record'
|
6
|
+
require "action_view"
|
7
|
+
require "action_controller"
|
8
|
+
require "routes"
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
|
+
require 'easycrumbs'
|
13
|
+
|
14
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
15
|
+
ActiveRecord::Base.configurations = true
|
16
|
+
|
17
|
+
ActiveRecord::Schema.verbose = false
|
18
|
+
ActiveRecord::Schema.define(:version => 1) do
|
19
|
+
create_table :countries do |t|
|
20
|
+
t.string :name
|
21
|
+
t.string :breadcrumb
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :movies do |t|
|
25
|
+
t.string :name
|
26
|
+
t.integer :country_id
|
27
|
+
t.string :breadcrumb
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table :actors do |t|
|
31
|
+
t.string :first_name
|
32
|
+
t.string :last_name
|
33
|
+
t.integer :movie_id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# =========== Rails Classes and Objects ===========
|
39
|
+
|
40
|
+
include EasyCrumbs
|
41
|
+
|
42
|
+
class Country < ActiveRecord::Base
|
43
|
+
has_many :movies
|
44
|
+
end
|
45
|
+
|
46
|
+
class Movie < ActiveRecord::Base
|
47
|
+
has_many :actors
|
48
|
+
belongs_to :country
|
49
|
+
end
|
50
|
+
|
51
|
+
class Actor < ActiveRecord::Base
|
52
|
+
belongs_to :movie
|
53
|
+
|
54
|
+
def breadcrumb
|
55
|
+
"#{first_name} #{last_name}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ApplicationController < ActionController::Base
|
60
|
+
end
|
61
|
+
|
62
|
+
class CountriesController < ApplicationController
|
63
|
+
def breadcrumb
|
64
|
+
"Countries list"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class MoviesController < ApplicationController
|
69
|
+
end
|
70
|
+
|
71
|
+
class ActorsController < ApplicationController
|
72
|
+
end
|
data/test/routes.rb
ADDED
@@ -0,0 +1,337 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestEasycrumbs < Test::Unit::TestCase
|
4
|
+
context "EasyCrumbs tests" do
|
5
|
+
setup do
|
6
|
+
@usa = Country.create(:name => "USA", :breadcrumb => "United States of America")
|
7
|
+
@titanic = @usa.movies.create(:name => "Titanic", :breadcrumb => "Titanic")
|
8
|
+
@leo = @titanic.actors.create(:first_name => "Leonardo", :last_name => "Di Caprio")
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Models testing" do
|
12
|
+
should "Leo play in Titanic" do
|
13
|
+
assert_equal(@titanic, @leo.movie)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "Titanic be produced in Usa" do
|
17
|
+
assert_equal(@usa, @titanic.country)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Breadcrumb model" do
|
22
|
+
context "set object" do
|
23
|
+
should "model object be ok" do
|
24
|
+
assert_equal(@usa, Breadcrumb.new(@usa).object)
|
25
|
+
end
|
26
|
+
|
27
|
+
should "controller object be ok" do
|
28
|
+
@controller = MoviesController.new
|
29
|
+
assert_equal(@controller, Breadcrumb.new(@controller).object)
|
30
|
+
end
|
31
|
+
|
32
|
+
should "raise exception for String object" do
|
33
|
+
assert_raise(InvalidObject) { Breadcrumb.new("Some string") }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "set name" do
|
38
|
+
context "for model" do
|
39
|
+
should "return breadcrumb column by default" do
|
40
|
+
assert_equal("United States of America", Breadcrumb.new(@usa).name)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "return name column if someone set it" do
|
44
|
+
assert_equal(@titanic.name, Breadcrumb.new(@titanic, :name_column => "name").name)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "return specyfic name using breadcrumb method" do
|
48
|
+
assert_equal("Leonardo Di Caprio", Breadcrumb.new(@leo).name)
|
49
|
+
end
|
50
|
+
|
51
|
+
should "raise exception if can not find name" do
|
52
|
+
assert_raise(NoName) { Breadcrumb.new(@leo, :name_column => "wrong_column")}
|
53
|
+
end
|
54
|
+
|
55
|
+
should "return model name if column return nil" do
|
56
|
+
assert_equal("Movie", Breadcrumb.new(Movie.new).name)
|
57
|
+
end
|
58
|
+
|
59
|
+
should "return model name from i18n if column return nil" do
|
60
|
+
I18n.expects(:t).with("breadcrumbs.models.movie").returns("Das film")
|
61
|
+
assert_equal("Das film", Breadcrumb.new(Movie.new, :i18n => true).name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "for controller" do
|
66
|
+
should "return controller name" do
|
67
|
+
assert_equal("Movies", Breadcrumb.new(MoviesController.new).name)
|
68
|
+
end
|
69
|
+
|
70
|
+
should "return breadcrumb method from controller" do
|
71
|
+
assert_equal("Countries list", Breadcrumb.new(CountriesController.new).name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with prefix option" do
|
76
|
+
should "return name with prefix if action is passed by parameter and it is one of defaults(new or edit)" do
|
77
|
+
assert_equal("Edit Leonardo Di Caprio", Breadcrumb.new(@leo, :action => "edit").name)
|
78
|
+
end
|
79
|
+
|
80
|
+
should "return only name if it is set to :none" do
|
81
|
+
assert_equal("Leonardo Di Caprio", Breadcrumb.new(@leo, :action => "edit", :prefix => :none).name)
|
82
|
+
end
|
83
|
+
|
84
|
+
should "return prefix and name for every action if it is set to :every" do
|
85
|
+
assert_equal("Show Leonardo Di Caprio", Breadcrumb.new(@leo, :action => "show", :prefix => :every).name)
|
86
|
+
end
|
87
|
+
|
88
|
+
should "return prefix and name if action is in prefix array" do
|
89
|
+
assert_equal("Destroy Leonardo Di Caprio", Breadcrumb.new(@leo, :action => "destroy", :prefix => [:destroy, :edit]).name)
|
90
|
+
end
|
91
|
+
|
92
|
+
should "return only name if action is not in prefix array" do
|
93
|
+
assert_equal("Leonardo Di Caprio", Breadcrumb.new(@leo, :action => "show", :prefix => [:destroy, :edit]).name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "with i18n enable" do
|
98
|
+
should "return transalted name for controller" do
|
99
|
+
I18n.expects(:t).with("breadcrumbs.controllers.movies").returns("la movies")
|
100
|
+
assert_equal("la movies", Breadcrumb.new(MoviesController.new, :i18n => true).name)
|
101
|
+
end
|
102
|
+
|
103
|
+
should "return transalted action as a prefix" do
|
104
|
+
name = "Leonardo Di Caprio"
|
105
|
+
I18n.expects(:t).with("breadcrumbs.actions.edit", {:name => name}).returns("Editzione #{name}")
|
106
|
+
assert_equal("Editzione Leonardo Di Caprio", Breadcrumb.new(@leo, :i18n => true, :action => "edit").name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "set path" do
|
111
|
+
should "return path if it exist" do
|
112
|
+
assert_equal("/countries/1/movies/1/actors/1", Breadcrumb.new(@leo, :path => {:country_id => "1", :movie_id => "1", :id => "1", :action => "show", :controller => "actors"}).path)
|
113
|
+
end
|
114
|
+
|
115
|
+
should "raise RoutingError when can not find path" do
|
116
|
+
assert_raise(EasyCrumbs::NoPath) { Breadcrumb.new(@leo, :path => {:country_id => "1", :movie_id => "1", :id => "1", :action => "no_action", :controller => "actors"}) }
|
117
|
+
end
|
118
|
+
|
119
|
+
should "retrun nil when can not find path and blank_links is on" do
|
120
|
+
assert_equal(nil, Breadcrumb.new(@leo, :path => {:country_id => "1", :movie_id => "1", :id => "1", :action => "no_action", :controller => "actors"}, :blank_links => true).path)
|
121
|
+
end
|
122
|
+
|
123
|
+
should "return root path for empty path" do
|
124
|
+
assert_equal("/", Breadcrumb.new(@leo, :path => {}).path)
|
125
|
+
end
|
126
|
+
|
127
|
+
should "return root path for nil path" do
|
128
|
+
assert_equal("/", Breadcrumb.new(@leo).path)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "Collection" do
|
135
|
+
setup do
|
136
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors/#{@leo.id}", :request_method => :put)
|
137
|
+
@collection = Collection.new("request object")
|
138
|
+
end
|
139
|
+
|
140
|
+
context "finding route" do
|
141
|
+
should "return route if it can find it" do
|
142
|
+
assert_equal(ActionController::Routing::Route, @collection.route.class)
|
143
|
+
end
|
144
|
+
|
145
|
+
should "raise error when it can not find route" do
|
146
|
+
assert_raise(EasyCrumbs::NotRecognized) do
|
147
|
+
@collection.stubs(:request_path => "/countres/1/videos/1")
|
148
|
+
@collection.find_route
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "find path" do
|
154
|
+
should "retrun path hash" do
|
155
|
+
assert_equal({:action => "update", :controller => "actors", :country_id => @usa.id.to_s, :movie_id => @titanic.id.to_s, :id => @leo.id.to_s}, @collection.path)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "selecting right segments" do
|
160
|
+
should "select only static and dynamic segments" do
|
161
|
+
results = @collection.segments
|
162
|
+
results = results.map(&:class).uniq
|
163
|
+
results.delete(ActionController::Routing::StaticSegment)
|
164
|
+
results.delete(ActionController::Routing::DynamicSegment)
|
165
|
+
assert_equal(true, results.empty?)
|
166
|
+
end
|
167
|
+
|
168
|
+
should "return proper segments for member action" do
|
169
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors/#{@leo.id}/edit", :request_method => :get)
|
170
|
+
collection = Collection.new("request object")
|
171
|
+
assert_equal(6, collection.segments.size)
|
172
|
+
end
|
173
|
+
|
174
|
+
should "return proper segments for new action" do
|
175
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors/new", :request_method => :get)
|
176
|
+
collection = Collection.new("request object")
|
177
|
+
assert_equal(6, collection.segments.size)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "pick_controller" do
|
182
|
+
should "return controller object" do
|
183
|
+
assert_equal(MoviesController, @collection.pick_controller(ActionController::Routing::StaticSegment.new("movies")).class)
|
184
|
+
end
|
185
|
+
|
186
|
+
should "return controller object if action is new" do
|
187
|
+
assert_equal(ActorsController, @collection.pick_controller(ActionController::Routing::StaticSegment.new("new")).class)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "last controller" do
|
192
|
+
should "return last controller object if last segment is dynamic" do
|
193
|
+
assert_equal("actors", @collection.last_controller_segment.value)
|
194
|
+
end
|
195
|
+
|
196
|
+
should "return last controller object if last segment is static" do
|
197
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors", :request_method => :get)
|
198
|
+
collection = Collection.new("request object")
|
199
|
+
assert_equal("actors", collection.last_controller_segment.value)
|
200
|
+
end
|
201
|
+
|
202
|
+
should "return last controller object if last segment is new action" do
|
203
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors/new", :request_method => :get)
|
204
|
+
collection = Collection.new("request object")
|
205
|
+
assert_equal("actors", collection.last_controller_segment.value)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "pick_model" do
|
210
|
+
should "return model object when key has model name" do
|
211
|
+
segment = ActionController::Routing::DynamicSegment.new(:movie_id)
|
212
|
+
assert_equal(@titanic, @collection.pick_model(segment))
|
213
|
+
end
|
214
|
+
|
215
|
+
should "return model object when key has not model name"do
|
216
|
+
segment = ActionController::Routing::DynamicSegment.new(:id)
|
217
|
+
assert_equal(@leo, @collection.pick_model(segment))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "objects" do
|
222
|
+
should "change segments into objects" do
|
223
|
+
assert_equal([CountriesController, Country, MoviesController, Movie, ActorsController, Actor], @collection.objects.map(&:class))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "path_for_model" do
|
228
|
+
should "return id and current action for last object" do
|
229
|
+
segment = ActionController::Routing::DynamicSegment.new(:id)
|
230
|
+
assert_equal({:action => 'update', :id => @leo.id.to_s}, @collection.path_for_model(segment))
|
231
|
+
end
|
232
|
+
|
233
|
+
should "return show action and object id for not last object" do
|
234
|
+
segment = ActionController::Routing::DynamicSegment.new(:movie_id)
|
235
|
+
assert_equal({:action => 'show', :movie_id => @titanic.id.to_s}, @collection.path_for_model(segment))
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "path_for_controller" do
|
240
|
+
should "return index action and controller name" do
|
241
|
+
segment = ActionController::Routing::StaticSegment.new("movies")
|
242
|
+
assert_equal({:action => 'index', :controller => 'movies'}, @collection.path_for_controller(segment))
|
243
|
+
end
|
244
|
+
|
245
|
+
should "return new action and controller name for new action segment" do
|
246
|
+
segment = ActionController::Routing::StaticSegment.new("new")
|
247
|
+
assert_equal({:action => 'new', :controller => 'actors'}, @collection.path_for_controller(segment))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "repaired_model_path" do
|
252
|
+
should "return repaired path if model is connected with controller" do
|
253
|
+
path = {:action => "show", :controller => "movies", :movie_id => 3}
|
254
|
+
assert_equal({:action => "show", :controller => "movies", :id => 3}, @collection.repaired_model_path(path))
|
255
|
+
end
|
256
|
+
|
257
|
+
should "return same path if model is not connected with controller" do
|
258
|
+
path = {:action => "show", :controller => "actors", :movie_id => 3}
|
259
|
+
assert_equal(path, @collection.repaired_model_path(path))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context "make_pathes" do
|
264
|
+
should "return patches array for objects" do
|
265
|
+
assert_equal([
|
266
|
+
{:action => 'index', :controller => 'countries'},
|
267
|
+
{:action => 'show', :controller => 'countries', :id => @usa.id.to_s},
|
268
|
+
{:action => 'index', :controller => 'movies', :country_id => @usa.id.to_s},
|
269
|
+
{:action => 'show', :controller => 'movies', :country_id => @usa.id.to_s, :id => @titanic.id.to_s},
|
270
|
+
{:action => 'index', :controller => 'actors', :country_id => @usa.id.to_s, :movie_id => @titanic.id.to_s},
|
271
|
+
{:action => 'update', :controller => 'actors', :country_id => @usa.id.to_s, :movie_id => @titanic.id.to_s, :id => @leo.id.to_s}
|
272
|
+
], @collection.make_pathes)
|
273
|
+
end
|
274
|
+
|
275
|
+
should "return patches array for objects for new action" do
|
276
|
+
Collection.any_instance.stubs(:request_path => "/countries/#{@usa.id}/movies/#{@titanic.id}/actors/new", :request_method => :get)
|
277
|
+
collection = Collection.new("request_object")
|
278
|
+
assert_equal([
|
279
|
+
{:action => 'index', :controller => 'countries'},
|
280
|
+
{:action => 'show', :controller => 'countries', :id => @usa.id.to_s},
|
281
|
+
{:action => 'index', :controller => 'movies', :country_id => @usa.id.to_s},
|
282
|
+
{:action => 'show', :controller => 'movies', :country_id => @usa.id.to_s, :id => @titanic.id.to_s},
|
283
|
+
{:action => 'index', :controller => 'actors', :country_id => @usa.id.to_s, :movie_id => @titanic.id.to_s},
|
284
|
+
{:action => 'new', :controller => 'actors', :country_id => @usa.id.to_s, :movie_id => @titanic.id.to_s}
|
285
|
+
], collection.make_pathes)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "make_breadcrumbs" do
|
290
|
+
setup do
|
291
|
+
@results = @collection.make_breadcrumbs({:prefix => :every})
|
292
|
+
end
|
293
|
+
|
294
|
+
should "return array of breadcrumbs objects" do
|
295
|
+
assert_equal(@collection.objects.size + 1, @results.size)
|
296
|
+
results = @results.map(&:class).uniq
|
297
|
+
assert_equal(1, results.size)
|
298
|
+
assert_equal(EasyCrumbs::Breadcrumb, results.first)
|
299
|
+
end
|
300
|
+
|
301
|
+
should "last breadcrumb have name with action prefix" do
|
302
|
+
assert_equal("Update Leonardo Di Caprio", @results.last.name)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "render" do
|
307
|
+
setup do
|
308
|
+
@results = [ "<a href=\"/\">Application</a>",
|
309
|
+
"<a href=\"/countries\">Countries list</a>",
|
310
|
+
"<a href=\"/countries/#{@usa.id}\">United States of America</a>",
|
311
|
+
"<a href=\"/countries/#{@usa.id}/movies\">Movies</a>",
|
312
|
+
"<a href=\"/countries/#{@usa.id}/movies/#{@titanic.id}\">Titanic</a>",
|
313
|
+
"<a href=\"/countries/#{@usa.id}/movies/#{@titanic.id}/actors\">Actors</a>",
|
314
|
+
"<a href=\"/countries/#{@usa.id}/movies/#{@titanic.id}/actors/#{@leo.id}\">Leonardo Di Caprio</a>"]
|
315
|
+
end
|
316
|
+
|
317
|
+
context "separator" do
|
318
|
+
should "render all breadcrumbs with links and standard separator" do
|
319
|
+
assert_equal(@results.join(" > "), @collection.render)
|
320
|
+
end
|
321
|
+
|
322
|
+
should "render custom separator" do
|
323
|
+
assert_equal(@results.join(" | "), @collection.render(:separator => " | "))
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context "last item not link" do
|
328
|
+
should "last item not be link when last_link option is false" do
|
329
|
+
@results = @results[0..-2] << "Leonardo Di Caprio"
|
330
|
+
assert_equal(@results.join(" > "), @collection.render(:last_link => false))
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easycrumbs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Stanis\xC5\x82aw Kolarzowski"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-08 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thoughtbot-shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Easy breadcrumbs for your website
|
36
|
+
email: stanislaw.kolarzowski@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- .document
|
46
|
+
- .gitignore
|
47
|
+
- LICENSE
|
48
|
+
- README.rdoc
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- easycrumbs.gemspec
|
52
|
+
- lib/easycrumbs.rb
|
53
|
+
- lib/easycrumbs/breadcrumb.rb
|
54
|
+
- lib/easycrumbs/collection.rb
|
55
|
+
- lib/easycrumbs/errors.rb
|
56
|
+
- lib/easycrumbs/view_helpers.rb
|
57
|
+
- test/helper.rb
|
58
|
+
- test/routes.rb
|
59
|
+
- test/test_easycrumbs.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/staszek/easycrumbs
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --charset=UTF-8
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
requirements: []
|
88
|
+
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.3.7
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Easy breadcrumbs
|
94
|
+
test_files:
|
95
|
+
- test/helper.rb
|
96
|
+
- test/routes.rb
|
97
|
+
- test/test_easycrumbs.rb
|