loaf 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -8
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +23 -2
  5. data/Appraisals +18 -0
  6. data/CHANGELOG.md +12 -0
  7. data/Gemfile +16 -1
  8. data/README.md +69 -52
  9. data/Rakefile +5 -3
  10. data/bin/setup +7 -0
  11. data/gemfiles/rails3.2.gemfile +21 -0
  12. data/gemfiles/rails4.0.gemfile +22 -0
  13. data/gemfiles/rails4.1.gemfile +21 -0
  14. data/gemfiles/rails4.2.gemfile +21 -0
  15. data/lib/loaf.rb +24 -1
  16. data/lib/loaf/configuration.rb +15 -22
  17. data/lib/loaf/controller_extensions.rb +25 -12
  18. data/lib/loaf/crumb.rb +13 -1
  19. data/lib/loaf/crumb_formatter.rb +14 -13
  20. data/lib/loaf/options_validator.rb +10 -2
  21. data/lib/loaf/translation.rb +15 -8
  22. data/lib/loaf/version.rb +1 -8
  23. data/lib/loaf/view_extensions.rb +42 -15
  24. data/loaf.gemspec +10 -17
  25. data/spec/integration/configuration_spec.rb +7 -8
  26. data/spec/integration/crumbs_routing_spec.rb +31 -28
  27. data/spec/rails_app/app/controllers/application_controller.rb +0 -2
  28. data/spec/rails_app/app/controllers/posts_controller.rb +13 -2
  29. data/spec/rails_app/app/views/layouts/_breadcrumbs.html.erb +2 -0
  30. data/spec/rails_app/app/views/posts/index.html.erb +0 -2
  31. data/spec/rails_app/app/views/posts/new.html.erb +3 -0
  32. data/spec/rails_app/app/views/posts/show.html.erb +1 -0
  33. data/spec/rails_app/config/environments/development.rb +2 -0
  34. data/spec/rails_app/config/environments/production.rb +2 -0
  35. data/spec/rails_app/config/environments/test.rb +2 -3
  36. data/spec/rails_app/config/routes.rb +0 -2
  37. data/spec/rails_app/{app/mailers → log}/.gitkeep +0 -0
  38. data/spec/spec_helper.rb +42 -4
  39. data/spec/support/dummy_view.rb +7 -0
  40. data/spec/unit/controller_extensions_spec.rb +63 -0
  41. data/spec/unit/crumb_formatter_spec.rb +38 -0
  42. data/spec/unit/options_validator_spec.rb +17 -0
  43. data/spec/unit/translation_spec.rb +22 -0
  44. data/spec/unit/view_extensions/breadcrumb_spec.rb +24 -0
  45. data/spec/unit/view_extensions/breadcrumbs_spec.rb +128 -0
  46. data/spec/unit/view_extensions/has_breadcrumbs_spec.rb +12 -0
  47. data/tasks/console.rake +10 -0
  48. data/tasks/coverage.rake +11 -0
  49. data/tasks/spec.rake +29 -0
  50. metadata +50 -100
  51. data/.rvmrc +0 -28
  52. data/Gemfile.lock +0 -132
  53. data/spec/integration/nested_crumbs_spec.rb +0 -5
  54. data/spec/loaf/controller_extensions_spec.rb +0 -55
  55. data/spec/loaf/crumb_formatter_spec.rb +0 -33
  56. data/spec/loaf/options_validator_spec.rb +0 -29
  57. data/spec/loaf/translation_spec.rb +0 -23
  58. data/spec/loaf/view_extensions_spec.rb +0 -114
  59. data/spec/loaf_spec.rb +0 -5
  60. data/spec/rails_app/app/assets/images/rails.png +0 -0
  61. data/spec/rails_app/app/assets/javascripts/application.js +0 -9
  62. data/spec/rails_app/app/assets/stylesheets/application.css +0 -7
  63. data/spec/rails_app/app/helpers/application_helper.rb +0 -2
  64. data/spec/rails_app/app/models/.gitkeep +0 -0
@@ -1,5 +1,3 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
-
4
- add_breadcrumb 'Home', 'root_path'
5
3
  end
@@ -1,12 +1,23 @@
1
+ class Post < Struct.new(:id); end
2
+
1
3
  class PostsController < ApplicationController
2
4
 
3
- add_breadcrumb 'All Posts', :posts_path
5
+ breadcrumb 'Home', :root_path, only: :index
4
6
 
5
7
  def index
8
+ breadcrumb 'All Posts', posts_path
9
+ end
10
+
11
+ def show
12
+ @post = ::Post.new(1)
6
13
  end
7
14
 
8
15
  def new
9
- add_breadcrumb 'New Post', 'new_post_path'
16
+ breadcrumb 'New Post', new_post_path
10
17
  end
11
18
 
