waiter 0.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/waiter.rb CHANGED
@@ -1,4 +1,13 @@
1
- require "waiter/version"
2
1
  require "waiter/menu"
3
- require "waiter/menu/builder"
4
- require "waiter/menu/drawer"
2
+ require "waiter/dsl"
3
+ require "waiter/menu/drawer"
4
+
5
+ module Waiter
6
+ class Engine < ::Rails::Engine
7
+ initializer :assets do |config|
8
+ Rails.application.config.assets.precompile += %w{ waiter/menu.css }
9
+ Rails.application.config.assets.paths << root.join("app", "assets", "stylesheets")
10
+ Rails.application.config.assets.paths << root.join("app", "assets", "images")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,89 @@
1
+ require 'rspec/its'
2
+
3
+ Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
8
+ # file to always be loaded, without a need to explicitly require it in any files.
9
+ #
10
+ # Given that it is always loaded, you are encouraged to keep this file as
11
+ # light-weight as possible. Requiring heavyweight dependencies from this file
12
+ # will add to the boot time of your test suite on EVERY test run, even for an
13
+ # individual file that may not need all of that loaded. Instead, consider making
14
+ # a separate helper file that requires the additional dependencies and performs
15
+ # the additional setup, and require it from the spec files that actually need it.
16
+ #
17
+ # The `.rspec` file also contains a few flags that are not defaults but that
18
+ # users commonly want.
19
+ #
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ # rspec-expectations config goes here. You can use an alternate
23
+ # assertion/expectation library such as wrong or the stdlib/minitest
24
+ # assertions if you prefer.
25
+ config.expect_with :rspec do |expectations|
26
+ # This option will default to `true` in RSpec 4. It makes the `description`
27
+ # and `failure_message` of custom matchers include text for helper methods
28
+ # defined using `chain`, e.g.:
29
+ # be_bigger_than(2).and_smaller_than(4).description
30
+ # # => "be bigger than 2 and smaller than 4"
31
+ # ...rather than:
32
+ # # => "be bigger than 2"
33
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
34
+ end
35
+
36
+ # rspec-mocks config goes here. You can use an alternate test double
37
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
38
+ config.mock_with :rspec do |mocks|
39
+ # Prevents you from mocking or stubbing a method that does not exist on
40
+ # a real object. This is generally recommended, and will default to
41
+ # `true` in RSpec 4.
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # These two settings work together to allow you to limit a spec run
46
+ # to individual examples or groups you care about by tagging them with
47
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
48
+ # get run.
49
+ config.filter_run :focus
50
+ config.run_all_when_everything_filtered = true
51
+
52
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
53
+ # For more details, see:
54
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
55
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
56
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
57
+ config.disable_monkey_patching!
58
+
59
+ # This setting enables warnings. It's recommended, but in some cases may
60
+ # be too noisy due to issues in dependencies.
61
+ # config.warnings = true
62
+
63
+ # Many RSpec users commonly either run the entire suite or an individual
64
+ # file, and it's useful to allow more verbose output when running an
65
+ # individual spec file.
66
+ if config.files_to_run.one?
67
+ # Use the documentation formatter for detailed output,
68
+ # unless a formatter has already been configured
69
+ # (e.g. via a command-line flag).
70
+ config.default_formatter = 'doc'
71
+ end
72
+
73
+ # Print the 10 slowest examples and example groups at the
74
+ # end of the spec run, to help surface which specs are running
75
+ # particularly slow.
76
+ # config.profile_examples = 10
77
+
78
+ # Run specs in random order to surface order dependencies. If you find an
79
+ # order dependency and want to debug it, you can fix the order by providing
80
+ # the seed, which is printed after each run.
81
+ # --seed 1234
82
+ config.order = :random
83
+
84
+ # Seed global randomization in this process using the `--seed` CLI option.
85
+ # Setting this allows you to use `--seed` to deterministically reproduce
86
+ # test failures related to randomization by passing the same `--seed` value
87
+ # as the one that triggered the failure.
88
+ Kernel.srand config.seed
89
+ end
@@ -0,0 +1,34 @@
1
+ RSpec::Matchers.define :have_a_submenu do
2
+ match do |actual|
3
+ result = !actual.submenu.nil?
4
+ result &&= actual.submenu.items.size == @count unless @count.nil?
5
+ result
6
+ end
7
+
8
+ description do
9
+ desc = "have a submenu"
10
+ desc << " with #{@count} items" unless @count.nil?
11
+ desc
12
+ end
13
+
14
+ failure_message do
15
+ msg = "expected menu item to have a submenu"
16
+
17
+ unless @count.nil?
18
+ msg << " with #{@count} items but it actually has #{actual.submenu.items.size}"
19
+ end
20
+
21
+ msg
22
+ end
23
+
24
+ failure_message_when_negated do
25
+ msg = "expected menu item to not have a submenu"
26
+ end
27
+
28
+ chain :containing do |count|
29
+ @count = count
30
+ end
31
+
32
+ chain(:item) {}
33
+ chain(:items) {}
34
+ end
@@ -0,0 +1,24 @@
1
+ RSpec::Matchers.define :have_exactly do |expected|
2
+ match do |actual|
3
+ if @sections
4
+ actual.sections.size == expected
5
+ else
6
+ actual.items.size == expected
7
+ end
8
+ end
9
+
10
+ description do
11
+ "contain exactly #{expected} menu #{@sections ? 'sections' : 'items'}"
12
+ end
13
+
14
+ failure_message do
15
+ msg = "expected that the menu would contain #{expected} #{@sections ? 'sections' : 'items'}; actually contains "
16
+ msg << (@sections ? actual.sections.size : actual.items.size).to_s
17
+ msg
18
+ end
19
+
20
+ chain(:item) { @sections = false }
21
+ chain(:items) { @sections = false }
22
+ chain(:section) { @sections = true }
23
+ chain(:sections) { @sections = true }
24
+ end
@@ -0,0 +1,32 @@
1
+ RSpec::Matchers.define :have_menu_items_named do |*expected|
2
+ match do |actual|
3
+ actual.items.names & expected == expected
4
+ end
5
+
6
+ description do
7
+ if expected.one?
8
+ "contain a menu item named #{expected.first.to_s}"
9
+ else
10
+ "contain menu items named #{expected.map(&:to_s).join(', ')}"
11
+ end
12
+ end
13
+
14
+ failure_message do
15
+ msg = if expected.one?
16
+ "expected menu to contain a menu item named #{expected}"
17
+ else
18
+ "expected menu to contain a menu items named #{expected}"
19
+ end
20
+
21
+ msg << "\nDiff:"
22
+
23
+ differ = RSpec::Support::Differ.new(
24
+ :object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
25
+ :color => RSpec::Matchers.configuration.color?
26
+ )
27
+
28
+ msg << differ.diff(actual.items.names.join("\n"), expected.join("\n"))
29
+ end
30
+ end
31
+
32
+ RSpec::Matchers.alias_matcher :have_a_menu_item_named, :have_menu_items_named
@@ -0,0 +1,23 @@
1
+ require 'active_support/core_ext/hash/except'
2
+
3
+ RSpec::Matchers.define :have_options do |expected|
4
+ match do |actual|
5
+ options == expected
6
+ end
7
+
8
+ failure_message do
9
+ msg = "expected to contain options #{expected} but actually contained #{options}\nDiff:"
10
+
11
+ differ = RSpec::Support::Differ.new(
12
+ :object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
13
+ :color => RSpec::Matchers.configuration.color?
14
+ )
15
+
16
+ msg << differ.diff(options, expected)
17
+ msg
18
+ end
19
+
20
+ def options
21
+ expected.key?(:controllers) ? actual.options : actual.options.except(:controllers)
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ RSpec.shared_examples 'a menu item' do |instance_options|
2
+ let(:parent) { double(Waiter::Menu, context: nil, name: :parent, options: {}) }
3
+
4
+ before { allow_any_instance_of(Waiter::Menu::Item).to receive(:collect_controllers) }
5
+
6
+ context 'when given a block' do
7
+ subject do
8
+ described_class.new(parent, *instance_options) do
9
+ first
10
+ end
11
+ end
12
+
13
+ its(:submenu) { is_expected.to have_exactly(1).item }
14
+ its(:submenu) { is_expected.to have_a_menu_item_named(:first) }
15
+
16
+ context 'with options' do
17
+ before { allow(parent).to receive(:options).and_return(foo: :bar) }
18
+
19
+ subject do
20
+ described_class.new(parent, *instance_options) do
21
+ first foo: :baz
22
+ second
23
+ third quux: true
24
+ end
25
+ end
26
+
27
+ its([:first]) { is_expected.to have_options(foo: :baz) }
28
+ its([:second]) { is_expected.to have_options(foo: :bar) }
29
+ its([:third]) { is_expected.to have_options(foo: :bar, quux: true) }
30
+ end
31
+ end
32
+
33
+ describe '#items' do
34
+ context 'when a block is not given' do
35
+ its(:items) { is_expected.to be_empty }
36
+ end
37
+
38
+ context 'when a block is given' do
39
+ subject do
40
+ described_class.new(parent, *instance_options) do
41
+ first
42
+ second
43
+ end
44
+ end
45
+
46
+ its('items.size') { is_expected.to eq(2) }
47
+ end
48
+ end
49
+
50
+ describe '#[]' do
51
+ context 'when a block is not given' do
52
+ its([:third]) { is_expected.to be_nil }
53
+ end
54
+
55
+ context 'when a block is given' do
56
+ subject do
57
+ described_class.new(parent, *instance_options) do
58
+ first
59
+ second
60
+ end
61
+ end
62
+
63
+ its([:second]) { is_expected.to be_a(Waiter::Menu::Item) }
64
+ its([:third]) { is_expected.to be_nil }
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'waiter/menu/item_list'
3
+
4
+ RSpec.describe Waiter::Menu::ItemList do
5
+ let(:item1) { double('Item', name: :foo) }
6
+ let(:item2) { double('Item', name: :bar) }
7
+ let(:item3) { double('Item', name: :baz) }
8
+
9
+ subject { described_class.new [item1, item2, item3] }
10
+
11
+ describe '#include?' do
12
+ it { is_expected.to include item1 }
13
+ it { is_expected.to include item2 }
14
+ it { is_expected.to include item3 }
15
+
16
+ it { is_expected.to include :foo }
17
+ it { is_expected.to include :bar }
18
+ it { is_expected.to include :baz }
19
+
20
+ it { is_expected.to_not include :foobarbaz }
21
+ end
22
+
23
+ describe '#[]' do
24
+ its([0]) { is_expected.to eq item1 }
25
+ its([1]) { is_expected.to eq item2 }
26
+ its([2]) { is_expected.to eq item3 }
27
+
28
+ its([:foo]) { is_expected.to eq item1 }
29
+ its([:bar]) { is_expected.to eq item2 }
30
+ its([:baz]) { is_expected.to eq item3 }
31
+
32
+ its([:foobarbaz]) { is_expected.to be nil }
33
+ end
34
+
35
+ describe '#names' do
36
+ its(:names) { is_expected.to contain_exactly(:foo, :bar, :baz) }
37
+ end
38
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+ require 'waiter/menu/item'
3
+ require 'waiter/menu'
4
+
5
+ RSpec.describe Waiter::Menu::Item do
6
+ let(:parent) { double(Waiter::Menu, name: :parent, options: {}, top?: false) }
7
+ subject { described_class.new(parent, :item1, 'path') }
8
+
9
+ before { allow_any_instance_of(described_class).to receive(:collect_controllers) }
10
+
11
+ it { is_expected.to_not be_section }
12
+
13
+ its(:name) { is_expected.to eq :item1 }
14
+ its(:path) { is_expected.to eq 'path' }
15
+ its(:options) { is_expected.to be_empty }
16
+ its(:submenu) { is_expected.to be_nil }
17
+
18
+ it_behaves_like 'a menu item', [:item1, 'path']
19
+
20
+ context 'collecting controllers' do
21
+ before { allow_any_instance_of(described_class).to receive(:collect_controllers).and_call_original }
22
+
23
+ it 'should add its controller to the parent' do
24
+ described_class.new(parent, :item1, controller: :foo)
25
+ expect(parent.options[:controllers]).to contain_exactly('foo')
26
+ end
27
+
28
+ it 'should strip slashes from the controller name' do
29
+ described_class.new(parent, :item1, controller: '/foo')
30
+ expect(parent.options[:controllers]).to contain_exactly('foo')
31
+ end
32
+
33
+ it 'should retain controllers given in options' do
34
+ parent.options[:controllers] = ['bar']
35
+ described_class.new(parent, :item1, controller: :foo)
36
+ expect(parent.options[:controllers]).to contain_exactly('foo', 'bar')
37
+ end
38
+
39
+ it 'should not duplicate controllers' do
40
+ parent.options[:controllers] = ['foo']
41
+ described_class.new(parent, :item1, controller: :foo)
42
+ expect(parent.options[:controllers]).to contain_exactly('foo')
43
+ end
44
+ end
45
+
46
+ describe '#selected?' do
47
+ let(:parent) { double(Waiter::Menu, context: context, options: {}).as_null_object }
48
+ let(:context) { double('Context', params: { controller: @controller }) }
49
+
50
+ subject do
51
+ item = described_class.new(parent, :item1)
52
+ allow(item).to receive(:controllers).and_return(@controllers)
53
+ item
54
+ end
55
+
56
+ it 'should return true if the controller matches' do
57
+ @controller = 'foo'
58
+ @controllers = %w(foo)
59
+ expect(subject).to be_selected
60
+ end
61
+
62
+ it "should return false if the controller doesn't match" do
63
+ @controller = 'foo'
64
+ @controllers = %w(bar baz quux)
65
+ expect(subject).to_not be_selected
66
+ end
67
+
68
+ it 'should return true if the controller matches a wildcard' do
69
+ @controller = 'foo/bar/baz'
70
+ @controllers = %w(foo/*)
71
+ expect(subject).to be_selected
72
+ end
73
+
74
+ it 'should return true if a simple controller matches a wildcard' do
75
+ @controller = 'foo'
76
+ @controllers = %w(foo/*)
77
+ expect(subject).to be_selected
78
+ end
79
+
80
+ it "should return false if the wildcard doesn't match" do
81
+ @controller = 'foo'
82
+ @controllers = %w(foo/bar/*)
83
+ expect(subject).to_not be_selected
84
+ end
85
+
86
+ it 'should return false if the controller only matches part of a wildcard' do
87
+ @controller = 'foo/baz'
88
+ @controllers = %w(foo/bar/*)
89
+ expect(subject).to_not be_selected
90
+ end
91
+
92
+ context 'when a selected item is specified' do
93
+ subject { described_class.new(parent, :item1, nil, selected: :foo) }
94
+
95
+ it 'should return true if the item has the same name as the selection' do
96
+ allow(subject).to receive(:name).and_return(:foo)
97
+ expect(subject).to be_selected
98
+ end
99
+
100
+ it 'should return false if the item does not have the same name as the selection' do
101
+ expect(subject).to_not be_selected
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "#path" do
107
+ let(:menu) { Waiter::Menu.new(:test, nil) }
108
+
109
+ it 'should be nil if not specified for a top level item' do
110
+ item = described_class.new(menu, :item1, nil)
111
+ menu.add(item)
112
+
113
+ expect(item.path).to be_nil
114
+ end
115
+
116
+ it 'should not be nil for a submenu item' do
117
+ item = described_class.new(menu, :item1, nil) do
118
+ subitem1
119
+ end
120
+ menu.add(item)
121
+
122
+ expect(item.submenu[:subitem1].path).to eq(controller: '/subitem1', action: :index)
123
+ end
124
+
125
+ it 'should add an index if missing' do
126
+ item = described_class.new(menu, :item1, nil) do
127
+ subitem1 controller: :foo
128
+ end
129
+ menu.add(item)
130
+
131
+ expect(item.submenu[:subitem1].path).to eq(controller: '/foo', action: :index)
132
+ end
133
+
134
+ it 'should add a controller if missing' do
135
+ item = described_class.new(menu, :item1, nil) do
136
+ subitem1 action: :foo
137
+ end
138
+ menu.add(item)
139
+
140
+ expect(item.submenu[:subitem1].path).to eq(controller: '/subitem1', action: :foo)
141
+ end
142
+
143
+ it 'should not add anything if given a string' do
144
+ item = described_class.new(menu, :item1, nil) do
145
+ subitem1 '/foo/bar/baz'
146
+ end
147
+ menu.add(item)
148
+
149
+ expect(item.submenu[:subitem1].path).to eq '/foo/bar/baz'
150
+ end
151
+
152
+ it 'should not add anything if given controller & action' do
153
+ item = described_class.new(menu, :item1, nil) do
154
+ subitem1 controller: :foo, action: :bar, id: 123
155
+ end
156
+ menu.add(item)
157
+
158
+ expect(item.submenu[:subitem1].path).to eq(controller: '/foo', action: :bar, id: 123)
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'waiter/menu/section'
3
+ require 'waiter/menu'
4
+
5
+ RSpec.describe Waiter::Menu::Section do
6
+ let(:parent) { double(Waiter::Menu, name: :parent, options: {}) }
7
+ subject { described_class.new(parent) }
8
+
9
+ before do
10
+ allow_any_instance_of(described_class).to receive(:collect_controllers)
11
+ end
12
+
13
+ it { is_expected.to be_section }
14
+
15
+ its(:name) { is_expected.to be_nil }
16
+ its(:path) { is_expected.to be_nil }
17
+ its(:options) { is_expected.to be_empty }
18
+ its(:submenu) { is_expected.to be_nil }
19
+
20
+ it_behaves_like 'a menu item', []
21
+ end