breadcrumby 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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/LICENSE +21 -0
  4. data/README.md +221 -0
  5. data/lib/breadcrumby.rb +11 -0
  6. data/lib/breadcrumby/engine.rb +15 -0
  7. data/lib/breadcrumby/helpers/view_helper.rb +9 -0
  8. data/lib/breadcrumby/models/extension.rb +74 -0
  9. data/lib/breadcrumby/models/home.rb +23 -0
  10. data/lib/breadcrumby/models/viewer.rb +154 -0
  11. data/lib/breadcrumby/version.rb +5 -0
  12. data/spec/factories/course.rb +9 -0
  13. data/spec/factories/grade.rb +10 -0
  14. data/spec/factories/level.rb +9 -0
  15. data/spec/factories/school.rb +7 -0
  16. data/spec/factories/unit.rb +9 -0
  17. data/spec/lib/breadcrumby/helpers/breadcrumby_spec.rb +22 -0
  18. data/spec/lib/breadcrumby/models/extension/breadcrumby_options_spec.rb +89 -0
  19. data/spec/lib/breadcrumby/models/extension/breadcrumby_spec.rb +126 -0
  20. data/spec/lib/breadcrumby/models/home/name_spec.rb +25 -0
  21. data/spec/lib/breadcrumby/models/home/show_path_spec.rb +25 -0
  22. data/spec/lib/breadcrumby/models/viewer/breadcrumb_spec.rb +55 -0
  23. data/spec/lib/breadcrumby/models/viewer/breadcrumbs_spec.rb +25 -0
  24. data/spec/lib/breadcrumby/models/viewer/current_object_spec.rb +68 -0
  25. data/spec/lib/breadcrumby/models/viewer/i18n_action_name_spec.rb +28 -0
  26. data/spec/lib/breadcrumby/models/viewer/i18n_name_spec.rb +53 -0
  27. data/spec/lib/breadcrumby/models/viewer/item_options_spec.rb +17 -0
  28. data/spec/lib/breadcrumby/models/viewer/item_spec.rb +30 -0
  29. data/spec/lib/breadcrumby/models/viewer/link_action_spec.rb +23 -0
  30. data/spec/lib/breadcrumby/models/viewer/link_options_spec.rb +49 -0
  31. data/spec/lib/breadcrumby/models/viewer/link_spec.rb +40 -0
  32. data/spec/lib/breadcrumby/models/viewer/link_tag_name_options_spec.rb +13 -0
  33. data/spec/lib/breadcrumby/models/viewer/link_tag_name_spec.rb +35 -0
  34. data/spec/lib/breadcrumby/models/viewer/list_options_spec.rb +17 -0
  35. data/spec/lib/breadcrumby/models/viewer/meta_spec.rb +14 -0
  36. data/spec/lib/breadcrumby/models/viewer/object_extra_spec.rb +80 -0
  37. data/spec/lib/breadcrumby/models/viewer/scope_spec.rb +35 -0
  38. data/spec/rails_helper.rb +11 -0
  39. data/spec/support/common.rb +22 -0
  40. data/spec/support/database_cleaner.rb +21 -0
  41. data/spec/support/db/migrate/course.rb +11 -0
  42. data/spec/support/db/migrate/grade.rb +12 -0
  43. data/spec/support/db/migrate/level.rb +11 -0
  44. data/spec/support/db/migrate/school.rb +9 -0
  45. data/spec/support/db/migrate/unit.rb +11 -0
  46. data/spec/support/factory_girl.rb +9 -0
  47. data/spec/support/html_matchers.rb +7 -0
  48. data/spec/support/migrate.rb +9 -0
  49. data/spec/support/models/course.rb +5 -0
  50. data/spec/support/models/grade.rb +6 -0
  51. data/spec/support/models/level.rb +5 -0
  52. data/spec/support/models/school.rb +15 -0
  53. data/spec/support/models/unit.rb +13 -0
  54. data/spec/support/shoulda.rb +10 -0
  55. metadata +321 -0
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Breadcrumby
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :course do
5
+ sequence(:name) { |i| "Course #{i}" }
6
+
7
+ school
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :grade do
5
+ sequence(:name) { |i| "Grade #{i}" }
6
+
7
+ level
8
+ unit
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :level do
5
+ sequence(:name) { |i| "Level #{i}" }
6
+
7
+ course
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :school do
5
+ sequence(:name) { |i| "School #{i}" }
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :unit do
5
+ sequence(:name) { |i| "Unit #{i}" }
6
+
7
+ school
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ class DummyHelper
6
+ include Breadcrumby::ViewHelper
7
+ end
8
+
9
+ RSpec.describe DummyHelper, '.breadcrumby' do
10
+ let!(:object) { create :school, name: 'School 1' }
11
+ let!(:options) { { key: :value } }
12
+ let!(:helper) { DummyHelper.new }
13
+ let!(:viewer) { double Breadcrumby::Viewer, breadcrumb: :breadcrumb }
14
+
15
+ before do
16
+ allow(Breadcrumby::Viewer).to receive(:new).with(object, options, helper) { viewer }
17
+ end
18
+
19
+ it 'calls breadcrumb from viewer' do
20
+ expect(helper.breadcrumby(object, options)).to eq :breadcrumb
21
+ end
22
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Extension, '.breadcrumby_options' do
6
+ describe ':i18n_key' do
7
+ context 'when is not given' do
8
+ before do
9
+ stub_const 'Dummy', Class.new
10
+
11
+ Dummy.class_eval { include Breadcrumby::Extension }
12
+ Dummy.class_eval { breadcrumby }
13
+ end
14
+
15
+ it 'uses a default' do
16
+ expect(Dummy.new.breadcrumby_options[:i18n_key]).to eq 'dummy'
17
+ end
18
+ end
19
+
20
+ context 'when is given' do
21
+ before do
22
+ stub_const 'Dummy', Class.new
23
+
24
+ Dummy.class_eval { include Breadcrumby::Extension }
25
+ Dummy.class_eval { breadcrumby i18n_key: :key }
26
+ end
27
+
28
+ it 'uses the given one' do
29
+ expect(Dummy.new.breadcrumby_options[:i18n_key]).to eq :key
30
+ end
31
+ end
32
+ end
33
+
34
+ describe ':method_name' do
35
+ context 'when is not given' do
36
+ before do
37
+ stub_const 'Dummy', Class.new
38
+
39
+ Dummy.class_eval { include Breadcrumby::Extension }
40
+ Dummy.class_eval { breadcrumby }
41
+ end
42
+
43
+ it 'uses a default' do
44
+ expect(Dummy.new.breadcrumby_options[:method_name]).to eq :name
45
+ end
46
+ end
47
+
48
+ context 'when is given' do
49
+ before do
50
+ stub_const 'Dummy', Class.new
51
+
52
+ Dummy.class_eval { include Breadcrumby::Extension }
53
+ Dummy.class_eval { breadcrumby method_name: :some_name }
54
+ end
55
+
56
+ it 'uses the given one' do
57
+ expect(Dummy.new.breadcrumby_options[:method_name]).to eq :some_name
58
+ end
59
+ end
60
+ end
61
+
62
+ describe ':actions' do
63
+ context 'when is not given' do
64
+ before do
65
+ stub_const 'Dummy', Class.new
66
+
67
+ Dummy.class_eval { include Breadcrumby::Extension }
68
+ Dummy.class_eval { breadcrumby }
69
+ end
70
+
71
+ it 'uses a default' do
72
+ expect(Dummy.new.breadcrumby_options[:actions]).to eq({})
73
+ end
74
+ end
75
+
76
+ context 'when is given' do
77
+ before do
78
+ stub_const 'Dummy', Class.new
79
+
80
+ Dummy.class_eval { include Breadcrumby::Extension }
81
+ Dummy.class_eval { breadcrumby actions: { key: :value } }
82
+ end
83
+
84
+ it 'uses the given one' do
85
+ expect(Dummy.new.breadcrumby_options[:actions]).to eq(key: :value)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Extension, '.breadcrumby' do
6
+ context 'when object has no path option' do
7
+ let!(:school) { create :school, name: 'School 1' }
8
+
9
+ it 'returns itself' do
10
+ expect(school.breadcrumby).to eq [school]
11
+ end
12
+ end
13
+
14
+ context 'when object has an empty path option' do
15
+ let!(:school) { create :school }
16
+
17
+ before { School.class_eval { breadcrumby path: [] } }
18
+
19
+ it 'returns itself' do
20
+ expect(school.breadcrumby).to eq [school]
21
+ end
22
+ end
23
+
24
+ context 'when object has a nil path option' do
25
+ let!(:school) { create :school }
26
+
27
+ before { School.class_eval { breadcrumby path: nil } }
28
+
29
+ it 'returns itself' do
30
+ expect(school.breadcrumby).to eq [school]
31
+ end
32
+ end
33
+
34
+ context 'when object is the second level' do
35
+ let!(:course) { create :course }
36
+
37
+ context 'and has one item on path option' do
38
+ before { Course.class_eval { breadcrumby path: [:school] } }
39
+
40
+ it 'returns itself and the parents' do
41
+ expect(course.breadcrumby).to eq [course, course.school]
42
+ end
43
+ end
44
+
45
+ context 'and has just one symbol on path option' do
46
+ before { Course.class_eval { breadcrumby path: :school } }
47
+
48
+ it 'returns itself and the parents' do
49
+ expect(course.breadcrumby).to eq [course, course.school]
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'when object is on a deep level' do
55
+ let!(:school) { create :school }
56
+ let!(:unit) { create :unit, school: school }
57
+ let!(:course) { create :course, school: school }
58
+ let!(:level) { create :level, course: course }
59
+ let!(:grade) { create :grade, level: level, unit: unit }
60
+
61
+ context 'and parents just point to it parent' do
62
+ before do
63
+ Course.class_eval { breadcrumby path: :school }
64
+ Level.class_eval { breadcrumby path: :course }
65
+ Grade.class_eval { breadcrumby path: :level }
66
+ end
67
+
68
+ it 'returns itself and the parents' do
69
+ expect(grade.breadcrumby).to eq [grade, grade.level, grade.level.course, grade.level.course.school]
70
+ end
71
+ end
72
+
73
+ context 'and object points an specific path' do
74
+ context 'and last parent has no path' do
75
+ before do
76
+ # TODO: was need to declare again, site the other Course.class_eval keeped the callback on memory. Why if it is inside a before block?
77
+ Course.class_eval { breadcrumby }
78
+ Grade.class_eval { breadcrumby path: %i[course level] }
79
+ end
80
+
81
+ it 'returns itself and the parents until the last parent path' do
82
+ expect(grade.breadcrumby).to eq [grade, grade.level, grade.level.course]
83
+ end
84
+ end
85
+
86
+ context 'and last parent has path' do
87
+ before do
88
+ Course.class_eval { breadcrumby path: :school }
89
+ Grade.class_eval { breadcrumby path: %i[course level] }
90
+ end
91
+
92
+ it 'returns itself and the following parents' do
93
+ expect(grade.breadcrumby).to eq [grade, grade.level, grade.level.course, grade.level.course.school]
94
+ end
95
+ end
96
+
97
+ context 'and i want follow different paths' do
98
+ context 'until the end' do
99
+ before do
100
+ Unit.class_eval { breadcrumby path: :school }
101
+ Course.class_eval { breadcrumby path: :school }
102
+ Level.class_eval { breadcrumby path: :course }
103
+ Grade.class_eval { breadcrumby path: [[:unit], [:level]] }
104
+ end
105
+
106
+ it 'follows each one of the path groups' do
107
+ expect(grade.breadcrumby).to eq [grade, grade.level, grade.level.course, grade.level.course.school, grade.unit, grade.level.course.school]
108
+ end
109
+ end
110
+
111
+ context 'stopping on a specific point' do
112
+ before do
113
+ Unit.class_eval { breadcrumby path: :school }
114
+ Course.class_eval { breadcrumby path: :school }
115
+ Level.class_eval { breadcrumby path: :course }
116
+ Grade.class_eval { breadcrumby path: [[:unit], [nil, :course, :level]] }
117
+ end
118
+
119
+ it 'follows each one of the path groups until end of until the nil break point' do
120
+ expect(grade.breadcrumby).to eq [grade, grade.level, grade.level.course, grade.unit, grade.level.course.school]
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Home, '.name' do
6
+ subject { described_class.new view }
7
+
8
+ let!(:view) { double }
9
+
10
+ context 'when i18n key is not defined' do
11
+ it 'returns a default name' do
12
+ expect(subject.name).to eq 'Home'
13
+ end
14
+ end
15
+
16
+ context 'when i18n key is defined' do
17
+ before do
18
+ allow(I18n).to receive(:t).with('breadcrumby.home.name', default: 'Home') { 'Start' }
19
+ end
20
+
21
+ it 'returns a default name' do
22
+ expect(subject.name).to eq 'Start'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Home, '.show_path' do
6
+ context 'when route :root_path is not present' do
7
+ subject { described_class.new view }
8
+
9
+ let!(:view) { double }
10
+
11
+ it 'returns the root path' do
12
+ expect(subject.show_path).to eq '/'
13
+ end
14
+ end
15
+
16
+ context 'when route :root_path is present' do
17
+ subject { described_class.new view }
18
+
19
+ let!(:view) { double root_path: :root_path }
20
+
21
+ it 'returns the :root_path value' do
22
+ expect(subject.show_path).to eq :root_path
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Viewer, '.breadcrumb' do
6
+ context 'when object does not respond to :breadcrumby' do
7
+ subject { described_class.new object, options }
8
+
9
+ let!(:object) { {} }
10
+ let!(:options) { {} }
11
+ let!(:view) { double root_path: :root_path }
12
+
13
+ it 'returns an empty string' do
14
+ expect(subject.breadcrumb).to eq ''
15
+ end
16
+ end
17
+
18
+ context 'when object responds to :breadcrumby' do
19
+ subject { described_class.new object, options }
20
+
21
+ let!(:object) { create :school, name: 'The School' }
22
+ let!(:view) { double root_path: :root_path }
23
+ let!(:list) { [object] }
24
+
25
+ before do
26
+ allow(subject).to receive(:breadcrumbs).with(object) { [object] }
27
+ allow(subject).to receive(:object_extra).with(list.size) { ['<li class="object_extra"></li>'] }
28
+ allow(subject).to receive(:list_options) { { class: :list_options } }
29
+ end
30
+
31
+ context 'with no :actions' do
32
+ let!(:options) { {} }
33
+
34
+ before do
35
+ Unit.class_eval { breadcrumby }
36
+ end
37
+
38
+ it 'returns the default breadcrumb' do
39
+ expect(subject.breadcrumb).to have_tag(:ol, with: { class: 'list_options' }) do
40
+ with_tag(:li, with: { itemprop: 'itemListElement', itemscope: 'itemscope', itemtype: 'http://schema.org/ListItem' }) do
41
+ with_tag(:a, with: { itemprop: 'item', itemscope: 'itemscope', itemtype: 'http://schema.org/Thing', title: 'translation missing: en.breadcrumby.title', href: 'school.show.path' }) do
42
+ with_tag(:span, with: { itemprop: 'name' }) do
43
+ with_text 'The School'
44
+ end
45
+ end
46
+
47
+ with_tag(:meta, with: { content: '1', itemprop: 'position' })
48
+ end
49
+
50
+ with_tag :li, with: { class: 'object_extra' }
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Viewer, '.breadcrumbs' do
6
+ subject { described_class.new object, options, view }
7
+
8
+ let!(:object) { build :school }
9
+ let!(:options) { {} }
10
+ let!(:view) { double }
11
+ let!(:home) { double Breadcrumby::Home }
12
+
13
+ before do
14
+ Unit.class_eval { breadcrumby }
15
+
16
+ allow(Breadcrumby::Home).to receive(:new).with(view) { home }
17
+ end
18
+
19
+ it 'returns the objects including the home' do
20
+ expect(subject.breadcrumbs(object)).to eq [
21
+ Breadcrumby::Home.new(view),
22
+ object
23
+ ]
24
+ end
25
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Breadcrumby::Viewer, '.current_object' do
6
+ subject { described_class.new object, options, view }
7
+
8
+ let!(:object) { build :school }
9
+ let!(:view) { double }
10
+
11
+ context 'when object has no :actions' do
12
+ let!(:options) { {} }
13
+
14
+ before { School.class_eval { breadcrumby } }
15
+
16
+ it 'returns the self object' do
17
+ expect(subject.current_object).to eq object
18
+ end
19
+ end
20
+
21
+ context 'when object has :actions' do
22
+ before do
23
+ School.class_eval { breadcrumby actions: {} }
24
+ end
25
+
26
+ context 'but view action is not provided' do
27
+ let!(:options) { {} }
28
+
29
+ it 'returns the self object' do
30
+ expect(subject.current_object).to eq object
31
+ end
32
+ end
33
+
34
+ context 'and view action is provided' do
35
+ let!(:options) { { action: :new } }
36
+
37
+ context 'but object has not this action' do
38
+ it 'returns the self object' do
39
+ expect(subject.current_object).to eq object
40
+ end
41
+ end
42
+
43
+ context 'and object has this action' do
44
+ context 'but actions is not callable' do
45
+ before do
46
+ School.class_eval { breadcrumby actions: { new: :uncallable } }
47
+ end
48
+
49
+ it 'returns the self value' do
50
+ expect(subject.current_object).to eq :uncallable
51
+ end
52
+ end
53
+
54
+ context 'and actions is callable' do
55
+ before do
56
+ School.class_eval do
57
+ breadcrumby actions: { new: ->(view) { view } }
58
+ end
59
+ end
60
+
61
+ it 'returns the result call' do
62
+ expect(subject.current_object).to eq view
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end