19
+ def create
20
+ breadcrumb 'New Post', new_post_path, force: true
21
+ render action: :new
22
+ end
12
23
  end
@@ -1,3 +1,4 @@
1
+ <% if breadcrumbs? %>
1
2
  <ul id='breadcrumbs'>
2
3
  <% breadcrumbs do |name, url, styles| %>
3
4
  <li class="<%= styles %>">
@@ -6,3 +7,4 @@
6
7
  </li>
7
8
  <% end %>
8
9
  </ul>
10
+ <% end %>
@@ -1,3 +1 @@
1
- <% add_breadcrumb 'View Post', :posts_path %>
2
-
3
1
  <h1>Posts:index</h1>
@@ -1 +1,4 @@
1
1
  <h1>Posts:new</h1>
2
+ <%= form_tag posts_path, :method => "POST" do %>
3
+ <% submit_tag "Create"%>
4
+ <% end %>
@@ -0,0 +1 @@
1
+ <% breadcrumb 'Show Post in view', post_path(@post.id) %>
@@ -27,4 +27,6 @@ RailsApp::Application.configure do
27
27
 
28
28
  # Expands the lines which load the assets
29
29
  config.assets.debug = true
30
+
31
+ config.eager_load = false
30
32
  end
@@ -57,4 +57,6 @@ RailsApp::Application.configure do
57
57
 
58
58
  # Send deprecation notices to registered listeners
59
59
  config.active_support.deprecation = :notify
60
+
61
+ config.eager_load = true
60
62
  end
@@ -11,9 +11,6 @@ RailsApp::Application.configure do
11
11
  config.serve_static_assets = true
12
12
  config.static_cache_control = "public, max-age=3600"
13
13
 
14
- # Log error messages when you accidentally call methods on nil
15
- config.whiny_nils = true
16
-
17
14
  # Show full error reports and disable caching
18
15
  config.consider_all_requests_local = true
19
16
  config.action_controller.perform_caching = false
@@ -39,4 +36,6 @@ RailsApp::Application.configure do
39
36
 
40
37
  # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
41
38
  config.assets.allow_debugging = true
39
+
40
+ config.eager_load = false
42
41
  end
@@ -1,9 +1,7 @@
1
1
  RailsApp::Application.routes.draw do
2
-
3
2
  root :to => 'home#index'
4
3
 
5
4
  resources :posts do
6
5
  resources :comments
7
6
  end
8
-
9
7
  end
@@ -1,15 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ if RUBY_VERSION > '1.9' and (ENV['COVERAGE'] || ENV['TRAVIS'])
4
+ require 'simplecov'
5
+ require 'coveralls'
6
+
7
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ Coveralls::SimpleCov::Formatter
10
+ ]
11
+
12
+ SimpleCov.start do
13
+ command_name 'spec'
14
+ add_filter 'spec'
15
+ end
16
+ end
17
+
1
18
  # Configure Rails Environment
2
19
  ENV["RAILS_ENV"] = "test"
3
20
 
4
21
  require File.expand_path("../rails_app/config/environment.rb", __FILE__)
5
22
  require 'rspec/rails'
6
23
 
7
- Rails.backtrace_cleaner.remove_silencers!
24
+ require 'loaf'
8
25
 
9
- # Load support files
10
26
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
27
 
12
28
  RSpec.configure do |config|
13
- config.use_transactional_fixtures = true
14
- config.mock_with :rspec
29
+ config.expect_with :rspec do |expectations|
30
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
31
+ end
32
+
33
+ config.mock_with :rspec do |mocks|
34
+ mocks.verify_partial_doubles = true
35
+ end
36
+
37
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
38
+ config.disable_monkey_patching!
39
+
40
+ # This setting enables warnings. It's recommended, but in some cases may
41
+ # be too noisy due to issues in dependencies.
42
+ config.warnings = true
43
+
44
+ if config.files_to_run.one?
45
+ config.default_formatter = 'doc'
46
+ end
47
+
48
+ config.profile_examples = 2
49
+
50
+ config.order = :random
51
+
52
+ Kernel.srand config.seed
15
53
  end
@@ -0,0 +1,7 @@
1
+ class DummyView < ActionView::Base
2
+ include Loaf::ViewExtensions
3
+ attr_reader :_breadcrumbs
4
+
5
+ def url_for(*); end
6
+ def current_page?(*); end
7
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ class DummyController < ActionController::Base
6
+ def self.before_filter(options, &block)
7
+ yield self.new
8
+ end
9
+ include Loaf::ControllerExtensions
10
+ end
11
+
12
+ RSpec.describe Loaf::ControllerExtensions do
13
+
14
+ context 'when classes extend controller_extensions' do
15
+ it { expect(DummyController).to respond_to(:add_breadcrumb) }
16
+ it { expect(DummyController).to respond_to(:breadcrumb) }
17
+ it { expect(DummyController.new).to respond_to(:add_breadcrumb) }
18
+ it { expect(DummyController.new).to respond_to(:breadcrumb) }
19
+ it { expect(DummyController.new).to respond_to(:add_breadcrumbs) }
20
+ it { expect(DummyController.new).to respond_to(:clear_breadcrumbs) }
21
+ end
22
+
23
+ context 'class methods' do
24
+ it 'invokes before_filter' do
25
+ allow(DummyController).to receive(:before_filter)
26
+ DummyController.breadcrumb('name', 'url_path')
27
+ expect(DummyController).to have_received(:before_filter)
28
+ end
29
+
30
+ it 'delegates to instance' do
31
+ name = 'List objects'
32
+ url = :object_path
33
+ options = {force: true}
34
+ instance = double(:controller_instance).as_null_object
35
+
36
+ allow(DummyController).to receive(:new).and_return(instance)
37
+ DummyController.breadcrumb(name, url, options)
38
+ expect(instance).to have_received(:breadcrumb).with(name, url, options)
39
+ end
40
+ end
41
+
42
+ context 'instance methods' do
43
+ it 'instantiates breadcrumbs container' do
44
+ name = 'List objects'
45
+ url = :object_path
46
+ instance = DummyController.new
47
+
48
+ allow(Loaf::Crumb).to receive(:new)
49
+ instance.breadcrumb(name, url)
50
+ expect(Loaf::Crumb).to have_received(:new).with(name, url, {})
51
+ end
52
+
53
+ it 'adds breadcrumb to collection' do
54
+ name = 'List objects'
55
+ url = :object_path
56
+ instance = DummyController.new
57
+
58
+ expect {
59
+ instance.breadcrumb(name, url)
60
+ }.to change { instance._breadcrumbs.size }.by(1)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Loaf::CrumbFormatter, '.format_name' do
6
+ let(:formatter) {
7
+ Class.new do
8
+ extend Loaf::CrumbFormatter
9
+
10
+ def self.truncate(name, options)
11
+ name
12
+ end
13
+ end
14
+ }
15
+
16
+ it 'returns name error if breadcrumb name is nil' do
17
+ expect(formatter.format_name('')).to eql('')
18
+ end
19
+
20
+ it "doesn't capitalize by default" do
21
+ name = 'some random name'
22
+ formatted = formatter.format_name(name)
23
+ expect(formatted).to eql(name)
24
+ end
25
+
26
+ it 'capitalizes crumb name with capitalize option' do
27
+ name = 'some random name'
28
+ formatted = formatter.format_name(name, capitalize: true)
29
+ expect(formatted).to eql('Some random name')
30
+ end
31
+
32
+ it 'shortens crumb to provided length' do
33
+ name = 'very long name that is more that 30 characters long'
34
+ allow(formatter).to receive(:truncate).with(name, length: 30).
35
+ and_return(name[0..30])
36
+ expect(formatter.format_name(name, crumb_length: 30)).to eql(name[0..30])
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Loaf::OptionsValidator, '.valid?' do
6
+ let(:klass) { Class.extend Loaf::OptionsValidator }
7
+
8
+ it 'validates succesfully known option' do
9
+ expect(klass.valid?(crumb_length: 10)).to eq(true)
10
+ end
11
+
12
+ it 'validates unknown option with an error' do
13
+ expect {
14
+ klass.valid?(invalid_param: true)
15
+ }.to raise_error(Loaf::InvalidOptions)
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Loaf::Translation do
6
+
7
+ before { I18n.backend = I18n::Backend::Simple.new }
8
+
9
+ it 'translates breadcrumb title' do
10
+ I18n.backend.store_translations 'en', breadcrumbs: { home: 'Home'}
11
+ expect(described_class.breadcrumb_title('breadcrumbs.home')).to eql('Home')
12
+ end
13
+
14
+ it 'translates breadcrumb name with default scope' do
15
+ I18n.backend.store_translations 'en', breadcrumbs: {home: 'Home'}
16
+ expect(described_class.breadcrumb_title('home')).to eql('Home')
17
+ end
18
+
19
+ it 'translates breadcrumb name using default option' do
20
+ expect(described_class.breadcrumb_title('home', default: 'breadcrumb default name')).to eql('breadcrumb default name')
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Loaf::ViewExtensions, '.breadcrumb' do
6
+
7
+ it { expect(DummyView.new).to respond_to(:add_breadcrumb) }
8
+
9
+ it 'creates crumb instance' do
10
+ instance = DummyView.new
11
+ name = 'Home'
12
+ url = :home_path
13
+ allow(Loaf::Crumb).to receive(:new).with(name, url, {})
14
+ instance.breadcrumb name, url
15
+ expect(Loaf::Crumb).to have_received(:new).with(name, url, {})
16
+ end
17
+
18
+ it 'adds crumb to breadcrumbs storage' do
19
+ instance = DummyView.new
20
+ expect {
21
+ instance.breadcrumb 'Home', :home_path
22
+ }.to change { instance._breadcrumbs.size }.by(1)
23
+ end
24
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Loaf::ViewExtensions, '.breadcrumbs' do
6
+ it "yields to block all breadcrumbs" do
7
+ instance = DummyView.new
8
+ instance.breadcrumb('home', :home_path)
9
+ instance.breadcrumb('posts', :posts_path)
10
+
11
+ yielded = []
12
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
13
+ expect {
14
+ instance.breadcrumbs(&block)
15
+ }.to change { yielded.size }.from(0).to(2)
16
+ end
17
+
18
+ it "resolves breadcrumb paths" do
19
+ instance = DummyView.new
20
+ instance.breadcrumb('home', :home_path)
21
+ instance.breadcrumb('posts', :posts_path)
22
+
23
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
24
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
25
+ yielded = []
26
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
27
+ instance.breadcrumbs(&block)
28
+ expect(yielded).to eq([
29
+ ['home', '/home', ''],
30
+ ['posts', '/posts', '']
31
+ ])
32
+ end
33
+
34
+ it "checks current path and provides styles" do
35
+ instance = DummyView.new
36
+ instance.breadcrumb('home', :home_path)
37
+ instance.breadcrumb('posts', :posts_path)
38
+
39
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
40
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
41
+ allow(instance).to receive(:current_page?).with('/home').and_return(false)
42
+ allow(instance).to receive(:current_page?).with('/posts').and_return(true)
43
+
44
+ yielded = []
45
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
46
+ instance.breadcrumbs(&block)
47
+ expect(yielded).to eq([
48
+ ['home', '/home', ''],
49
+ ['posts', '/posts', 'selected']
50
+ ])
51
+ end
52
+
53
+ it "allows to force current path" do
54
+ instance = DummyView.new
55
+ instance.breadcrumb('home', :home_path)
56
+ instance.breadcrumb('posts', :posts_path, force: true)
57
+
58
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
59
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
60
+ allow(instance).to receive(:current_page?).and_return(false)
61
+
62
+ yielded = []
63
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
64
+ instance.breadcrumbs(&block)
65
+ expect(yielded).to eq([
66
+ ['home', '/home', ''],
67
+ ['posts', '/posts', 'selected']
68
+ ])
69
+ end
70
+
71
+ it "returns enumerator without block" do
72
+ instance = DummyView.new
73
+ instance.breadcrumb('home', :home_path)
74
+ instance.breadcrumb('posts', :posts_path)
75
+
76
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
77
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
78
+
79
+ result = instance.breadcrumbs
80
+ expect(result).to be_a(Enumerable)
81
+ expect(result.take(2)).to eq([
82
+ ['home', '/home', ''],
83
+ ['posts', '/posts', '']
84
+ ])
85
+ end
86
+
87
+ it 'validates passed options' do
88
+ instance = DummyView.new
89
+ block = lambda { |name, url, styles| }
90
+ expect {
91
+ instance.breadcrumbs(unknown: true, &block)
92
+ }.to raise_error(Loaf::InvalidOptions)
93
+ end
94
+
95
+ it 'uses global configuration for crumb formatting' do
96
+ allow(Loaf.configuration).to receive(:crumb_length).and_return(10)
97
+ instance = DummyView.new
98
+ instance.breadcrumb('home-sweet-home', :home_path)
99
+ instance.breadcrumb('posts-for-everybody', :posts_path)
100
+
101
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
102
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
103
+ yielded = []
104
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
105
+ instance.breadcrumbs(&block)
106
+ expect(yielded).to eq([
107
+ ['home-sw...', '/home', ''],
108
+ ['posts-f...', '/posts', '']
109
+ ])
110
+ end
111
+
112
+ it "allows to overwrite global configuration" do
113
+ allow(Loaf.configuration).to receive(:crumb_length).and_return(10)
114
+ instance = DummyView.new
115
+ instance.breadcrumb('home-sweet-home', :home_path)
116
+ instance.breadcrumb('posts-for-everybody', :posts_path)
117
+
118
+ allow(instance).to receive(:url_for).with(:home_path).and_return('/home')
119
+ allow(instance).to receive(:url_for).with(:posts_path).and_return('/posts')
120
+ yielded = []
121
+ block = lambda { |name, url, styles| yielded << [name, url, styles]}
122
+ instance.breadcrumbs(crumb_length: 15, &block)
123
+ expect(yielded).to eq([
124
+ ['home-sweet-home', '/home', ''],
125
+ ['posts-for-ev...', '/posts', '']
126
+ ])
127
+ end
128
+ end