From c9fdb216f321b332d166f23ec849e6b165f9bfe3 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Tue, 27 Jan 2026 19:08:31 +0100 Subject: [PATCH 01/30] Move dev dependencies to Gemfile --- Gemfile | 10 ++++++++++ simple-navigation.gemspec | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index fa75df15..e7f2a6b4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,13 @@ source 'https://rubygems.org' gemspec + +gem 'bundler' +gem 'capybara' +gem 'coveralls', '~> 0.7' +gem 'guard-rspec', '~> 4.2' +gem 'memfs', '~> 0.4.1' +gem 'rake' +gem 'rdoc' +gem 'rspec', '~> 3.0' +gem 'tzinfo', '>= 0' diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index aa4e3f2d..18bc8584 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -29,14 +29,4 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'activesupport', '>= 2.3.2' spec.add_runtime_dependency 'ostruct' - - spec.add_development_dependency 'bundler' - spec.add_development_dependency 'capybara' - spec.add_development_dependency 'coveralls', '~> 0.7' - spec.add_development_dependency 'guard-rspec', '~> 4.2' - spec.add_development_dependency 'memfs', '~> 0.4.1' - spec.add_development_dependency 'rake' - spec.add_development_dependency 'rdoc' - spec.add_development_dependency 'rspec', '~> 3.0' - spec.add_development_dependency 'tzinfo', '>= 0' end From 298eceb474ab7ebfa88bb2d485d5a74f998cf225 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Tue, 27 Jan 2026 19:19:01 +0100 Subject: [PATCH 02/30] Add some binstubs (bin/rake is faster than bundle exec rake) --- bin/_guard-core | 16 ++++++++++++++++ bin/guard | 16 ++++++++++++++++ bin/rake | 16 ++++++++++++++++ bin/rspec | 16 ++++++++++++++++ simple-navigation.gemspec | 1 - 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100755 bin/_guard-core create mode 100755 bin/guard create mode 100755 bin/rake create mode 100755 bin/rspec diff --git a/bin/_guard-core b/bin/_guard-core new file mode 100755 index 00000000..3b368e69 --- /dev/null +++ b/bin/_guard-core @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application '_guard-core' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("guard", "_guard-core") diff --git a/bin/guard b/bin/guard new file mode 100755 index 00000000..643c81f6 --- /dev/null +++ b/bin/guard @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'guard' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("guard", "guard") diff --git a/bin/rake b/bin/rake new file mode 100755 index 00000000..9efbee99 --- /dev/null +++ b/bin/rake @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rake", "rake") diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 00000000..93e191c2 --- /dev/null +++ b/bin/rspec @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rspec-core", "rspec") diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index 18bc8584..88ef9091 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -21,7 +21,6 @@ Gem::Specification.new do |spec| spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") - spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] From 4eb4457c4bf196cc8e2dfdb871461cc02a879c02 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Tue, 27 Jan 2026 19:27:05 +0100 Subject: [PATCH 03/30] Use apparaisal gem to manage Rails versions, switch from Travis to GithubActions Also disable coveralls for now [Coveralls] Submitting to https://coveralls.io/api/v1 Coveralls encountered an exception: OpenSSL::SSL::SSLError SSL_connect returned=1 errno=0 peeraddr=104.21.9.230:443 state=error: no protocols available --- .github/workflows/ci.yml | 90 +++++++++++++++++++++++++++++++ .travis.yml | 23 -------- Appraisals | 58 ++++++++++++++++++++ Gemfile | 1 + Rakefile | 23 -------- bin/appraisal | 16 ++++++ gemfiles/rails-3-2-stable.gemfile | 11 ---- gemfiles/rails-4-1-stable.gemfile | 7 --- gemfiles/rails-4-2-stable.gemfile | 7 --- gemfiles/rails-5-2-stable.gemfile | 7 --- gemfiles/rails-6-0-stable.gemfile | 9 ---- gemfiles/rails-6-1-stable.gemfile | 9 ---- gemfiles/rails_3.2.gemfile | 18 +++++++ gemfiles/rails_4.1.gemfile | 18 +++++++ gemfiles/rails_4.2.gemfile | 18 +++++++ gemfiles/rails_5.2.gemfile | 18 +++++++ gemfiles/rails_6.0.gemfile | 19 +++++++ gemfiles/rails_6.1.gemfile | 19 +++++++ gemfiles/rails_7.0.gemfile | 18 +++++++ gemfiles/rails_7.1.gemfile | 18 +++++++ gemfiles/rails_7.2.gemfile | 18 +++++++ gemfiles/rails_8.0.gemfile | 18 +++++++ gemfiles/rails_8.1.gemfile | 18 +++++++ spec/spec_helper.rb | 2 +- 24 files changed, 366 insertions(+), 97 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml create mode 100644 Appraisals create mode 100755 bin/appraisal delete mode 100644 gemfiles/rails-3-2-stable.gemfile delete mode 100644 gemfiles/rails-4-1-stable.gemfile delete mode 100644 gemfiles/rails-4-2-stable.gemfile delete mode 100644 gemfiles/rails-5-2-stable.gemfile delete mode 100644 gemfiles/rails-6-0-stable.gemfile delete mode 100644 gemfiles/rails-6-1-stable.gemfile create mode 100644 gemfiles/rails_3.2.gemfile create mode 100644 gemfiles/rails_4.1.gemfile create mode 100644 gemfiles/rails_4.2.gemfile create mode 100644 gemfiles/rails_5.2.gemfile create mode 100644 gemfiles/rails_6.0.gemfile create mode 100644 gemfiles/rails_6.1.gemfile create mode 100644 gemfiles/rails_7.0.gemfile create mode 100644 gemfiles/rails_7.1.gemfile create mode 100644 gemfiles/rails_7.2.gemfile create mode 100644 gemfiles/rails_8.0.gemfile create mode 100644 gemfiles/rails_8.1.gemfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..38064d78 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +--- +name: CI + +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + schedule: + - cron: '0 4 1 * *' + +jobs: + rspec: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + ruby: + - '4.0' + - '3.4' + - '3.3' + - '3.2' + - '3.1' + - '3.0' + - '2.7' + - '2.6' + rails: + - rails_8.1 + - rails_8.0 + - rails_7.2 + - rails_7.1 + - rails_7.0 + - rails_6.1 + - rails_6.0 + + exclude: + - ruby: '2.6' + rails: 'rails_7.0' + - ruby: '2.6' + rails: 'rails_7.1' + - ruby: '2.6' + rails: 'rails_7.2' + - ruby: '2.6' + rails: 'rails_8.0' + - ruby: '2.6' + rails: 'rails_8.1' + + - ruby: '2.7' + rails: 'rails_7.2' + - ruby: '2.7' + rails: 'rails_8.0' + - ruby: '2.7' + rails: 'rails_8.1' + + - ruby: '3.0' + rails: 'rails_6.0' + - ruby: '3.0' + rails: 'rails_7.2' + - ruby: '3.0' + rails: 'rails_8.0' + - ruby: '3.0' + rails: 'rails_8.1' + + - ruby: '3.1' + rails: 'rails_8.0' + - ruby: '3.1' + rails: 'rails_8.1' + + env: + # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.rails }}.gemfile + + # reenable me later + # RUBYOPT: "--enable=frozen-string-literal --debug=frozen-string-literal" + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: RSpec + run: bin/rspec diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 73ad5af5..00000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -before_install: - - gem install bundler - -matrix: - include: - - rvm: 2.5.8 - gemfile: gemfiles/rails-6-1-stable.gemfile - - rvm: 2.6.7 - gemfile: gemfiles/rails-6-1-stable.gemfile - - rvm: 2.7.3 - gemfile: gemfiles/rails-6-1-stable.gemfile - - rvm: 2.5.3 - gemfile: gemfiles/rails-6-0-stable.gemfile - - rvm: 2.6.0 - gemfile: gemfiles/rails-6-0-stable.gemfile - - rvm: 2.5.1 - gemfile: gemfiles/rails-5-2-stable.gemfile - - rvm: 2.4.5 - gemfile: gemfiles/rails-4-2-stable.gemfile - - rvm: 2.3.3 - gemfile: gemfiles/rails-4-1-stable.gemfile - - rvm: 2.3.3 - gemfile: gemfiles/rails-3-2-stable.gemfile diff --git a/Appraisals b/Appraisals new file mode 100644 index 00000000..d57bee4d --- /dev/null +++ b/Appraisals @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +appraise 'rails_3.2' do + gem 'railties', '~> 3.2.0' + gem 'rspec-rails' +end + +appraise 'rails_4.1' do + gem 'railties', '~> 4.1.0' + gem 'rspec-rails' +end + +appraise 'rails_4.2' do + gem 'railties', '~> 4.2.0' + gem 'rspec-rails' +end + +appraise 'rails_5.2' do + gem 'railties', '~> 5.2.0' + gem 'rspec-rails' +end + +appraise 'rails_6.0' do + gem 'railties', '~> 6.0.0' + gem 'rspec-rails' + gem 'concurrent-ruby', '1.3.4' +end + +appraise 'rails_6.1' do + gem 'railties', '~> 6.1.0' + gem 'rspec-rails' + gem 'concurrent-ruby', '1.3.4' +end + +appraise 'rails_7.0' do + gem 'railties', '~> 7.0.0' + gem 'rspec-rails' +end + +appraise 'rails_7.1' do + gem 'railties', '~> 7.1.0' + gem 'rspec-rails' +end + +appraise 'rails_7.2' do + gem 'railties', '~> 7.2.0' + gem 'rspec-rails' +end + +appraise 'rails_8.0' do + gem 'railties', '~> 8.0.0' + gem 'rspec-rails' +end + +appraise 'rails_8.1' do + gem 'railties', '~> 8.1.0' + gem 'rspec-rails' +end diff --git a/Gemfile b/Gemfile index e7f2a6b4..f5bae82d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gemspec +gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git' gem 'bundler' gem 'capybara' gem 'coveralls', '~> 0.7' diff --git a/Rakefile b/Rakefile index feeb7998..08012be7 100644 --- a/Rakefile +++ b/Rakefile @@ -7,31 +7,8 @@ require 'rspec/core/rake_task' require 'rdoc/task' RSpec::Core::RakeTask.new(:spec) - task default: 'spec' -namespace :spec do - mappers = %w[ - rails-3-2-stable - rails-4-1-stable - rails-4-2-stable - rails-5-2-stable - rails-6-0-stable - rails-6-1-stable - ] - - mappers.each do |gemfile| - desc "Run Tests against #{gemfile}" - task gemfile do - sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle -j 4 --quiet" - sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake -t spec" - end - end - - desc 'Run Tests against all ORMs' - task all: mappers -end - RDoc::Task.new do |rdoc| rdoc.rdoc_dir = 'rdoc' rdoc.title = 'SimpleNavigation' diff --git a/bin/appraisal b/bin/appraisal new file mode 100755 index 00000000..620da794 --- /dev/null +++ b/bin/appraisal @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'appraisal' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("appraisal", "appraisal") diff --git a/gemfiles/rails-3-2-stable.gemfile b/gemfiles/rails-3-2-stable.gemfile deleted file mode 100644 index 5b62ac4b..00000000 --- a/gemfiles/rails-3-2-stable.gemfile +++ /dev/null @@ -1,11 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' -gem 'railties', '~> 3.2.0' -gem 'rspec-rails', '~> 3.2.1' - -if RUBY_VERSION >= '2.2.0' - gem 'test-unit', '~> 3.0' -end - -gemspec path: '../' diff --git a/gemfiles/rails-4-1-stable.gemfile b/gemfiles/rails-4-1-stable.gemfile deleted file mode 100644 index 57c4af92..00000000 --- a/gemfiles/rails-4-1-stable.gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' -gem 'railties', '~> 4.1.0' -gem 'rspec-rails', '~> 3.2.1' - -gemspec path: '../' diff --git a/gemfiles/rails-4-2-stable.gemfile b/gemfiles/rails-4-2-stable.gemfile deleted file mode 100644 index 98206291..00000000 --- a/gemfiles/rails-4-2-stable.gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' -gem 'railties', '~> 4.2.0' -gem 'rspec-rails', '~> 3.2.1' - -gemspec path: '../' diff --git a/gemfiles/rails-5-2-stable.gemfile b/gemfiles/rails-5-2-stable.gemfile deleted file mode 100644 index a5fb0470..00000000 --- a/gemfiles/rails-5-2-stable.gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' -gem 'railties', '~> 5.2.0' -gem 'rspec-rails', '~> 3.8.2' - -gemspec path: '../' diff --git a/gemfiles/rails-6-0-stable.gemfile b/gemfiles/rails-6-0-stable.gemfile deleted file mode 100644 index 095cb349..00000000 --- a/gemfiles/rails-6-0-stable.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' - -# not yet -gem 'railties', '~> 6.0.0' -gem 'rspec-rails', '~> 3.8.2' - -gemspec path: '../' diff --git a/gemfiles/rails-6-1-stable.gemfile b/gemfiles/rails-6-1-stable.gemfile deleted file mode 100644 index c2024a32..00000000 --- a/gemfiles/rails-6-1-stable.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake', '< 11.0' - -# not yet -gem 'railties', '~> 6.1.0' -gem 'rspec-rails', '~> 3.8.2' - -gemspec path: '../' diff --git a/gemfiles/rails_3.2.gemfile b/gemfiles/rails_3.2.gemfile new file mode 100644 index 00000000..e41f15a6 --- /dev/null +++ b/gemfiles/rails_3.2.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 3.2.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_4.1.gemfile b/gemfiles/rails_4.1.gemfile new file mode 100644 index 00000000..72884a0b --- /dev/null +++ b/gemfiles/rails_4.1.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 4.1.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_4.2.gemfile b/gemfiles/rails_4.2.gemfile new file mode 100644 index 00000000..42d1a482 --- /dev/null +++ b/gemfiles/rails_4.2.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 4.2.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_5.2.gemfile b/gemfiles/rails_5.2.gemfile new file mode 100644 index 00000000..e95a2b7f --- /dev/null +++ b/gemfiles/rails_5.2.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 5.2.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_6.0.gemfile b/gemfiles/rails_6.0.gemfile new file mode 100644 index 00000000..fddf8260 --- /dev/null +++ b/gemfiles/rails_6.0.gemfile @@ -0,0 +1,19 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 6.0.0" +gem "rspec-rails" +gem "concurrent-ruby", "1.3.4" + +gemspec path: "../" diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile new file mode 100644 index 00000000..28f48b4c --- /dev/null +++ b/gemfiles/rails_6.1.gemfile @@ -0,0 +1,19 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 6.1.0" +gem "rspec-rails" +gem "concurrent-ruby", "1.3.4" + +gemspec path: "../" diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile new file mode 100644 index 00000000..98a7a080 --- /dev/null +++ b/gemfiles/rails_7.0.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 7.0.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile new file mode 100644 index 00000000..a26b86bf --- /dev/null +++ b/gemfiles/rails_7.1.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 7.1.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile new file mode 100644 index 00000000..79861158 --- /dev/null +++ b/gemfiles/rails_7.2.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 7.2.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile new file mode 100644 index 00000000..eb700a36 --- /dev/null +++ b/gemfiles/rails_8.0.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 8.0.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile new file mode 100644 index 00000000..bcfaf633 --- /dev/null +++ b/gemfiles/rails_8.1.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" +gem "bundler" +gem "capybara" +gem "coveralls", "~> 0.7" +gem "guard-rspec", "~> 4.2" +gem "memfs", "~> 0.4.1" +gem "rake" +gem "rdoc" +gem "rspec", "~> 3.0" +gem "tzinfo", ">= 0" +gem "railties", "~> 8.1.0" +gem "rspec-rails" + +gemspec path: "../" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 463deec4..25a2102f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ require 'initializers/have_css_matcher' require 'initializers/memfs' -require 'initializers/coveralls' +#require 'initializers/coveralls' require 'initializers/rails' require 'initializers/rspec' require 'capybara/rspec' From 929214e6eb41ddbe2db6279fe961c6f47dfe1559 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 03:39:43 +0100 Subject: [PATCH 04/30] Fix tests dealing with options hash expected: ({:renderer=>:my_renderer}) (keyword arguments) got: ({:renderer=>:my_renderer}) (options hash) --- spec/simple_navigation/adapters/padrino_spec.rb | 4 ++-- spec/simple_navigation/helpers_spec.rb | 6 +++--- spec/simple_navigation/item_container_spec.rb | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/simple_navigation/adapters/padrino_spec.rb b/spec/simple_navigation/adapters/padrino_spec.rb index db668cb1..5efb9ebf 100644 --- a/spec/simple_navigation/adapters/padrino_spec.rb +++ b/spec/simple_navigation/adapters/padrino_spec.rb @@ -9,7 +9,7 @@ module Adapters describe '#link_to' do it 'delegates to context' do expect(context).to receive(:link_to) - .with('name', 'url', :my_option => true) + .with('name', 'url', {:my_option => true}) adapter.link_to('name', 'url', :my_option => true) end end @@ -18,7 +18,7 @@ module Adapters it 'delegates to context' do expect(content).to receive(:html_safe).and_return('content') expect(context).to receive(:content_tag) - .with('type', 'content', my_option: true) + .with('type', 'content', {my_option: true}) adapter.content_tag('type', content, my_option: true) end end diff --git a/spec/simple_navigation/helpers_spec.rb b/spec/simple_navigation/helpers_spec.rb index f6b720dd..85b8ff50 100644 --- a/spec/simple_navigation/helpers_spec.rb +++ b/spec/simple_navigation/helpers_spec.rb @@ -344,7 +344,7 @@ module SimpleNavigation context 'when the :level option is set' do context 'and its value is 1' do it 'calls render on the primary navigation' do - expect(navigation).to receive(:render).with(level: 1) + expect(navigation).to receive(:render).with({level: 1}) controller.render_navigation(level: 1) end end @@ -364,7 +364,7 @@ module SimpleNavigation end it 'calls render on the active item_container' do - expect(item_container).to receive(:render).with(level: 2) + expect(item_container).to receive(:render).with({level: 2}) controller.render_navigation(level: 2) end end @@ -391,7 +391,7 @@ module SimpleNavigation before { allow(SimpleNavigation).to receive_messages(active_item_container_for: navigation) } it 'treats it like the :level option' do - expect(navigation).to receive(:render).with(level: 2) + expect(navigation).to receive(:render).with({level: 2}) controller.render_navigation(levels: 2) end end diff --git a/spec/simple_navigation/item_container_spec.rb b/spec/simple_navigation/item_container_spec.rb index 863e7ddd..a6aa7794 100644 --- a/spec/simple_navigation/item_container_spec.rb +++ b/spec/simple_navigation/item_container_spec.rb @@ -492,7 +492,7 @@ module SimpleNavigation context 'and is specified as a class' do it 'instantiates the passed renderer_class with the options' do expect(renderer_class).to receive(:new) - .with(renderer: renderer_class) + .with({renderer: renderer_class}) item_container.render(renderer: renderer_class) end @@ -510,7 +510,7 @@ module SimpleNavigation end it "instantiates the passed renderer_class with the options" do - expect(renderer_class).to receive(:new).with(renderer: :my_renderer) + expect(renderer_class).to receive(:new).with({renderer: :my_renderer}) item_container.render(renderer: :my_renderer) end From cbce38ef80be47b19533da5da5a16aa63d9dd27e Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 03:42:50 +0100 Subject: [PATCH 05/30] Add missing dependencies due to stdlib gemification --- Appraisals | 26 ++++++++++++++++++++++++++ gemfiles/rails_6.0.gemfile | 10 ++++++++++ gemfiles/rails_6.1.gemfile | 10 ++++++++++ 3 files changed, 46 insertions(+) diff --git a/Appraisals b/Appraisals index d57bee4d..30560506 100644 --- a/Appraisals +++ b/Appraisals @@ -24,12 +24,38 @@ appraise 'rails_6.0' do gem 'railties', '~> 6.0.0' gem 'rspec-rails' gem 'concurrent-ruby', '1.3.4' + + # Fix: + # warning: drb was loaded from the standard library, but is not part of the default gems starting from Ruby 3.4.0. + # You can add drb to your Gemfile or gemspec to silence this warning. + install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do + gem 'base64' + gem 'drb' + gem 'mutex_m' + end + + install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") }' do + gem 'benchmark' + end end appraise 'rails_6.1' do gem 'railties', '~> 6.1.0' gem 'rspec-rails' gem 'concurrent-ruby', '1.3.4' + + # Fix: + # warning: drb was loaded from the standard library, but is not part of the default gems starting from Ruby 3.4.0. + # You can add drb to your Gemfile or gemspec to silence this warning. + install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do + gem 'base64' + gem 'drb' + gem 'mutex_m' + end + + install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") }' do + gem 'benchmark' + end end appraise 'rails_7.0' do diff --git a/gemfiles/rails_6.0.gemfile b/gemfiles/rails_6.0.gemfile index fddf8260..3f510c6b 100644 --- a/gemfiles/rails_6.0.gemfile +++ b/gemfiles/rails_6.0.gemfile @@ -16,4 +16,14 @@ gem "railties", "~> 6.0.0" gem "rspec-rails" gem "concurrent-ruby", "1.3.4" +install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") } do + gem "base64" + gem "drb" + gem "mutex_m" +end + +install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") } do + gem "benchmark" +end + gemspec path: "../" diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index 28f48b4c..6439a1e9 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -16,4 +16,14 @@ gem "railties", "~> 6.1.0" gem "rspec-rails" gem "concurrent-ruby", "1.3.4" +install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") } do + gem "base64" + gem "drb" + gem "mutex_m" +end + +install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") } do + gem "benchmark" +end + gemspec path: "../" From d120b95fed4c58915d8f9d74b0eef8f5f0c46123 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 03:56:11 +0100 Subject: [PATCH 06/30] Disable RSpec monkey patching, don't reopen Ruby namespaces in specs --- spec/initializers/rspec.rb | 4 + .../adapters/padrino_spec.rb | 40 +- spec/simple_navigation/adapters/rails_spec.rb | 406 +++++---- .../adapters/sinatra_spec.rb | 2 +- .../config_file_finder_spec.rb | 54 +- spec/simple_navigation/config_file_spec.rb | 26 +- spec/simple_navigation/configuration_spec.rb | 240 +++--- spec/simple_navigation/helpers_spec.rb | 562 ++++++------ spec/simple_navigation/item_adapter_spec.rb | 236 +++-- spec/simple_navigation/item_container_spec.rb | 814 +++++++++--------- spec/simple_navigation/item_spec.rb | 608 +++++++------ spec/simple_navigation/items_provider_spec.rb | 52 +- spec/simple_navigation/renderer/base_spec.rb | 332 ++++--- .../renderer/breadcrumbs_spec.rb | 162 ++-- spec/simple_navigation/renderer/json_spec.rb | 106 ++- spec/simple_navigation/renderer/links_spec.rb | 162 ++-- spec/simple_navigation/renderer/list_spec.rb | 178 ++-- spec/simple_navigation/renderer/text_spec.rb | 74 +- spec/simple_navigation_spec.rb | 2 +- 19 files changed, 2008 insertions(+), 2052 deletions(-) diff --git a/spec/initializers/rspec.rb b/spec/initializers/rspec.rb index fe69bb29..c08eb59f 100644 --- a/spec/initializers/rspec.rb +++ b/spec/initializers/rspec.rb @@ -4,4 +4,8 @@ end config.order = :random + + # disable monkey patching + # see: https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! end diff --git a/spec/simple_navigation/adapters/padrino_spec.rb b/spec/simple_navigation/adapters/padrino_spec.rb index 5efb9ebf..48841147 100644 --- a/spec/simple_navigation/adapters/padrino_spec.rb +++ b/spec/simple_navigation/adapters/padrino_spec.rb @@ -1,27 +1,23 @@ -module SimpleNavigation - module Adapters - describe Padrino do - let(:adapter) { SimpleNavigation::Adapters::Padrino.new(context) } - let(:content) { double(:content) } - let(:context) { double(:context, request: request) } - let(:request) { double(:request) } +RSpec.describe SimpleNavigation::Adapters::Padrino do + let(:adapter) { SimpleNavigation::Adapters::Padrino.new(context) } + let(:content) { double(:content) } + let(:context) { double(:context, request: request) } + let(:request) { double(:request) } - describe '#link_to' do - it 'delegates to context' do - expect(context).to receive(:link_to) - .with('name', 'url', {:my_option => true}) - adapter.link_to('name', 'url', :my_option => true) - end - end + describe '#link_to' do + it 'delegates to context' do + expect(context).to receive(:link_to) + .with('name', 'url', {:my_option => true}) + adapter.link_to('name', 'url', :my_option => true) + end + end - describe '#content_tag' do - it 'delegates to context' do - expect(content).to receive(:html_safe).and_return('content') - expect(context).to receive(:content_tag) - .with('type', 'content', {my_option: true}) - adapter.content_tag('type', content, my_option: true) - end - end + describe '#content_tag' do + it 'delegates to context' do + expect(content).to receive(:html_safe).and_return('content') + expect(context).to receive(:content_tag) + .with('type', 'content', {my_option: true}) + adapter.content_tag('type', content, my_option: true) end end end diff --git a/spec/simple_navigation/adapters/rails_spec.rb b/spec/simple_navigation/adapters/rails_spec.rb index 3dcb3c50..1d9c6704 100644 --- a/spec/simple_navigation/adapters/rails_spec.rb +++ b/spec/simple_navigation/adapters/rails_spec.rb @@ -1,281 +1,277 @@ -module SimpleNavigation - module Adapters - describe Rails do - let(:action_controller) { ActionController::Base } - let(:adapter) { Rails.new(context) } - let(:context) { double(:context, controller: controller) } - let(:controller) { double(:controller) } - let(:request) { double(:request) } - let(:simple_navigation) { SimpleNavigation } - let(:template) { double(:template, request: request) } - - describe '.register' do - before { allow(action_controller).to receive(:include) } - - it 'calls set_env' do - app_path = RailsApp::Application.root - expect(simple_navigation).to receive(:set_env).with(app_path, 'test') - simple_navigation.register - end +RSpec.describe SimpleNavigation::Adapters::Rails do + let(:action_controller) { ActionController::Base } + let(:adapter) { SimpleNavigation::Adapters::Rails.new(context) } + let(:context) { double(:context, controller: controller) } + let(:controller) { double(:controller) } + let(:request) { double(:request) } + let(:simple_navigation) { SimpleNavigation } + let(:template) { double(:template, request: request) } + + describe '.register' do + before { allow(action_controller).to receive(:include) } + + it 'calls set_env' do + app_path = RailsApp::Application.root + expect(simple_navigation).to receive(:set_env).with(app_path, 'test') + simple_navigation.register + end - it 'extends the ActionController::Base with the Helpers' do - expect(action_controller).to receive(:include) - .with(SimpleNavigation::Helpers) - simple_navigation.register - end + it 'extends the ActionController::Base with the Helpers' do + expect(action_controller).to receive(:include) + .with(SimpleNavigation::Helpers) + simple_navigation.register + end - shared_examples 'installing helper method' do |method| - it "installs the #{method} method as helper method" do - simple_navigation.register + shared_examples 'installing helper method' do |method| + it "installs the #{method} method as helper method" do + simple_navigation.register - helper_methods = action_controller.send(:_helper_methods) - expect(helper_methods).to include(method) - end - end - - it_behaves_like 'installing helper method', :render_navigation - it_behaves_like 'installing helper method', :active_navigation_item_name - it_behaves_like 'installing helper method', :active_navigation_item_key - it_behaves_like 'installing helper method', :active_navigation_item - it_behaves_like 'installing helper method', :active_navigation_item_container + helper_methods = action_controller.send(:_helper_methods) + expect(helper_methods).to include(method) end + end - describe '#initialize' do - context "when the controller's template is set" do - before { allow(controller).to receive_messages(instance_variable_get: template) } - - it "sets the adapter's request accordingly" do - expect(adapter.request).to be request - end - end + it_behaves_like 'installing helper method', :render_navigation + it_behaves_like 'installing helper method', :active_navigation_item_name + it_behaves_like 'installing helper method', :active_navigation_item_key + it_behaves_like 'installing helper method', :active_navigation_item + it_behaves_like 'installing helper method', :active_navigation_item_container + end - context "when the controller's template is not set" do - before { allow(controller).to receive_messages(instance_variable_get: nil) } + describe '#initialize' do + context "when the controller's template is set" do + before { allow(controller).to receive_messages(instance_variable_get: template) } - it "sets the adapter's request to nil" do - expect(adapter.request).to be_nil - end - end + it "sets the adapter's request accordingly" do + expect(adapter.request).to be request + end + end - it "sets the adapter's controller to the context's controller" do - expect(adapter.controller).to be controller - end + context "when the controller's template is not set" do + before { allow(controller).to receive_messages(instance_variable_get: nil) } - context "when the controller's template is stored as instance var (Rails2)" do - context "when the controller's template is set" do - before { allow(controller).to receive_messages(instance_variable_get: template) } + it "sets the adapter's request to nil" do + expect(adapter.request).to be_nil + end + end - it "sets the adapter's template accordingly" do - expect(adapter.template).to be template - end - end + it "sets the adapter's controller to the context's controller" do + expect(adapter.controller).to be controller + end - context "when the controller's template is not set" do - before { allow(controller).to receive_messages(instance_variable_get: nil) } + context "when the controller's template is stored as instance var (Rails2)" do + context "when the controller's template is set" do + before { allow(controller).to receive_messages(instance_variable_get: template) } - it "set the adapter's template to nil" do - expect(adapter.template).to be_nil - end - end + it "sets the adapter's template accordingly" do + expect(adapter.template).to be template end + end - context "when the controller's template is stored as view_context (Rails3)" do - context 'and the template is set' do - before { allow(controller).to receive_messages(view_context: template) } + context "when the controller's template is not set" do + before { allow(controller).to receive_messages(instance_variable_get: nil) } - it "sets the adapter's template accordingly" do - expect(adapter.template).to be template - end - end + it "set the adapter's template to nil" do + expect(adapter.template).to be_nil + end + end + end - context 'and the template is not set' do - before { allow(controller).to receive_messages(view_context: nil) } + context "when the controller's template is stored as view_context (Rails3)" do + context 'and the template is set' do + before { allow(controller).to receive_messages(view_context: template) } - it "sets the adapter's template to nil" do - expect(adapter.template).to be_nil - end - end + it "sets the adapter's template accordingly" do + expect(adapter.template).to be template end end - describe '#request_uri' do - context "when the adapter's request is set" do - before { allow(adapter).to receive_messages(request: request) } + context 'and the template is not set' do + before { allow(controller).to receive_messages(view_context: nil) } - context 'and request.fullpath is defined' do - let(:request) { double(:request, fullpath: '/fullpath') } - - it "sets the adapter's request_uri to the request.fullpath" do - expect(adapter.request_uri).to eq '/fullpath' - end - end + it "sets the adapter's template to nil" do + expect(adapter.template).to be_nil + end + end + end + end - context 'and request.fullpath is not defined' do - let(:request) { double(:request, request_uri: '/request_uri') } + describe '#request_uri' do + context "when the adapter's request is set" do + before { allow(adapter).to receive_messages(request: request) } - before { allow(adapter).to receive_messages(request: request) } + context 'and request.fullpath is defined' do + let(:request) { double(:request, fullpath: '/fullpath') } - it "sets the adapter's request_uri to the request.request_uri" do - expect(adapter.request_uri).to eq '/request_uri' - end - end + it "sets the adapter's request_uri to the request.fullpath" do + expect(adapter.request_uri).to eq '/fullpath' end + end - context "when the adapter's request is not set" do - before { allow(adapter).to receive_messages(request: nil) } + context 'and request.fullpath is not defined' do + let(:request) { double(:request, request_uri: '/request_uri') } - it "sets the adapter's request_uri to an empty string" do - expect(adapter.request_uri).to eq '' - end + before { allow(adapter).to receive_messages(request: request) } + + it "sets the adapter's request_uri to the request.request_uri" do + expect(adapter.request_uri).to eq '/request_uri' end end + end - describe '#request_path' do - context "when the adapter's request is set" do - let(:request) { double(:request, path: '/request_path') } + context "when the adapter's request is not set" do + before { allow(adapter).to receive_messages(request: nil) } - before { allow(adapter).to receive_messages(request: request) } + it "sets the adapter's request_uri to an empty string" do + expect(adapter.request_uri).to eq '' + end + end + end - it "sets the adapter's request_path to the request.path" do - expect(adapter.request_path).to eq '/request_path' - end - end + describe '#request_path' do + context "when the adapter's request is set" do + let(:request) { double(:request, path: '/request_path') } - context "when the adapter's request is not set" do - before { allow(adapter).to receive_messages(request: nil) } + before { allow(adapter).to receive_messages(request: request) } - it "sets the adapter's request_path to an empty string" do - expect(adapter.request_path).to eq '' - end - end + it "sets the adapter's request_path to the request.path" do + expect(adapter.request_path).to eq '/request_path' end + end - describe '#context_for_eval' do - context "when the adapter's controller is set" do - before { adapter.instance_variable_set(:@controller, controller) } + context "when the adapter's request is not set" do + before { allow(adapter).to receive_messages(request: nil) } - context "and the adapter's template is set" do - before { adapter.instance_variable_set(:@template, template) } + it "sets the adapter's request_path to an empty string" do + expect(adapter.request_path).to eq '' + end + end + end - it "sets the adapter's context_for_eval to the template" do - expect(adapter.context_for_eval).to be template - end - end + describe '#context_for_eval' do + context "when the adapter's controller is set" do + before { adapter.instance_variable_set(:@controller, controller) } - context "and the adapter's template is not set" do - before { adapter.instance_variable_set(:@template, nil) } + context "and the adapter's template is set" do + before { adapter.instance_variable_set(:@template, template) } - it "sets the adapter's context_for_eval to the controller" do - expect(adapter.context_for_eval).to be controller - end - end + it "sets the adapter's context_for_eval to the template" do + expect(adapter.context_for_eval).to be template end + end - context "when the adapter's controller is not set" do - before { adapter.instance_variable_set(:@controller, nil) } + context "and the adapter's template is not set" do + before { adapter.instance_variable_set(:@template, nil) } - context "and the adapter's template is set" do - before { adapter.instance_variable_set(:@template, template) } + it "sets the adapter's context_for_eval to the controller" do + expect(adapter.context_for_eval).to be controller + end + end + end - it "sets the adapter's context_for_eval to the template" do - expect(adapter.context_for_eval).to be template - end - end + context "when the adapter's controller is not set" do + before { adapter.instance_variable_set(:@controller, nil) } - context "and the adapter's template is not set" do - before { adapter.instance_variable_set(:@template, nil) } + context "and the adapter's template is set" do + before { adapter.instance_variable_set(:@template, template) } - it 'raises an exception' do - expect{ adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') - end - end + it "sets the adapter's context_for_eval to the template" do + expect(adapter.context_for_eval).to be template end end - describe '#current_page?' do - context "when the adapter's template is set" do - before { allow(adapter).to receive_messages(template: template) } + context "and the adapter's template is not set" do + before { adapter.instance_variable_set(:@template, nil) } - it 'delegates the call to the template' do - expect(template).to receive(:current_page?).with(:page) - adapter.current_page?(:page) - end + it 'raises an exception' do + expect{ adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') end + end + end + end - context "when the adapter's template is not set" do - before { allow(adapter).to receive_messages(template: nil) } - - it 'returns false' do - expect(adapter.current_page?(:page)).to be_falsey - end - end + describe '#current_page?' do + context "when the adapter's template is set" do + before { allow(adapter).to receive_messages(template: template) } - context 'when the given url is nil' do - it 'returns false' do - expect(adapter.current_page?(nil)).to be_falsey - end - end + it 'delegates the call to the template' do + expect(template).to receive(:current_page?).with(:page) + adapter.current_page?(:page) end + end - describe '#link_to' do - let(:options) { double(:options) } + context "when the adapter's template is not set" do + before { allow(adapter).to receive_messages(template: nil) } - context "when the adapter's template is set" do - before { allow(adapter).to receive_messages(template: template, html_safe: 'safe_text') } + it 'returns false' do + expect(adapter.current_page?(:page)).to be_falsey + end + end - context 'with considering item names as safe' do - before { SimpleNavigation.config.consider_item_names_as_safe = true } - after { SimpleNavigation.config.consider_item_names_as_safe = false } + context 'when the given url is nil' do + it 'returns false' do + expect(adapter.current_page?(nil)).to be_falsey + end + end + end - it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:link_to) - .with('safe_text', 'url', options) - adapter.link_to('text', 'url', options) - end - end + describe '#link_to' do + let(:options) { double(:options) } - context 'with considering item names as UNsafe (default)' do + context "when the adapter's template is set" do + before { allow(adapter).to receive_messages(template: template, html_safe: 'safe_text') } - it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:link_to) - .with('text', 'url', options) - adapter.link_to('text', 'url', options) - end - end + context 'with considering item names as safe' do + before { SimpleNavigation.config.consider_item_names_as_safe = true } + after { SimpleNavigation.config.consider_item_names_as_safe = false } + it 'delegates the call to the template (with html_safe text)' do + expect(template).to receive(:link_to) + .with('safe_text', 'url', options) + adapter.link_to('text', 'url', options) end + end - context "when the adapter's template is not set" do - before { allow(adapter).to receive_messages(template: nil) } + context 'with considering item names as UNsafe (default)' do - it 'returns nil' do - expect(adapter.link_to('text', 'url', options)).to be_nil - end + it 'delegates the call to the template (with html_safe text)' do + expect(template).to receive(:link_to) + .with('text', 'url', options) + adapter.link_to('text', 'url', options) end end - describe '#content_tag' do - let(:options) { double(:options) } + end - context "when the adapter's template is set" do - before { allow(adapter).to receive_messages(template: template, html_safe: 'safe_text') } + context "when the adapter's template is not set" do + before { allow(adapter).to receive_messages(template: nil) } - it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:content_tag) - .with(:div, 'safe_text', options) - adapter.content_tag(:div, 'text', options) - end - end + it 'returns nil' do + expect(adapter.link_to('text', 'url', options)).to be_nil + end + end + end - context "when the adapter's template is not set" do - before { allow(adapter).to receive_messages(template: nil) } + describe '#content_tag' do + let(:options) { double(:options) } - it 'returns nil' do - expect(adapter.content_tag(:div, 'text', options)).to be_nil - end - end + context "when the adapter's template is set" do + before { allow(adapter).to receive_messages(template: template, html_safe: 'safe_text') } + + it 'delegates the call to the template (with html_safe text)' do + expect(template).to receive(:content_tag) + .with(:div, 'safe_text', options) + adapter.content_tag(:div, 'text', options) end + end + + context "when the adapter's template is not set" do + before { allow(adapter).to receive_messages(template: nil) } + it 'returns nil' do + expect(adapter.content_tag(:div, 'text', options)).to be_nil + end end end + end diff --git a/spec/simple_navigation/adapters/sinatra_spec.rb b/spec/simple_navigation/adapters/sinatra_spec.rb index c587f105..14a0534f 100644 --- a/spec/simple_navigation/adapters/sinatra_spec.rb +++ b/spec/simple_navigation/adapters/sinatra_spec.rb @@ -1,4 +1,4 @@ -describe SimpleNavigation::Adapters::Sinatra do +RSpec.describe SimpleNavigation::Adapters::Sinatra do let(:adapter) { SimpleNavigation::Adapters::Sinatra.new(context) } let(:context) { double(:context) } let(:request) { double(:request, fullpath: '/full?param=true', path: '/full') } diff --git a/spec/simple_navigation/config_file_finder_spec.rb b/spec/simple_navigation/config_file_finder_spec.rb index 6b4581be..bee7111a 100644 --- a/spec/simple_navigation/config_file_finder_spec.rb +++ b/spec/simple_navigation/config_file_finder_spec.rb @@ -1,48 +1,46 @@ require 'fileutils' require 'simple_navigation/config_file_finder' -module SimpleNavigation - describe ConfigFileFinder do - subject(:finder) { ConfigFileFinder.new(paths) } +RSpec.describe SimpleNavigation::ConfigFileFinder do + subject(:finder) { SimpleNavigation::ConfigFileFinder.new(paths) } - let(:paths) { ['/path/one', '/path/two'] } + let(:paths) { ['/path/one', '/path/two'] } - describe '#find', memfs: true do - before { FileUtils.mkdir_p(paths) } + describe '#find', memfs: true do + before { FileUtils.mkdir_p(paths) } - context 'when the context is :default' do - let(:context) { :default } + context 'when the context is :default' do + let(:context) { :default } - context 'and a navigation.rb file is found in one of the paths' do - before { FileUtils.touch('/path/one/navigation.rb') } + context 'and a navigation.rb file is found in one of the paths' do + before { FileUtils.touch('/path/one/navigation.rb') } - it 'returns its full path' do - expect(finder.find(context)).to eq '/path/one/navigation.rb' - end + it 'returns its full path' do + expect(finder.find(context)).to eq '/path/one/navigation.rb' end + end - context 'and no navigation.rb file is found in the paths' do - it 'raises an exception' do - expect { finder.find(context) }.to raise_error(RuntimeError, /Config file 'navigation.rb' not found in path\(s\)/) - end + context 'and no navigation.rb file is found in the paths' do + it 'raises an exception' do + expect { finder.find(context) }.to raise_error(RuntimeError, /Config file 'navigation.rb' not found in path\(s\)/) end end + end - context 'when the context is :other' do - let(:context) { :other } + context 'when the context is :other' do + let(:context) { :other } - context 'and a other_navigation.rb file is found in one of the paths' do - before { FileUtils.touch('/path/two/other_navigation.rb') } + context 'and a other_navigation.rb file is found in one of the paths' do + before { FileUtils.touch('/path/two/other_navigation.rb') } - it 'returns its full path' do - expect(finder.find(context)).to eq '/path/two/other_navigation.rb' - end + it 'returns its full path' do + expect(finder.find(context)).to eq '/path/two/other_navigation.rb' end + end - context 'and no other_navigation.rb file is found in the paths' do - it 'raise an exception' do - expect{ finder.find(context) }.to raise_error(RuntimeError, /Config file 'other_navigation.rb' not found in path\(s\)/) - end + context 'and no other_navigation.rb file is found in the paths' do + it 'raise an exception' do + expect{ finder.find(context) }.to raise_error(RuntimeError, /Config file 'other_navigation.rb' not found in path\(s\)/) end end end diff --git a/spec/simple_navigation/config_file_spec.rb b/spec/simple_navigation/config_file_spec.rb index 56d481e3..b70c4251 100644 --- a/spec/simple_navigation/config_file_spec.rb +++ b/spec/simple_navigation/config_file_spec.rb @@ -1,24 +1,22 @@ require 'simple_navigation/config_file' -module SimpleNavigation - describe ConfigFile do - subject(:config_file) { ConfigFile.new(context) } +RSpec.describe SimpleNavigation::ConfigFile do + subject(:config_file) { SimpleNavigation::ConfigFile.new(context) } - let(:context) { :default } + let(:context) { :default } - describe '#name' do - context 'when the context is :default' do - it 'returns navigation.rb' do - expect(config_file.name).to eq 'navigation.rb' - end + describe '#name' do + context 'when the context is :default' do + it 'returns navigation.rb' do + expect(config_file.name).to eq 'navigation.rb' end + end - context 'when the context is different from :default' do - let(:context) { :HelloWorld } + context 'when the context is different from :default' do + let(:context) { :HelloWorld } - it 'returns UNDERSCORED_CONTEXT_navigation.rb' do - expect(config_file.name).to eq 'hello_world_navigation.rb' - end + it 'returns UNDERSCORED_CONTEXT_navigation.rb' do + expect(config_file.name).to eq 'hello_world_navigation.rb' end end end diff --git a/spec/simple_navigation/configuration_spec.rb b/spec/simple_navigation/configuration_spec.rb index da4790f4..f8f405c4 100644 --- a/spec/simple_navigation/configuration_spec.rb +++ b/spec/simple_navigation/configuration_spec.rb @@ -1,164 +1,162 @@ -module SimpleNavigation - describe Configuration do - subject(:config) { Configuration.instance } +RSpec.describe SimpleNavigation::Configuration do + subject(:config) { SimpleNavigation::Configuration.instance } - describe '.run' do - it "yields the singleton Configuration object" do - expect{ |blk| Configuration.run(&blk) }.to yield_with_args(config) - end + describe '.run' do + it "yields the singleton Configuration object" do + expect{ |blk| SimpleNavigation::Configuration.run(&blk) }.to yield_with_args(config) end + end - describe '.eval_config' do - let(:config_files) {{ default: 'default', my_context: 'my_context' }} - let(:eval_context) { double(:eval_context) } + describe '.eval_config' do + let(:config_files) {{ default: 'default', my_context: 'my_context' }} + let(:eval_context) { double(:eval_context) } - before do - allow(eval_context).to receive(:instance_eval) - allow(SimpleNavigation).to \ - receive_messages(context_for_eval: eval_context, config_files: config_files) - end + before do + allow(eval_context).to receive(:instance_eval) + allow(SimpleNavigation).to \ + receive_messages(context_for_eval: eval_context, config_files: config_files) + end - context "with default navigation context" do - it "calls instance_eval with the default config_file-string inside the context" do - expect(eval_context).to receive(:instance_eval).with('default') - Configuration.eval_config - end + context "with default navigation context" do + it "calls instance_eval with the default config_file-string inside the context" do + expect(eval_context).to receive(:instance_eval).with('default') + SimpleNavigation::Configuration.eval_config end + end - context 'with non default navigation context' do - it "calls instance_eval with the specified config_file-string inside the context" do - expect(eval_context).to receive(:instance_eval).with('my_context') - Configuration.eval_config(:my_context) - end + context 'with non default navigation context' do + it "calls instance_eval with the specified config_file-string inside the context" do + expect(eval_context).to receive(:instance_eval).with('my_context') + SimpleNavigation::Configuration.eval_config(:my_context) end end + end - describe '#initialize' do - it 'sets the List-Renderer as default' do - expect(config.renderer).to be Renderer::List - end + describe '#initialize' do + it 'sets the List-Renderer as default' do + expect(config.renderer).to be SimpleNavigation::Renderer::List + end - it 'sets the selected_class to "selected" as default' do - expect(config.selected_class).to eq 'selected' - end + it 'sets the selected_class to "selected" as default' do + expect(config.selected_class).to eq 'selected' + end - it 'sets the active_leaf_class to "simple-navigation-active-leaf" as default' do - expect(config.active_leaf_class).to eq 'simple-navigation-active-leaf' - end + it 'sets the active_leaf_class to "simple-navigation-active-leaf" as default' do + expect(config.active_leaf_class).to eq 'simple-navigation-active-leaf' + end - it 'sets autogenerate_item_ids to true as default' do - expect(config.autogenerate_item_ids).to be true - end + it 'sets autogenerate_item_ids to true as default' do + expect(config.autogenerate_item_ids).to be true + end - it 'sets auto_highlight to true as default' do - expect(config.auto_highlight).to be true - end + it 'sets auto_highlight to true as default' do + expect(config.auto_highlight).to be true + end - it 'sets the id_generator to a callable object' do - expect(config.id_generator).to respond_to(:call) - end + it 'sets the id_generator to a callable object' do + expect(config.id_generator).to respond_to(:call) + end - it 'sets the name_generator to a callable object' do - expect(config.name_generator).to respond_to(:call) - end + it 'sets the name_generator to a callable object' do + expect(config.name_generator).to respond_to(:call) + end - it 'sets the consider_item_names_as_safe to false' do - expect(config.consider_item_names_as_safe).to be false - end + it 'sets the consider_item_names_as_safe to false' do + expect(config.consider_item_names_as_safe).to be false + end - it 'sets highlights_on_subpath to false as default' do - expect(config.highlight_on_subpath).to be false - end - - it 'sets ignore_query_params_on_auto_highlight to true as default' do - expect(config.ignore_query_params_on_auto_highlight).to be true - end + it 'sets highlights_on_subpath to false as default' do + expect(config.highlight_on_subpath).to be false + end - it 'sets ignore_anchors_on_auto_highlight to true as default' do - expect(config.ignore_anchors_on_auto_highlight).to be true - end + it 'sets ignore_query_params_on_auto_highlight to true as default' do + expect(config.ignore_query_params_on_auto_highlight).to be true + end + + it 'sets ignore_anchors_on_auto_highlight to true as default' do + expect(config.ignore_anchors_on_auto_highlight).to be true end + end - describe '#items' do - let(:container) { double(:items_container) } + describe '#items' do + let(:container) { double(:items_container) } - before { allow(ItemContainer).to receive_messages(new: container) } + before { allow(SimpleNavigation::ItemContainer).to receive_messages(new: container) } - context 'when a block is given' do - context 'and items_provider is specified' do - let(:provider) { double(:provider) } + context 'when a block is given' do + context 'and items_provider is specified' do + let(:provider) { double(:provider) } - it 'raises an exception' do - expect{ config.items(provider) {} }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') - end + it 'raises an exception' do + expect{ config.items(provider) {} }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') end + end - context 'when no items_provider is specified' do - it 'yields an new ItemContainer' do - expect{ |blk| config.items(&blk) }.to yield_with_args(container) - end + context 'when no items_provider is specified' do + it 'yields an new ItemContainer' do + expect{ |blk| config.items(&blk) }.to yield_with_args(container) + end - it 'assigns the ItemContainer to an instance-var' do - config.items {} - expect(config.primary_navigation).to be container - end + it 'assigns the ItemContainer to an instance-var' do + config.items {} + expect(config.primary_navigation).to be container + end - it "doesn't set the items on the container" do - expect(container).not_to receive(:items=) - config.items {} - end + it "doesn't set the items on the container" do + expect(container).not_to receive(:items=) + config.items {} end end + end + + context 'when no block is given' do + context 'and items_provider is specified' do + let(:external_provider) { double(:external_provider) } + let(:items) { double(:items) } + let(:items_provider) { double(:items_provider, items: items) } - context 'when no block is given' do - context 'and items_provider is specified' do - let(:external_provider) { double(:external_provider) } - let(:items) { double(:items) } - let(:items_provider) { double(:items_provider, items: items) } - - before do - allow(SimpleNavigation::ItemsProvider).to receive_messages(new: items_provider) - allow(container).to receive(:items=) - end - - it 'creates a new Provider object for the specified provider' do - expect(ItemsProvider).to receive(:new).with(external_provider) - config.items(external_provider) - end - - it 'calls items on the provider object' do - expect(items_provider).to receive(:items) - config.items(external_provider) - end - - it 'sets the items on the container' do - expect(container).to receive(:items=).with(items) - config.items(external_provider) - end + before do + allow(SimpleNavigation::ItemsProvider).to receive_messages(new: items_provider) + allow(container).to receive(:items=) end - context 'when items_provider is not specified' do - it "raises an exception" do - expect{ config.items }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') - end + it 'creates a new Provider object for the specified provider' do + expect(SimpleNavigation::ItemsProvider).to receive(:new).with(external_provider) + config.items(external_provider) + end + + it 'calls items on the provider object' do + expect(items_provider).to receive(:items) + config.items(external_provider) end - end - end - describe '#loaded?' do - context 'when primary_nav is set' do - it 'returns true' do - config.instance_variable_set(:@primary_navigation, :bla) - expect(config).to be_loaded + it 'sets the items on the container' do + expect(container).to receive(:items=).with(items) + config.items(external_provider) end end - context 'when primary_nav is not set' do - it 'returns false if no primary_nav is set' do - config.instance_variable_set(:@primary_navigation, nil) - expect(config).not_to be_loaded + context 'when items_provider is not specified' do + it "raises an exception" do + expect{ config.items }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') end end end end + + describe '#loaded?' do + context 'when primary_nav is set' do + it 'returns true' do + config.instance_variable_set(:@primary_navigation, :bla) + expect(config).to be_loaded + end + end + + context 'when primary_nav is not set' do + it 'returns false if no primary_nav is set' do + config.instance_variable_set(:@primary_navigation, nil) + expect(config).not_to be_loaded + end + end + end end diff --git a/spec/simple_navigation/helpers_spec.rb b/spec/simple_navigation/helpers_spec.rb index 85b8ff50..2fbf1213 100644 --- a/spec/simple_navigation/helpers_spec.rb +++ b/spec/simple_navigation/helpers_spec.rb @@ -1,34 +1,57 @@ -module SimpleNavigation - describe Helpers do - subject(:controller) { test_controller_class.new } - - let(:invoices_item) { navigation[:invoices] } - let(:item) { nil } - let(:navigation) { setup_navigation('nav_id', 'nav_class') } - let(:test_controller_class) do - Class.new { include SimpleNavigation::Helpers } - end - let(:unpaid_item) { invoices_item.sub_navigation[:unpaid] } +RSpec.describe SimpleNavigation::Helpers do + subject(:controller) { test_controller_class.new } + + let(:invoices_item) { navigation[:invoices] } + let(:item) { nil } + let(:navigation) { setup_navigation('nav_id', 'nav_class') } + let(:test_controller_class) do + Class.new { include SimpleNavigation::Helpers } + end + let(:unpaid_item) { invoices_item.sub_navigation[:unpaid] } + + before do + allow(SimpleNavigation::Configuration).to receive(:eval_config) + allow(SimpleNavigation).to receive_messages(load_config: nil, + primary_navigation: navigation, + config_file?: true, + context_for_eval: controller) + + select_an_item(navigation[item]) if item + end - before do - allow(Configuration).to receive(:eval_config) - allow(SimpleNavigation).to receive_messages(load_config: nil, - primary_navigation: navigation, - config_file?: true, - context_for_eval: controller) + describe '#active_navigation_item_name' do + context 'when no item is selected' do + it 'returns an empty string for no parameters' do + expect(controller.active_navigation_item_name).to eq '' + end + + it "returns an empty string for level: 1" do + item_name = controller.active_navigation_item_name(level: 1) + expect(item_name).to eq '' + end + + it 'returns an empty string for level: 2' do + item_name = controller.active_navigation_item_name(level: 2) + expect(item_name).to eq '' + end - select_an_item(navigation[item]) if item + it 'returns an empty string for level: :all' do + item_name = controller.active_navigation_item_name(level: :all) + expect(item_name).to eq '' + end end - describe '#active_navigation_item_name' do - context 'when no item is selected' do - it 'returns an empty string for no parameters' do + context 'when an item is selected' do + context "and it's a primary item" do + let(:item) { :invoices } + + it 'returns an empty string' do expect(controller.active_navigation_item_name).to eq '' end - it "returns an empty string for level: 1" do + it "returns the selected item's name for level: 1" do item_name = controller.active_navigation_item_name(level: 1) - expect(item_name).to eq '' + expect(item_name).to eq 'Invoices' end it 'returns an empty string for level: 2' do @@ -42,71 +65,71 @@ module SimpleNavigation end end - context 'when an item is selected' do - context "and it's a primary item" do - let(:item) { :invoices } + context "and it's a sub navigation item" do + before do + select_an_item(invoices_item) + select_an_item(unpaid_item) + end - it 'returns an empty string' do - expect(controller.active_navigation_item_name).to eq '' - end + it "returns the selected item's name" do + expect(controller.active_navigation_item_name).to eq 'Unpaid' + end - it "returns the selected item's name for level: 1" do - item_name = controller.active_navigation_item_name(level: 1) - expect(item_name).to eq 'Invoices' - end + it "returns the selected item's parent name for level: 1" do + item_name = controller.active_navigation_item_name(level: 1) + expect(item_name).to eq 'Invoices' + end - it 'returns an empty string for level: 2' do - item_name = controller.active_navigation_item_name(level: 2) - expect(item_name).to eq '' - end + it "returns the selected item's name for level: 2" do + item_name = controller.active_navigation_item_name(level: 2) + expect(item_name).to eq 'Unpaid' + end - it 'returns an empty string for level: :all' do - item_name = controller.active_navigation_item_name(level: :all) - expect(item_name).to eq '' - end + it "returns the selected item's name for level: :all" do + item_name = controller.active_navigation_item_name(level: :all) + expect(item_name).to eq 'Unpaid' end + end + end + end - context "and it's a sub navigation item" do - before do - select_an_item(invoices_item) - select_an_item(unpaid_item) - end + describe '#active_navigation_item_key' do + context 'when no item is selected' do + it 'returns nil' do + expect(controller.active_navigation_item_key).to be_nil + end - it "returns the selected item's name" do - expect(controller.active_navigation_item_name).to eq 'Unpaid' - end + it 'returns nil for no parameters' do + expect(controller.active_navigation_item_key).to be_nil + end - it "returns the selected item's parent name for level: 1" do - item_name = controller.active_navigation_item_name(level: 1) - expect(item_name).to eq 'Invoices' - end + it "returns nil for level: 1" do + item_key = controller.active_navigation_item_key(level: 1) + expect(item_key).to be_nil + end - it "returns the selected item's name for level: 2" do - item_name = controller.active_navigation_item_name(level: 2) - expect(item_name).to eq 'Unpaid' - end + it 'returns nil for level: 2' do + item_key = controller.active_navigation_item_key(level: 2) + expect(item_key).to be_nil + end - it "returns the selected item's name for level: :all" do - item_name = controller.active_navigation_item_name(level: :all) - expect(item_name).to eq 'Unpaid' - end - end + it 'returns nil for level: :all' do + item_key = controller.active_navigation_item_key(level: :all) + expect(item_key).to be_nil end end - describe '#active_navigation_item_key' do - context 'when no item is selected' do - it 'returns nil' do - expect(controller.active_navigation_item_key).to be_nil - end + context 'when an item is selected' do + context "and it's a primary item" do + let(:item) { :invoices } it 'returns nil for no parameters' do expect(controller.active_navigation_item_key).to be_nil end - it "returns nil for level: 1" do + it "returns the selected item's name for level: 1" do item_key = controller.active_navigation_item_key(level: 1) - expect(item_key).to be_nil + expect(item_key).to eq :invoices end it 'returns nil for level: 2' do @@ -120,67 +143,67 @@ module SimpleNavigation end end - context 'when an item is selected' do - context "and it's a primary item" do - let(:item) { :invoices } - - it 'returns nil for no parameters' do - expect(controller.active_navigation_item_key).to be_nil - end + context "and it's a sub navigation item" do + before do + select_an_item(invoices_item) + select_an_item(unpaid_item) + end - it "returns the selected item's name for level: 1" do - item_key = controller.active_navigation_item_key(level: 1) - expect(item_key).to eq :invoices - end + it "returns the selected item's name" do + expect(controller.active_navigation_item_key).to eq :unpaid + end - it 'returns nil for level: 2' do - item_key = controller.active_navigation_item_key(level: 2) - expect(item_key).to be_nil - end + it "returns the selected item's parent name for level: 1" do + item_key = controller.active_navigation_item_key(level: 1) + expect(item_key).to eq :invoices + end - it 'returns nil for level: :all' do - item_key = controller.active_navigation_item_key(level: :all) - expect(item_key).to be_nil - end + it "returns the selected item's name for level: 2" do + item_key = controller.active_navigation_item_key(level: 2) + expect(item_key).to eq :unpaid end - context "and it's a sub navigation item" do - before do - select_an_item(invoices_item) - select_an_item(unpaid_item) - end + it "returns the selected item's name for level: :all" do + item_key = controller.active_navigation_item_key(level: :all) + expect(item_key).to eq :unpaid + end + end + end + end - it "returns the selected item's name" do - expect(controller.active_navigation_item_key).to eq :unpaid - end + describe '#active_navigation_item' do + context 'when no item is selected' do + it 'returns nil for no parameters' do + expect(controller.active_navigation_item).to be_nil + end - it "returns the selected item's parent name for level: 1" do - item_key = controller.active_navigation_item_key(level: 1) - expect(item_key).to eq :invoices - end + it "returns nil for level: 1" do + item_key = controller.active_navigation_item(level: 1) + expect(item_key).to be_nil + end - it "returns the selected item's name for level: 2" do - item_key = controller.active_navigation_item_key(level: 2) - expect(item_key).to eq :unpaid - end + it 'returns nil for level: 2' do + item_key = controller.active_navigation_item(level: 2) + expect(item_key).to be_nil + end - it "returns the selected item's name for level: :all" do - item_key = controller.active_navigation_item_key(level: :all) - expect(item_key).to eq :unpaid - end - end + it 'returns nil for level: :all' do + item_key = controller.active_navigation_item(level: :all) + expect(item_key).to be_nil end end - describe '#active_navigation_item' do - context 'when no item is selected' do + context 'when an item is selected' do + context "and it's a primary item" do + let(:item) { :invoices } + it 'returns nil for no parameters' do expect(controller.active_navigation_item).to be_nil end - it "returns nil for level: 1" do + it "returns the selected item's name for level: 1" do item_key = controller.active_navigation_item(level: 1) - expect(item_key).to be_nil + expect(item_key).to be invoices_item end it 'returns nil for level: 2' do @@ -194,235 +217,210 @@ module SimpleNavigation end end - context 'when an item is selected' do - context "and it's a primary item" do - let(:item) { :invoices } + context "and it's a sub navigation item" do + before do + select_an_item(invoices_item) + select_an_item(unpaid_item) + end - it 'returns nil for no parameters' do - expect(controller.active_navigation_item).to be_nil - end + it "returns the selected item's name for no parameters" do + expect(controller.active_navigation_item).to be unpaid_item + end - it "returns the selected item's name for level: 1" do - item_key = controller.active_navigation_item(level: 1) - expect(item_key).to be invoices_item - end + it "returns the selected item's parent name for level: 1" do + item_key = controller.active_navigation_item(level: 1) + expect(item_key).to be invoices_item + end - it 'returns nil for level: 2' do - item_key = controller.active_navigation_item(level: 2) - expect(item_key).to be_nil - end + it "returns the selected item's name for level: 2" do + item_key = controller.active_navigation_item(level: 2) + expect(item_key).to eq unpaid_item + end - it 'returns nil for level: :all' do - item_key = controller.active_navigation_item(level: :all) - expect(item_key).to be_nil - end + it "returns the selected item's name for level: :all" do + item_key = controller.active_navigation_item(level: :all) + expect(item_key).to eq unpaid_item end + end + end + end - context "and it's a sub navigation item" do - before do - select_an_item(invoices_item) - select_an_item(unpaid_item) - end + describe '#active_navigation_item_container' do + shared_examples 'returning items container' do + it 'returns the primary navigation for no parameters' do + expect(controller.active_navigation_item_container).to be navigation + end - it "returns the selected item's name for no parameters" do - expect(controller.active_navigation_item).to be unpaid_item - end + it "returns the primary navigation for level: 1" do + item_container = controller.active_navigation_item_container(level: 1) + expect(item_container).to be navigation + end - it "returns the selected item's parent name for level: 1" do - item_key = controller.active_navigation_item(level: 1) - expect(item_key).to be invoices_item - end + it 'returns the primary navigation level: :all' do + item_container = + controller.active_navigation_item_container(level: :all) + expect(item_container).to be navigation + end + end - it "returns the selected item's name for level: 2" do - item_key = controller.active_navigation_item(level: 2) - expect(item_key).to eq unpaid_item - end + context 'when no item is selected' do + it_behaves_like 'returning items container' - it "returns the selected item's name for level: :all" do - item_key = controller.active_navigation_item(level: :all) - expect(item_key).to eq unpaid_item - end - end + it 'returns nil for level: 2' do + item_container = controller.active_navigation_item_container(level: 2) + expect(item_container).to be_nil end end - describe '#active_navigation_item_container' do - shared_examples 'returning items container' do - it 'returns the primary navigation for no parameters' do - expect(controller.active_navigation_item_container).to be navigation - end + context 'when an item is selected' do + context "and it's a primary item" do + let(:item) { :invoices } - it "returns the primary navigation for level: 1" do - item_container = controller.active_navigation_item_container(level: 1) - expect(item_container).to be navigation - end + it_behaves_like 'returning items container' - it 'returns the primary navigation level: :all' do + it 'returns the invoices items container for level: 2' do item_container = - controller.active_navigation_item_container(level: :all) - expect(item_container).to be navigation + controller.active_navigation_item_container(level: 2) + expect(item_container).to be invoices_item.sub_navigation end end - context 'when no item is selected' do - it_behaves_like 'returning items container' - - it 'returns nil for level: 2' do - item_container = controller.active_navigation_item_container(level: 2) - expect(item_container).to be_nil + context "and it's a sub navigation item" do + before do + select_an_item(invoices_item) + select_an_item(unpaid_item) end - end - - context 'when an item is selected' do - context "and it's a primary item" do - let(:item) { :invoices } - it_behaves_like 'returning items container' + it_behaves_like 'returning items container' - it 'returns the invoices items container for level: 2' do - item_container = - controller.active_navigation_item_container(level: 2) - expect(item_container).to be invoices_item.sub_navigation - end + it 'returns the invoices items container for level: 2' do + item_container = + controller.active_navigation_item_container(level: 2) + expect(item_container).to be invoices_item.sub_navigation end + end + end + end - context "and it's a sub navigation item" do - before do - select_an_item(invoices_item) - select_an_item(unpaid_item) - end + describe '#render_navigation' do + it 'evaluates the configuration on every request' do + expect(SimpleNavigation).to receive(:load_config).twice + 2.times { controller.render_navigation } + end - it_behaves_like 'returning items container' + it 'loads the :default configuration' do + expect(SimpleNavigation).to receive(:load_config).with(:default) + controller.render_navigation + end - it 'returns the invoices items container for level: 2' do - item_container = - controller.active_navigation_item_container(level: 2) - expect(item_container).to be invoices_item.sub_navigation - end - end - end + it "doesn't set the items directly" do + expect(SimpleNavigation.config).not_to receive(:items) + controller.render_navigation end - describe '#render_navigation' do - it 'evaluates the configuration on every request' do - expect(SimpleNavigation).to receive(:load_config).twice - 2.times { controller.render_navigation } - end + it 'looks up the active_item_container based on the level' do + expect(SimpleNavigation).to receive(:active_item_container_for) + .with(:all) + controller.render_navigation + end - it 'loads the :default configuration' do - expect(SimpleNavigation).to receive(:load_config).with(:default) - controller.render_navigation + context 'when the :context option is specified' do + it 'loads the configuration for the specified context' do + expect(SimpleNavigation).to receive(:load_config).with(:my_context) + controller.render_navigation(context: :my_context) end + end - it "doesn't set the items directly" do - expect(SimpleNavigation.config).not_to receive(:items) - controller.render_navigation - end + context 'when the :items option is specified' do + let(:items) { double(:items) } - it 'looks up the active_item_container based on the level' do - expect(SimpleNavigation).to receive(:active_item_container_for) - .with(:all) - controller.render_navigation + it 'sets the items directly' do + expect(SimpleNavigation.config).to receive(:items).with(items) + controller.render_navigation(items: items) end + end - context 'when the :context option is specified' do - it 'loads the configuration for the specified context' do - expect(SimpleNavigation).to receive(:load_config).with(:my_context) - controller.render_navigation(context: :my_context) + context 'when the :level option is set' do + context 'and its value is 1' do + it 'calls render on the primary navigation' do + expect(navigation).to receive(:render).with({level: 1}) + controller.render_navigation(level: 1) end end - context 'when the :items option is specified' do - let(:items) { double(:items) } + context 'and its value is 2' do + context 'and the active_item_container is set' do + let(:item_container) { double(:container).as_null_object } - it 'sets the items directly' do - expect(SimpleNavigation.config).to receive(:items).with(items) - controller.render_navigation(items: items) - end - end - - context 'when the :level option is set' do - context 'and its value is 1' do - it 'calls render on the primary navigation' do - expect(navigation).to receive(:render).with({level: 1}) - controller.render_navigation(level: 1) + before do + allow(SimpleNavigation).to receive_messages(active_item_container_for: item_container) end - end - - context 'and its value is 2' do - context 'and the active_item_container is set' do - let(:item_container) { double(:container).as_null_object } - - before do - allow(SimpleNavigation).to receive_messages(active_item_container_for: item_container) - end - - it 'finds the selected sub navigation for the specified level' do - expect(SimpleNavigation).to receive(:active_item_container_for) - .with(2) - controller.render_navigation(level: 2) - end - it 'calls render on the active item_container' do - expect(item_container).to receive(:render).with({level: 2}) - controller.render_navigation(level: 2) - end + it 'finds the selected sub navigation for the specified level' do + expect(SimpleNavigation).to receive(:active_item_container_for) + .with(2) + controller.render_navigation(level: 2) end - context "and the active_item_container isn't set" do - it "doesn't raise an exception" do - expect{ - controller.render_navigation(level: 2) - }.not_to raise_error - end + it 'calls render on the active item_container' do + expect(item_container).to receive(:render).with({level: 2}) + controller.render_navigation(level: 2) end end - context "and its value isn't a valid level" do - it 'raises an exception' do + context "and the active_item_container isn't set" do + it "doesn't raise an exception" do expect{ - controller.render_navigation(level: :invalid) - }.to raise_error(ArgumentError, 'Invalid navigation level: invalid') + controller.render_navigation(level: 2) + }.not_to raise_error end end end - context 'when the :levels option is set' do - before { allow(SimpleNavigation).to receive_messages(active_item_container_for: navigation) } - - it 'treats it like the :level option' do - expect(navigation).to receive(:render).with({level: 2}) - controller.render_navigation(levels: 2) + context "and its value isn't a valid level" do + it 'raises an exception' do + expect{ + controller.render_navigation(level: :invalid) + }.to raise_error(ArgumentError, 'Invalid navigation level: invalid') end end + end - context 'when a block is given' do - it 'calls the block passing it an item container' do - expect{ |blk| - controller.render_navigation(&blk) - }.to yield_with_args(ItemContainer) - end + context 'when the :levels option is set' do + before { allow(SimpleNavigation).to receive_messages(active_item_container_for: navigation) } + + it 'treats it like the :level option' do + expect(navigation).to receive(:render).with({level: 2}) + controller.render_navigation(levels: 2) end + end - context 'when no primary configuration is defined' do - before { allow(SimpleNavigation).to receive_messages(primary_navigation: nil) } + context 'when a block is given' do + it 'calls the block passing it an item container' do + expect{ |blk| + controller.render_navigation(&blk) + }.to yield_with_args(SimpleNavigation::ItemContainer) + end + end - it 'raises an exception' do - expect{controller.render_navigation}.to raise_error(RuntimeError, 'no primary navigation defined, either use a navigation config file or pass items directly to render_navigation') - end + context 'when no primary configuration is defined' do + before { allow(SimpleNavigation).to receive_messages(primary_navigation: nil) } + + it 'raises an exception' do + expect{controller.render_navigation}.to raise_error(RuntimeError, 'no primary navigation defined, either use a navigation config file or pass items directly to render_navigation') end + end - context "when active_item_container is set" do - let(:active_item_container) { double(:container).as_null_object } + context "when active_item_container is set" do + let(:active_item_container) { double(:container).as_null_object } - before do - allow(SimpleNavigation).to receive_messages(active_item_container_for: active_item_container) - end + before do + allow(SimpleNavigation).to receive_messages(active_item_container_for: active_item_container) + end - it 'calls render on the active_item_container' do - expect(active_item_container).to receive(:render) - controller.render_navigation - end + it 'calls render on the active_item_container' do + expect(active_item_container).to receive(:render) + controller.render_navigation end end end diff --git a/spec/simple_navigation/item_adapter_spec.rb b/spec/simple_navigation/item_adapter_spec.rb index db672712..a54c3505 100644 --- a/spec/simple_navigation/item_adapter_spec.rb +++ b/spec/simple_navigation/item_adapter_spec.rb @@ -1,187 +1,185 @@ -module SimpleNavigation - describe ItemAdapter do - let(:item_adapter) { ItemAdapter.new(item) } +RSpec.describe SimpleNavigation::ItemAdapter do + let(:item_adapter) { SimpleNavigation::ItemAdapter.new(item) } - context 'when item is an object' do - let(:item) { double(:item, key: 'key', name: 'name', url: 'url') } + context 'when item is an object' do + let(:item) { double(:item, key: 'key', name: 'name', url: 'url') } - shared_examples 'delegating to item' do |meth| - it "delegates #{meth} to item" do - expect(item).to receive(meth) - item_adapter.public_send(meth) - end + shared_examples 'delegating to item' do |meth| + it "delegates #{meth} to item" do + expect(item).to receive(meth) + item_adapter.public_send(meth) end + end - it_behaves_like 'delegating to item', :key - it_behaves_like 'delegating to item', :url - it_behaves_like 'delegating to item', :name + it_behaves_like 'delegating to item', :key + it_behaves_like 'delegating to item', :url + it_behaves_like 'delegating to item', :name - describe '#initialize' do - it 'sets the item' do - expect(item_adapter.item).to be item - end + describe '#initialize' do + it 'sets the item' do + expect(item_adapter.item).to be item end + end - describe '#options' do - context 'when item responds to options' do - let(:options) { double(:options) } + describe '#options' do + context 'when item responds to options' do + let(:options) { double(:options) } - before { allow(item).to receive_messages(options: options) } + before { allow(item).to receive_messages(options: options) } - it "returns the item's options" do - expect(item_adapter.options).to be options - end + it "returns the item's options" do + expect(item_adapter.options).to be options end + end - context 'item does not respond to options' do - it 'returns an empty hash' do - expect(item_adapter.options).to eq({}) - end + context 'item does not respond to options' do + it 'returns an empty hash' do + expect(item_adapter.options).to eq({}) end end + end - describe '#items' do - context 'when item responds to items' do - context 'and items is nil' do - before { allow(item).to receive_messages(items: nil) } + describe '#items' do + context 'when item responds to items' do + context 'and items is nil' do + before { allow(item).to receive_messages(items: nil) } - it 'returns nil' do - expect(item_adapter.items).to be_nil - end + it 'returns nil' do + expect(item_adapter.items).to be_nil end + end - context 'when items is not nil' do - context 'and items is empty' do - before { allow(item).to receive_messages(items: []) } + context 'when items is not nil' do + context 'and items is empty' do + before { allow(item).to receive_messages(items: []) } - it 'returns nil' do - expect(item_adapter.items).to be_nil - end + it 'returns nil' do + expect(item_adapter.items).to be_nil end + end - context 'and items is not empty' do - let(:items) { [true] } + context 'and items is not empty' do + let(:items) { [true] } - before { allow(item).to receive_messages(items: items) } + before { allow(item).to receive_messages(items: items) } - it 'returns the items' do - expect(item_adapter.items).to eq items - end + it 'returns the items' do + expect(item_adapter.items).to eq items end end end + end - context "when item doesn't respond to items" do - it 'returns nil' do - expect(item_adapter.items).to be_nil - end + context "when item doesn't respond to items" do + it 'returns nil' do + expect(item_adapter.items).to be_nil end end + end - describe '#to_simple_navigation_item' do - let(:container) { double(:container) } + describe '#to_simple_navigation_item' do + let(:container) { double(:container) } - before { allow(item).to receive_messages(items: [], options: {}) } + before { allow(item).to receive_messages(items: [], options: {}) } - it 'creates an Item' do - expect(Item).to receive(:new) - .with(container, 'key', 'name', 'url', {}) - item_adapter.to_simple_navigation_item(container) - end + it 'creates an Item' do + expect(SimpleNavigation::Item).to receive(:new) + .with(container, 'key', 'name', 'url', {}) + item_adapter.to_simple_navigation_item(container) end end + end - context 'when item is a kind of hash' do - class ModifiedHash < Hash; end + context 'when item is a kind of hash' do + class ModifiedHash < Hash; end - let(:item) { ModifiedHash[key: 'key', url: 'url', name: 'name'] } + let(:item) { ModifiedHash[key: 'key', url: 'url', name: 'name'] } - shared_examples 'delegating to item' do |meth| - it "delegates #{meth} to item" do - expect(item_adapter.item).to receive(meth) - item_adapter.public_send(meth) - end + shared_examples 'delegating to item' do |meth| + it "delegates #{meth} to item" do + expect(item_adapter.item).to receive(meth) + item_adapter.public_send(meth) end + end - it_behaves_like 'delegating to item', :key - it_behaves_like 'delegating to item', :url - it_behaves_like 'delegating to item', :name + it_behaves_like 'delegating to item', :key + it_behaves_like 'delegating to item', :url + it_behaves_like 'delegating to item', :name - describe '#initialize' do - it 'sets the item' do - expect(item_adapter.item).not_to be_nil - end + describe '#initialize' do + it 'sets the item' do + expect(item_adapter.item).not_to be_nil + end - it 'converts the item into an object' do - expect(item_adapter.item).to respond_to(:url) - end + it 'converts the item into an object' do + expect(item_adapter.item).to respond_to(:url) end + end - describe '#options' do - context 'when item responds to options' do - before { item[:options] = { my: :options } } + describe '#options' do + context 'when item responds to options' do + before { item[:options] = { my: :options } } - it "returns the item's options" do - expect(item_adapter.options).to eq({ my: :options }) - end + it "returns the item's options" do + expect(item_adapter.options).to eq({ my: :options }) end + end - context 'when item does not respond to options' do - it 'returns an empty hash' do - expect(item_adapter.options).to eq({}) - end + context 'when item does not respond to options' do + it 'returns an empty hash' do + expect(item_adapter.options).to eq({}) end end + end - describe '#items' do - context 'when item responds to items' do - context 'and items is nil' do - before { item[:items] = nil } + describe '#items' do + context 'when item responds to items' do + context 'and items is nil' do + before { item[:items] = nil } + + it 'returns nil' do + expect(item_adapter.items).to be_nil + end + end + context 'when items is not nil' do + context 'and items is empty' do it 'returns nil' do expect(item_adapter.items).to be_nil end end - context 'when items is not nil' do - context 'and items is empty' do - it 'returns nil' do - expect(item_adapter.items).to be_nil - end - end - - context 'and items is not empty' do - before { item[:items] = ['not', 'empty'] } + context 'and items is not empty' do + before { item[:items] = ['not', 'empty'] } - it 'returns the items' do - expect(item_adapter.items).to eq ['not', 'empty'] - end + it 'returns the items' do + expect(item_adapter.items).to eq ['not', 'empty'] end end end + end - context 'when item does not respond to items' do - it 'returns nil' do - expect(item_adapter.items).to be_nil - end + context 'when item does not respond to items' do + it 'returns nil' do + expect(item_adapter.items).to be_nil end end + end - describe '#to_simple_navigation_item' do - let(:container) { double(:container) } + describe '#to_simple_navigation_item' do + let(:container) { double(:container) } - before { item.merge(options: {}) } + before { item.merge(options: {}) } - it 'passes the right arguments to Item' do - expect(Item).to receive(:new) - .with(container, 'key', 'name', 'url', {}) - item_adapter.to_simple_navigation_item(container) - end + it 'passes the right arguments to Item' do + expect(SimpleNavigation::Item).to receive(:new) + .with(container, 'key', 'name', 'url', {}) + item_adapter.to_simple_navigation_item(container) + end - it 'creates an Item' do - created_item = item_adapter.to_simple_navigation_item(container) - expect(created_item).to be_an(Item) - end + it 'creates an Item' do + created_item = item_adapter.to_simple_navigation_item(container) + expect(created_item).to be_an(SimpleNavigation::Item) end end end diff --git a/spec/simple_navigation/item_container_spec.rb b/spec/simple_navigation/item_container_spec.rb index a6aa7794..464946c0 100644 --- a/spec/simple_navigation/item_container_spec.rb +++ b/spec/simple_navigation/item_container_spec.rb @@ -1,599 +1,597 @@ -module SimpleNavigation - describe ItemContainer do - subject(:item_container) { ItemContainer.new } - - shared_examples 'adding the item to the list' do - it 'adds the item to the list' do - allow(Item).to receive_messages(new: item) - item_container.item(*args) - expect(item_container.items).to include(item) - end +RSpec.describe SimpleNavigation::ItemContainer do + subject(:item_container) { SimpleNavigation::ItemContainer.new } + + shared_examples 'adding the item to the list' do + it 'adds the item to the list' do + allow(SimpleNavigation::Item).to receive_messages(new: item) + item_container.item(*args) + expect(item_container.items).to include(item) end + end - shared_examples 'not adding the item to the list' do - it "doesn't add the item to the list" do - allow(Item).to receive_messages(new: item) - item_container.item(*args) - expect(item_container.items).not_to include(item) - end + shared_examples 'not adding the item to the list' do + it "doesn't add the item to the list" do + allow(SimpleNavigation::Item).to receive_messages(new: item) + item_container.item(*args) + expect(item_container.items).not_to include(item) end + end - describe '#initialize' do - it 'sets an empty items array' do - expect(item_container.items).to be_empty - end + describe '#initialize' do + it 'sets an empty items array' do + expect(item_container.items).to be_empty end + end - describe '#dom_attributes' do - let(:dom_attributes) {{ id: 'test_id', class: 'test_class' }} + describe '#dom_attributes' do + let(:dom_attributes) {{ id: 'test_id', class: 'test_class' }} - before { item_container.dom_attributes = dom_attributes } + before { item_container.dom_attributes = dom_attributes } - it "returns the container's dom_attributes" do - expect(item_container.dom_attributes).to eq dom_attributes - end + it "returns the container's dom_attributes" do + expect(item_container.dom_attributes).to eq dom_attributes + end - context 'when the dom_attributes do not contain any id or class' do - let(:dom_attributes) {{ test: 'test' }} + context 'when the dom_attributes do not contain any id or class' do + let(:dom_attributes) {{ test: 'test' }} - context "and the container hasn't any dom_id" do - it "returns the contaier's dom_attributes without any id" do - expect(item_container.dom_attributes).not_to include(:id) - end + context "and the container hasn't any dom_id" do + it "returns the contaier's dom_attributes without any id" do + expect(item_container.dom_attributes).not_to include(:id) end + end - context 'and the container has a dom_id' do - before { item_container.dom_id = 'test_id' } + context 'and the container has a dom_id' do + before { item_container.dom_id = 'test_id' } - it "returns the contaier's dom_attributes including the #dom_id" do - expect(item_container.dom_attributes).to include(id: 'test_id') - end + it "returns the contaier's dom_attributes including the #dom_id" do + expect(item_container.dom_attributes).to include(id: 'test_id') end + end - context "and the container hasn't any dom_class" do - it "returns the contaier's dom_attributes without any class" do - expect(item_container.dom_attributes).not_to include(:class) - end + context "and the container hasn't any dom_class" do + it "returns the contaier's dom_attributes without any class" do + expect(item_container.dom_attributes).not_to include(:class) end + end - context 'and the container has a dom_class' do - before { item_container.dom_class = 'test_class' } + context 'and the container has a dom_class' do + before { item_container.dom_class = 'test_class' } - it "returns the contaier's dom_attributes including the #dom_class" do - expect(item_container.dom_attributes).to include(class: 'test_class') - end + it "returns the contaier's dom_attributes including the #dom_class" do + expect(item_container.dom_attributes).to include(class: 'test_class') end end end + end - describe '#items=' do - let(:item) {{ key: :my_key, name: 'test', url: '/' }} - let(:items) { [item] } - let(:item_adapter) { double(:item_adapter).as_null_object } - let(:real_item) { double(:real_item) } - - before do - allow(ItemAdapter).to receive_messages(new: item_adapter) - allow(item_adapter).to receive(:to_simple_navigation_item) - .with(item_container) - .and_return(real_item) - end + describe '#items=' do + let(:item) {{ key: :my_key, name: 'test', url: '/' }} + let(:items) { [item] } + let(:item_adapter) { double(:item_adapter).as_null_object } + let(:real_item) { double(:real_item) } + + before do + allow(SimpleNavigation::ItemAdapter).to receive_messages(new: item_adapter) + allow(item_adapter).to receive(:to_simple_navigation_item) + .with(item_container) + .and_return(real_item) + end - context 'when the item should be added' do - before { allow(item_container).to receive_messages(should_add_item?: true) } + context 'when the item should be added' do + before { allow(item_container).to receive_messages(should_add_item?: true) } - it 'converts it to an Item and adds it to the items collection' do - item_container.items = items - expect(item_container.items).to include(real_item) - end + it 'converts it to an Item and adds it to the items collection' do + item_container.items = items + expect(item_container.items).to include(real_item) end + end - context 'when the item should not be added' do - before { allow(item_container).to receive_messages(should_add_item?: false) } + context 'when the item should not be added' do + before { allow(item_container).to receive_messages(should_add_item?: false) } - it "doesn't add it to the items collection" do - item_container.items = items - expect(item_container.items).not_to include(real_item) - end + it "doesn't add it to the items collection" do + item_container.items = items + expect(item_container.items).not_to include(real_item) end end + end - describe '#selected?' do - let(:item_1) { double(:item, selected?: false) } - let(:item_2) { double(:item, selected?: false) } + describe '#selected?' do + let(:item_1) { double(:item, selected?: false) } + let(:item_2) { double(:item, selected?: false) } - before do - item_container.instance_variable_set(:@items, [item_1, item_2]) - end + before do + item_container.instance_variable_set(:@items, [item_1, item_2]) + end - context 'when no item is selected' do - it 'returns nil' do - expect(item_container).not_to be_selected - end + context 'when no item is selected' do + it 'returns nil' do + expect(item_container).not_to be_selected end + end - context 'when an item is selected' do - it 'returns true' do - allow(item_1).to receive_messages(selected?: true) - expect(item_container).to be_selected - end + context 'when an item is selected' do + it 'returns true' do + allow(item_1).to receive_messages(selected?: true) + expect(item_container).to be_selected end end + end - describe '#selected_item' do - let(:item_1) { double(:item, selected?: false) } - let(:item_2) { double(:item, selected?: false) } + describe '#selected_item' do + let(:item_1) { double(:item, selected?: false) } + let(:item_2) { double(:item, selected?: false) } - before(:each) do - allow(SimpleNavigation).to receive_messages(current_navigation_for: :nav) - allow(item_container).to receive_messages(:[] => nil) - item_container.instance_variable_set(:@items, [item_1, item_2]) - end + before(:each) do + allow(SimpleNavigation).to receive_messages(current_navigation_for: :nav) + allow(item_container).to receive_messages(:[] => nil) + item_container.instance_variable_set(:@items, [item_1, item_2]) + end - context "when navigation isn't explicitely set" do - context 'and no item is selected' do - it 'returns nil' do - expect(item_container.selected_item).to be_nil - end + context "when navigation isn't explicitely set" do + context 'and no item is selected' do + it 'returns nil' do + expect(item_container.selected_item).to be_nil end + end - context 'and an item selected' do - before { allow(item_1).to receive_messages(selected?: true) } + context 'and an item selected' do + before { allow(item_1).to receive_messages(selected?: true) } - it 'returns the selected item' do - expect(item_container.selected_item).to be item_1 - end + it 'returns the selected item' do + expect(item_container.selected_item).to be item_1 end end end + end - describe '#active_item_container_for' do - context "when the desired level is the same as the container's" do - it 'returns the container itself' do - expect(item_container.active_item_container_for(1)).to be item_container - end + describe '#active_item_container_for' do + context "when the desired level is the same as the container's" do + it 'returns the container itself' do + expect(item_container.active_item_container_for(1)).to be item_container end + end - context "when the desired level is different than the container's" do - context 'and no subnavigation is selected' do - before { allow(item_container).to receive_messages(selected_sub_navigation?: false) } - - it 'returns nil' do - expect(item_container.active_item_container_for(2)).to be_nil - end - end - - context 'and a subnavigation is selected' do - let(:sub_navigation) { double(:sub_navigation) } - let(:selected_item) { double(:selected_item) } - - before do - allow(item_container).to \ - receive_messages(selected_sub_navigation?: true, selected_item: selected_item) - allow(selected_item).to receive_messages(sub_navigation: sub_navigation) - end + context "when the desired level is different than the container's" do + context 'and no subnavigation is selected' do + before { allow(item_container).to receive_messages(selected_sub_navigation?: false) } - it 'calls recursively on the sub_navigation' do - expect(sub_navigation).to receive(:active_item_container_for) - .with(2) - item_container.active_item_container_for(2) - end + it 'returns nil' do + expect(item_container.active_item_container_for(2)).to be_nil end end - end - describe '#active_leaf_container' do - context 'when the current container has a selected subnavigation' do + context 'and a subnavigation is selected' do let(:sub_navigation) { double(:sub_navigation) } let(:selected_item) { double(:selected_item) } before do - allow(item_container).to receive_messages(selected_sub_navigation?: true, - selected_item: selected_item) + allow(item_container).to \ + receive_messages(selected_sub_navigation?: true, selected_item: selected_item) allow(selected_item).to receive_messages(sub_navigation: sub_navigation) end it 'calls recursively on the sub_navigation' do - expect(sub_navigation).to receive(:active_leaf_container) - item_container.active_leaf_container + expect(sub_navigation).to receive(:active_item_container_for) + .with(2) + item_container.active_item_container_for(2) end end + end + end - context 'when the current container is the leaf already' do - before { allow(item_container).to receive_messages(selected_sub_navigation?: false) } + describe '#active_leaf_container' do + context 'when the current container has a selected subnavigation' do + let(:sub_navigation) { double(:sub_navigation) } + let(:selected_item) { double(:selected_item) } - it 'returns itsself' do - expect(item_container.active_leaf_container).to be item_container - end + before do + allow(item_container).to receive_messages(selected_sub_navigation?: true, + selected_item: selected_item) + allow(selected_item).to receive_messages(sub_navigation: sub_navigation) + end + + it 'calls recursively on the sub_navigation' do + expect(sub_navigation).to receive(:active_leaf_container) + item_container.active_leaf_container end end - describe '#item' do - let(:options) { Hash.new } - let(:item) { double(:item) } + context 'when the current container is the leaf already' do + before { allow(item_container).to receive_messages(selected_sub_navigation?: false) } + + it 'returns itsself' do + expect(item_container.active_leaf_container).to be item_container + end + end + end - context 'when a block is given' do - let(:block) { proc{} } - let(:sub_container) { double(:sub_container) } + describe '#item' do + let(:options) { Hash.new } + let(:item) { double(:item) } - it 'yields a new ItemContainer' do - allow_any_instance_of(Item).to \ - receive_messages(sub_navigation: sub_container) + context 'when a block is given' do + let(:block) { proc{} } + let(:sub_container) { double(:sub_container) } - expect{ |blk| - item_container.item('key', 'name', 'url', options, &blk) - }.to yield_with_args(sub_container) - end + it 'yields a new ItemContainer' do + allow_any_instance_of(SimpleNavigation::Item).to \ + receive_messages(sub_navigation: sub_container) - it "creates a new Item with the given params and block" do - allow(Item).to receive(:new) - .with(item_container, 'key', 'name', 'url', options, &block) - .and_return(item) - item_container.item('key', 'name', 'url', options, &block) - expect(item_container.items).to include(item) - end + expect{ |blk| + item_container.item('key', 'name', 'url', options, &blk) + }.to yield_with_args(sub_container) + end - it 'adds the created item to the list of items' do - item_container.item('key', 'name', 'url', options) {} - expect(item_container.items).not_to include(item) - end + it "creates a new Item with the given params and block" do + allow(SimpleNavigation::Item).to receive(:new) + .with(item_container, 'key', 'name', 'url', options, &block) + .and_return(item) + item_container.item('key', 'name', 'url', options, &block) + expect(item_container.items).to include(item) end - context 'when no block is given' do - it 'creates a new Item with the given params and no sub navigation' do - allow(Item).to receive(:new) - .with(item_container, 'key', 'name', 'url', options) - .and_return(item) - item_container.item('key', 'name', 'url', options) - expect(item_container.items).to include(item) - end + it 'adds the created item to the list of items' do + item_container.item('key', 'name', 'url', options) {} + expect(item_container.items).not_to include(item) + end + end - it 'adds the created item to the list of items' do - allow(Item).to receive_messages(new: item) - item_container.item('key', 'name', 'url', options) {} - expect(item_container.items).to include(item) + context 'when no block is given' do + it 'creates a new Item with the given params and no sub navigation' do + allow(SimpleNavigation::Item).to receive(:new) + .with(item_container, 'key', 'name', 'url', options) + .and_return(item) + item_container.item('key', 'name', 'url', options) + expect(item_container.items).to include(item) + end + + it 'adds the created item to the list of items' do + allow(SimpleNavigation::Item).to receive_messages(new: item) + item_container.item('key', 'name', 'url', options) {} + expect(item_container.items).to include(item) + end + end + + describe 'Optional url and optional options' do + context 'when item specifed without url or options' do + it_behaves_like 'adding the item to the list' do + let(:args) { ['key', 'name'] } end end - describe 'Optional url and optional options' do - context 'when item specifed without url or options' do - it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name'] } - end + context 'when item is specified with only a url' do + it_behaves_like 'adding the item to the list' do + let(:args) { ['key', 'name', 'url'] } end + end - context 'when item is specified with only a url' do + context 'when item is specified with only options' do + context 'and options do not contain any condition' do it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', 'url'] } + let(:args) { ['key', 'name', { option: true }] } end end - context 'when item is specified with only options' do - context 'and options do not contain any condition' do - it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', { option: true }] } - end - end - - context 'and options contains a negative condition' do - it_behaves_like 'not adding the item to the list' do - let(:args) { ['key', 'name', nil, { if: ->{ false }, option: true }] } - end + context 'and options contains a negative condition' do + it_behaves_like 'not adding the item to the list' do + let(:args) { ['key', 'name', nil, { if: ->{ false }, option: true }] } end + end - context 'and options contains a positive condition' do - it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', nil, { if: ->{ true }, option: true }] } - end + context 'and options contains a positive condition' do + it_behaves_like 'adding the item to the list' do + let(:args) { ['key', 'name', nil, { if: ->{ true }, option: true }] } end end + end - context 'when item is specified with a url and options' do - context 'and options do not contain any condition' do - it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', 'url', { option: true }] } - end + context 'when item is specified with a url and options' do + context 'and options do not contain any condition' do + it_behaves_like 'adding the item to the list' do + let(:args) { ['key', 'name', 'url', { option: true }] } end + end - context 'and options contains a negative condition' do - it_behaves_like 'not adding the item to the list' do - let(:args) { ['key', 'name', 'url', { if: ->{ false }, option: true }] } - end + context 'and options contains a negative condition' do + it_behaves_like 'not adding the item to the list' do + let(:args) { ['key', 'name', 'url', { if: ->{ false }, option: true }] } end + end - context 'and options contains a positive condition' do - it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', 'url', { if: ->{ true }, option: true }] } - end + context 'and options contains a positive condition' do + it_behaves_like 'adding the item to the list' do + let(:args) { ['key', 'name', 'url', { if: ->{ true }, option: true }] } end end + end - context 'when a frozen options hash is given' do - let(:options) do - { html: { id: 'test' } }.freeze - end + context 'when a frozen options hash is given' do + let(:options) do + { html: { id: 'test' } }.freeze + end - it 'does not raise an exception' do - expect{ - item_container.item('key', 'name', 'url', options) - }.not_to raise_error - end + it 'does not raise an exception' do + expect{ + item_container.item('key', 'name', 'url', options) + }.not_to raise_error end + end - describe "container options" do - before do - allow(item_container).to receive_messages(should_add_item?: add_item) - item_container.item :key, 'name', 'url', options - end + describe "container options" do + before do + allow(item_container).to receive_messages(should_add_item?: add_item) + item_container.item :key, 'name', 'url', options + end - context 'when the container :id option is specified' do - let(:options) {{ container: { id: 'c_id' } }} + context 'when the container :id option is specified' do + let(:options) {{ container: { id: 'c_id' } }} - context 'and the item should be added' do - let(:add_item) { true } + context 'and the item should be added' do + let(:add_item) { true } - it 'changes its dom_id' do - expect(item_container.dom_id).to eq 'c_id' - end + it 'changes its dom_id' do + expect(item_container.dom_id).to eq 'c_id' end + end - context "and the item shouldn't be added" do - let(:add_item) { false } + context "and the item shouldn't be added" do + let(:add_item) { false } - it "doesn't change its dom_id" do - expect(item_container.dom_id).to be_nil - end + it "doesn't change its dom_id" do + expect(item_container.dom_id).to be_nil end end + end - context 'when the container :class option is specified' do - let(:options) {{ container: { class: 'c_class' } }} + context 'when the container :class option is specified' do + let(:options) {{ container: { class: 'c_class' } }} - context 'and the item should be added' do - let(:add_item) { true } + context 'and the item should be added' do + let(:add_item) { true } - it 'changes its dom_class' do - expect(item_container.dom_class).to eq 'c_class' - end + it 'changes its dom_class' do + expect(item_container.dom_class).to eq 'c_class' end + end - context "and the item shouldn't be added" do - let(:add_item) { false } + context "and the item shouldn't be added" do + let(:add_item) { false } - it "doesn't change its dom_class" do - expect(item_container.dom_class).to be_nil - end + it "doesn't change its dom_class" do + expect(item_container.dom_class).to be_nil end end + end - context 'when the container :attributes option is specified' do - let(:options) {{ container: { attributes: { option: true } } }} + context 'when the container :attributes option is specified' do + let(:options) {{ container: { attributes: { option: true } } }} - context 'and the item should be added' do - let(:add_item) { true } + context 'and the item should be added' do + let(:add_item) { true } - it 'changes its dom_attributes' do - expect(item_container.dom_attributes).to eq(option: true) - end + it 'changes its dom_attributes' do + expect(item_container.dom_attributes).to eq(option: true) end + end - context "and the item shouldn't be added" do - let(:add_item) { false } + context "and the item shouldn't be added" do + let(:add_item) { false } - it "doesn't change its dom_attributes" do - expect(item_container.dom_attributes).to eq({}) - end + it "doesn't change its dom_attributes" do + expect(item_container.dom_attributes).to eq({}) end end + end - context 'when the container :selected_class option is specified' do - let(:options) {{ container: { selected_class: 'sel_class' } }} + context 'when the container :selected_class option is specified' do + let(:options) {{ container: { selected_class: 'sel_class' } }} - context 'and the item should be added' do - let(:add_item) { true } + context 'and the item should be added' do + let(:add_item) { true } - it 'changes its selected_class' do - expect(item_container.selected_class).to eq 'sel_class' - end + it 'changes its selected_class' do + expect(item_container.selected_class).to eq 'sel_class' end + end - context "and the item shouldn't be added" do - let(:add_item) { false } + context "and the item shouldn't be added" do + let(:add_item) { false } - it "doesn't change its selected_class" do - expect(item_container.selected_class).to be_nil - end + it "doesn't change its selected_class" do + expect(item_container.selected_class).to be_nil end end end end + end - describe 'Conditions' do - context 'when an :if option is given' do - let(:options) {{ if: proc{condition} }} - let(:condition) { nil } + describe 'Conditions' do + context 'when an :if option is given' do + let(:options) {{ if: proc{condition} }} + let(:condition) { nil } - context 'and it evals to true' do - let(:condition) { true } + context 'and it evals to true' do + let(:condition) { true } - it 'creates a new Item' do - expect(Item).to receive(:new) - item_container.item('key', 'name', 'url', options) - end + it 'creates a new Item' do + expect(SimpleNavigation::Item).to receive(:new) + item_container.item('key', 'name', 'url', options) end + end - context 'and it evals to false' do - let(:condition) { false } + context 'and it evals to false' do + let(:condition) { false } - it "doesn't create a new Item" do - expect(Item).not_to receive(:new) - item_container.item('key', 'name', 'url', options) - end + it "doesn't create a new Item" do + expect(SimpleNavigation::Item).not_to receive(:new) + item_container.item('key', 'name', 'url', options) end + end - context 'and it is not a proc or a method' do - it 'raises an error' do - expect{ - item_container.item('key', 'name', 'url', { if: 'text' }) - }.to raise_error(ArgumentError, ':if or :unless must be procs or lambdas') - end + context 'and it is not a proc or a method' do + it 'raises an error' do + expect{ + item_container.item('key', 'name', 'url', { if: 'text' }) + }.to raise_error(ArgumentError, ':if or :unless must be procs or lambdas') end end + end - context 'when an :unless option is given' do - let(:options) {{ unless: proc{condition} }} - let(:condition) { nil } + context 'when an :unless option is given' do + let(:options) {{ unless: proc{condition} }} + let(:condition) { nil } - context 'and it evals to false' do - let(:condition) { false } + context 'and it evals to false' do + let(:condition) { false } - it 'creates a new Navigation-Item' do - expect(Item).to receive(:new) - item_container.item('key', 'name', 'url', options) - end + it 'creates a new Navigation-Item' do + expect(SimpleNavigation::Item).to receive(:new) + item_container.item('key', 'name', 'url', options) end + end - context 'and it evals to true' do - let(:condition) { true } + context 'and it evals to true' do + let(:condition) { true } - it "doesn't create a new Navigation-Item" do - expect(Item).not_to receive(:new) - item_container.item('key', 'name', 'url', options) - end + it "doesn't create a new Navigation-Item" do + expect(SimpleNavigation::Item).not_to receive(:new) + item_container.item('key', 'name', 'url', options) end end end end + end - describe '#[]' do - before do - item_container.item(:first, 'first', 'bla') - item_container.item(:second, 'second', 'bla') - item_container.item(:third, 'third', 'bla') - end + describe '#[]' do + before do + item_container.item(:first, 'first', 'bla') + item_container.item(:second, 'second', 'bla') + item_container.item(:third, 'third', 'bla') + end - it 'returns the item with the specified navi_key' do - expect(item_container[:second].name).to eq 'second' - end + it 'returns the item with the specified navi_key' do + expect(item_container[:second].name).to eq 'second' + end - context 'when no item exists for the specified navi_key' do - it 'returns nil' do - expect(item_container[:invalid]).to be_nil - end + context 'when no item exists for the specified navi_key' do + it 'returns nil' do + expect(item_container[:invalid]).to be_nil end end + end - describe '#render' do - # TODO - let(:renderer_instance) { double(:renderer).as_null_object } - let(:renderer_class) { double(:renderer_class, new: renderer_instance) } - - context 'when renderer is specified as an option' do - context 'and is specified as a class' do - it 'instantiates the passed renderer_class with the options' do - expect(renderer_class).to receive(:new) - .with({renderer: renderer_class}) - item_container.render(renderer: renderer_class) - end + describe '#render' do + # TODO + let(:renderer_instance) { double(:renderer).as_null_object } + let(:renderer_class) { double(:renderer_class, new: renderer_instance) } - it 'calls render on the renderer and passes self' do - expect(renderer_instance).to receive(:render).with(item_container) - item_container.render(renderer: renderer_class) - end + context 'when renderer is specified as an option' do + context 'and is specified as a class' do + it 'instantiates the passed renderer_class with the options' do + expect(renderer_class).to receive(:new) + .with({renderer: renderer_class}) + item_container.render(renderer: renderer_class) end - context 'and is specified as a symbol' do - before do - SimpleNavigation.registered_renderers = { - my_renderer: renderer_class - } - end - - it "instantiates the passed renderer_class with the options" do - expect(renderer_class).to receive(:new).with({renderer: :my_renderer}) - item_container.render(renderer: :my_renderer) - end - - it 'calls render on the renderer and passes self' do - expect(renderer_instance).to receive(:render).with(item_container) - item_container.render(renderer: :my_renderer) - end + it 'calls render on the renderer and passes self' do + expect(renderer_instance).to receive(:render).with(item_container) + item_container.render(renderer: renderer_class) end end - context 'when no renderer is specified' do - let(:options) { Hash.new } - - before { allow(item_container).to receive_messages(renderer: renderer_class) } + context 'and is specified as a symbol' do + before do + SimpleNavigation.registered_renderers = { + my_renderer: renderer_class + } + end - it "instantiates the container's renderer with the options" do - expect(renderer_class).to receive(:new).with(options) - item_container.render(options) + it "instantiates the passed renderer_class with the options" do + expect(renderer_class).to receive(:new).with({renderer: :my_renderer}) + item_container.render(renderer: :my_renderer) end it 'calls render on the renderer and passes self' do expect(renderer_instance).to receive(:render).with(item_container) - item_container.render(options) + item_container.render(renderer: :my_renderer) end end end - describe '#renderer' do - context 'when no renderer is set explicitly' do - it 'returns globally-configured renderer' do - expect(item_container.renderer).to be Configuration.instance.renderer - end - end + context 'when no renderer is specified' do + let(:options) { Hash.new } - context 'when a renderer is set explicitly' do - let(:renderer) { double(:renderer) } + before { allow(item_container).to receive_messages(renderer: renderer_class) } - before { item_container.renderer = renderer } + it "instantiates the container's renderer with the options" do + expect(renderer_class).to receive(:new).with(options) + item_container.render(options) + end - it 'returns the specified renderer' do - expect(item_container.renderer).to be renderer - end + it 'calls render on the renderer and passes self' do + expect(renderer_instance).to receive(:render).with(item_container) + item_container.render(options) end end + end - describe '#level_for_item' do - before(:each) do - item_container.item(:p1, 'p1', 'p1') - item_container.item(:p2, 'p2', 'p2') do |p2| - p2.item(:s1, 's1', 's1') - p2.item(:s2, 's2', 's2') do |s2| - s2.item(:ss1, 'ss1', 'ss1') - s2.item(:ss2, 'ss2', 'ss2') - end - p2.item(:s3, 's3', 's3') - end - item_container.item(:p3, 'p3', 'p3') + describe '#renderer' do + context 'when no renderer is set explicitly' do + it 'returns globally-configured renderer' do + expect(item_container.renderer).to be SimpleNavigation::Configuration.instance.renderer end + end - shared_examples 'returning the level of an item' do |item, level| - specify{ expect(item_container.level_for_item(item)).to eq level } - end + context 'when a renderer is set explicitly' do + let(:renderer) { double(:renderer) } - it_behaves_like 'returning the level of an item', :p1, 1 - it_behaves_like 'returning the level of an item', :p3, 1 - it_behaves_like 'returning the level of an item', :s1, 2 - it_behaves_like 'returning the level of an item', :ss1, 3 - it_behaves_like 'returning the level of an item', :x, nil + before { item_container.renderer = renderer } + + it 'returns the specified renderer' do + expect(item_container.renderer).to be renderer + end end + end - describe '#empty?' do - context 'when there are no items' do - it 'returns true' do - item_container.instance_variable_set(:@items, []) - expect(item_container).to be_empty + describe '#level_for_item' do + before(:each) do + item_container.item(:p1, 'p1', 'p1') + item_container.item(:p2, 'p2', 'p2') do |p2| + p2.item(:s1, 's1', 's1') + p2.item(:s2, 's2', 's2') do |s2| + s2.item(:ss1, 'ss1', 'ss1') + s2.item(:ss2, 'ss2', 'ss2') end + p2.item(:s3, 's3', 's3') end + item_container.item(:p3, 'p3', 'p3') + end - context 'when there are some items' do - it 'returns false' do - item_container.instance_variable_set(:@items, [double(:item)]) - expect(item_container).not_to be_empty - end + shared_examples 'returning the level of an item' do |item, level| + specify{ expect(item_container.level_for_item(item)).to eq level } + end + + it_behaves_like 'returning the level of an item', :p1, 1 + it_behaves_like 'returning the level of an item', :p3, 1 + it_behaves_like 'returning the level of an item', :s1, 2 + it_behaves_like 'returning the level of an item', :ss1, 3 + it_behaves_like 'returning the level of an item', :x, nil + end + + describe '#empty?' do + context 'when there are no items' do + it 'returns true' do + item_container.instance_variable_set(:@items, []) + expect(item_container).to be_empty + end + end + + context 'when there are some items' do + it 'returns false' do + item_container.instance_variable_set(:@items, [double(:item)]) + expect(item_container).not_to be_empty end end end diff --git a/spec/simple_navigation/item_spec.rb b/spec/simple_navigation/item_spec.rb index 96ac5155..12bd2533 100644 --- a/spec/simple_navigation/item_spec.rb +++ b/spec/simple_navigation/item_spec.rb @@ -1,473 +1,471 @@ -module SimpleNavigation - describe Item do - let!(:item_container) { ItemContainer.new } +RSpec.describe SimpleNavigation::Item do + let!(:item_container) { SimpleNavigation::ItemContainer.new } - let(:adapter) { double(:adapter) } - let(:item_args) { [item_container, :my_key, 'name', url, options] } - let(:item) { Item.new(*item_args) } - let(:options) { Hash.new } - let(:url) { 'url' } + let(:adapter) { double(:adapter) } + let(:item_args) { [item_container, :my_key, 'name', url, options] } + let(:item) { SimpleNavigation::Item.new(*item_args) } + let(:options) { Hash.new } + let(:url) { 'url' } - before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } + before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } - describe '#highlights_on' do - let(:options) {{ highlights_on: :test }} + describe '#highlights_on' do + let(:options) {{ highlights_on: :test }} - it "returns the item's highlights_on option" do - expect(item.highlights_on).to eq :test - end + it "returns the item's highlights_on option" do + expect(item.highlights_on).to eq :test end + end - describe '#initialize' do - context 'when there is a sub_navigation' do - let(:subnav_container) { double(:subnav_container).as_null_object } + describe '#initialize' do + context 'when there is a sub_navigation' do + let(:subnav_container) { double(:subnav_container).as_null_object } - shared_examples 'creating sub navigation container' do - it 'creates a sub navigation container with a level+1' do - expect(item.sub_navigation.level).to eq 2 - end + shared_examples 'creating sub navigation container' do + it 'creates a sub navigation container with a level+1' do + expect(item.sub_navigation.level).to eq 2 end + end - context 'when a block is given' do - it_behaves_like 'creating sub navigation container' do - let(:item) { Item.new(*item_args) {} } - end + context 'when a block is given' do + it_behaves_like 'creating sub navigation container' do + let(:item) { SimpleNavigation::Item.new(*item_args) {} } + end - it 'calls the block' do - allow(ItemContainer).to receive_messages(new: subnav_container) + it 'calls the block' do + allow(SimpleNavigation::ItemContainer).to receive_messages(new: subnav_container) - expect{ |blk| - Item.new(*item_args, &blk) - }.to yield_with_args(subnav_container) - end + expect{ |blk| + SimpleNavigation::Item.new(*item_args, &blk) + }.to yield_with_args(subnav_container) end + end - context 'when no block is given' do - context 'and items are given' do - let(:items) { [] } - let(:options) {{ items: items }} + context 'when no block is given' do + context 'and items are given' do + let(:items) { [] } + let(:options) {{ items: items }} - it_behaves_like 'creating sub navigation container' + it_behaves_like 'creating sub navigation container' - it "sets the items on the subnav_container" do - expect(item.sub_navigation.items).to eq items - end + it "sets the items on the subnav_container" do + expect(item.sub_navigation.items).to eq items end + end - context 'and no items are given' do - it "doesn't create a new ItemContainer" do - item = Item.new(*item_args) - expect(item.sub_navigation).to be_nil - end + context 'and no items are given' do + it "doesn't create a new ItemContainer" do + item = SimpleNavigation::Item.new(*item_args) + expect(item.sub_navigation).to be_nil end end end + end - context 'when a :method option is given' do - let(:options) {{ method: :delete }} + context 'when a :method option is given' do + let(:options) {{ method: :delete }} - it "sets the item's method" do - expect(item.method).to eq :delete - end + it "sets the item's method" do + expect(item.method).to eq :delete end + end - context 'when no :method option is given' do - it "sets the item's method to nil" do - expect(item.method).to be_nil - end + context 'when no :method option is given' do + it "sets the item's method to nil" do + expect(item.method).to be_nil end + end - context 'when an :highlights_on option is given' do - let(:highlights_on) { double(:highlights_on) } - let(:options) {{ highlights_on: highlights_on }} + context 'when an :highlights_on option is given' do + let(:highlights_on) { double(:highlights_on) } + let(:options) {{ highlights_on: highlights_on }} - it "sets the item's highlights_on" do - expect(item.highlights_on).to eq highlights_on - end + it "sets the item's highlights_on" do + expect(item.highlights_on).to eq highlights_on end + end - context 'when no :highlights_on option is given' do - it "sets the item's highlights_on to nil" do - expect(item.highlights_on).to be_nil - end + context 'when no :highlights_on option is given' do + it "sets the item's highlights_on to nil" do + expect(item.highlights_on).to be_nil end + end - context 'when a url is given' do - context 'and it is a string' do - it "sets the item's url accordingly" do - expect(item.url).to eq 'url' - end + context 'when a url is given' do + context 'and it is a string' do + it "sets the item's url accordingly" do + expect(item.url).to eq 'url' end + end - context 'and it is a proc' do - let(:url) { proc{ "my_" + "url" } } + context 'and it is a proc' do + let(:url) { proc{ "my_" + "url" } } - it "sets the item's url accordingly" do - expect(item.url).to eq 'my_url' - end + it "sets the item's url accordingly" do + expect(item.url).to eq 'my_url' end + end - context 'and it is nil' do - let(:url) { nil } + context 'and it is nil' do + let(:url) { nil } - it "sets the item's url accordingly" do - expect(item.url).to be_nil - end + it "sets the item's url accordingly" do + expect(item.url).to be_nil end end + end - context 'when no url nor options is specified' do - let(:item_args) { [item_container, :my_key, 'name'] } + context 'when no url nor options is specified' do + let(:item_args) { [item_container, :my_key, 'name'] } - it "sets the item's url to nil" do - expect(item.url).to be_nil - end + it "sets the item's url to nil" do + expect(item.url).to be_nil end + end - context 'when only a url is given' do - let(:item_args) { [item_container, :my_key, 'name', 'url'] } + context 'when only a url is given' do + let(:item_args) { [item_container, :my_key, 'name', 'url'] } - it "set the item's url accordingly" do - expect(item.url).to eq 'url' - end + it "set the item's url accordingly" do + expect(item.url).to eq 'url' end + end - context 'when url and options are given' do - let(:options) {{ html: { option: true } }} + context 'when url and options are given' do + let(:options) {{ html: { option: true } }} - before { allow(adapter).to receive_messages(current_page?: false) } + before { allow(adapter).to receive_messages(current_page?: false) } - it "set the item's url accordingly" do - expect(item.url).to eq 'url' - end + it "set the item's url accordingly" do + expect(item.url).to eq 'url' + end - it "sets the item's html_options accordingly" do - allow(item).to \ - receive_messages(selected_by_subnav?: false, - selected_by_condition?: false) - expect(item.html_options).to include(option: true) - end + it "sets the item's html_options accordingly" do + allow(item).to \ + receive_messages(selected_by_subnav?: false, + selected_by_condition?: false) + expect(item.html_options).to include(option: true) end end + end - describe '#link_html_options' do - let(:options) {{ link_html: :test }} + describe '#link_html_options' do + let(:options) {{ link_html: :test }} - it "returns the item's link_html option" do - expect(item.link_html_options).to eq :test - end + it "returns the item's link_html option" do + expect(item.link_html_options).to eq :test end + end - describe '#method' do - let(:options) {{ method: :test }} + describe '#method' do + let(:options) {{ method: :test }} - it "returns the item's method option" do - expect(item.method).to eq :test - end + it "returns the item's method option" do + expect(item.method).to eq :test end + end - describe '#name' do - before do - allow(SimpleNavigation.config).to \ - receive_messages(name_generator: proc{ |name| "#{name}" }) - end + describe '#name' do + before do + allow(SimpleNavigation.config).to \ + receive_messages(name_generator: proc{ |name| "#{name}" }) + end - context 'when no option is given' do - context 'and the name_generator uses only the name' do - it 'uses the default name_generator' do - expect(item.name).to eq 'name' - end + context 'when no option is given' do + context 'and the name_generator uses only the name' do + it 'uses the default name_generator' do + expect(item.name).to eq 'name' end + end - context 'and the name_generator uses only the item itself' do - before do - allow(SimpleNavigation.config).to \ - receive_messages(name_generator: proc{ |name, item| "#{item.key}" }) - end + context 'and the name_generator uses only the item itself' do + before do + allow(SimpleNavigation.config).to \ + receive_messages(name_generator: proc{ |name, item| "#{item.key}" }) + end - it 'uses the default name_generator' do - expect(item.name).to eq 'my_key' - end + it 'uses the default name_generator' do + expect(item.name).to eq 'my_key' end end + end - context 'when the :apply_generator is false' do - it "returns the item's name" do - expect(item.name(apply_generator: false)).to eq 'name' - end + context 'when the :apply_generator is false' do + it "returns the item's name" do + expect(item.name(apply_generator: false)).to eq 'name' end + end - context 'when a block is given' do - let(:item_args) { [item_container, :my_key, -> { 'Name in block' }, url, options] } + context 'when a block is given' do + let(:item_args) { [item_container, :my_key, -> { 'Name in block' }, url, options] } - it "returns the item's name that is defined in the block" do - expect(item.name).to include 'Name in block' - end + it "returns the item's name that is defined in the block" do + expect(item.name).to include 'Name in block' end end + end - describe '#selected?' do - context 'when the item has no :highlights_on option' do - before { allow(SimpleNavigation).to receive_messages(config: config) } + describe '#selected?' do + context 'when the item has no :highlights_on option' do + before { allow(SimpleNavigation).to receive_messages(config: config) } - context 'and auto highlighting is off' do - let(:config) { double(:config, auto_highlight: false) } + context 'and auto highlighting is off' do + let(:config) { double(:config, auto_highlight: false) } - it 'returns false' do - expect(item.selected?).to be false + it 'returns false' do + expect(item.selected?).to be false + end + end + + context 'and auto highlighting is on' do + let(:config) { double(:config, ignore_query_params_on_auto_highlight: true, ignore_anchors_on_auto_highlight: true, auto_highlight: true) } + + context "and the current url matches the item's url" do + before { allow(adapter).to receive_messages(current_page?: true) } + + it 'returns true' do + expect(item.selected?).to be true end end - context 'and auto highlighting is on' do - let(:config) { double(:config, ignore_query_params_on_auto_highlight: true, ignore_anchors_on_auto_highlight: true, auto_highlight: true) } + context "and the current url does not match the item's url" do + let(:config) do + double(:config, auto_highlight: false, highlight_on_subpath: false) + end - context "and the current url matches the item's url" do - before { allow(adapter).to receive_messages(current_page?: true) } + before { allow(adapter).to receive_messages(current_page?: false) } - it 'returns true' do - expect(item.selected?).to be true - end + it 'returns false' do + expect(item.selected?).to be false end + end - context "and the current url does not match the item's url" do - let(:config) do - double(:config, auto_highlight: false, highlight_on_subpath: false) - end + context 'and highlights_on_subpath is on' do + let(:config) do + double(:config, auto_highlight: true, highlight_on_subpath: true, ignore_query_params_on_auto_highlight: true, ignore_anchors_on_auto_highlight: true) + end - before { allow(adapter).to receive_messages(current_page?: false) } + context "but item has no url" do + let(:url) { nil } it 'returns false' do expect(item.selected?).to be false end end - context 'and highlights_on_subpath is on' do - let(:config) do - double(:config, auto_highlight: true, highlight_on_subpath: true, ignore_query_params_on_auto_highlight: true, ignore_anchors_on_auto_highlight: true) + context "and the current url is a sub path of the item's url" do + before do + allow(adapter).to \ + receive_messages(current_page?: false, request_uri: 'url/test') end - context "but item has no url" do - let(:url) { nil } - - it 'returns false' do - expect(item.selected?).to be false - end + it 'returns true' do + expect(item.selected?).to be true end + end - context "and the current url is a sub path of the item's url" do - before do - allow(adapter).to \ - receive_messages(current_page?: false, request_uri: 'url/test') - end - - it 'returns true' do - expect(item.selected?).to be true - end + context "and the current url is not a sub path of the item's url" do + before do + allow(adapter).to \ + receive_messages(current_page?: false, request_uri: 'other/test') end - context "and the current url is not a sub path of the item's url" do - before do - allow(adapter).to \ - receive_messages(current_page?: false, request_uri: 'other/test') - end - - it 'returns false' do - expect(item.selected?).to be false - end + it 'returns false' do + expect(item.selected?).to be false end end end end + end - context 'when the item has a :highlights_on option' do - context 'and it is a regular expression' do - before { allow(adapter).to receive_messages(request_uri: '/test') } + context 'when the item has a :highlights_on option' do + context 'and it is a regular expression' do + before { allow(adapter).to receive_messages(request_uri: '/test') } - context 'and the current url matches the expression' do - let(:options) {{ highlights_on: /test/ }} + context 'and the current url matches the expression' do + let(:options) {{ highlights_on: /test/ }} - it 'returns true' do - expect(item.selected?).to be true - end + it 'returns true' do + expect(item.selected?).to be true end + end - context 'and the current url does not match the expression' do - let(:options) {{ highlights_on: /other/ }} + context 'and the current url does not match the expression' do + let(:options) {{ highlights_on: /other/ }} - it 'returns false' do - expect(item.selected?).to be false - end + it 'returns false' do + expect(item.selected?).to be false end end + end - context 'and it is a callable object' do - context 'and the call returns true' do - let(:options) {{ highlights_on: -> { true } }} + context 'and it is a callable object' do + context 'and the call returns true' do + let(:options) {{ highlights_on: -> { true } }} - it 'returns true' do - expect(item.selected?).to be true - end + it 'returns true' do + expect(item.selected?).to be true end + end - context 'and the call returns false' do - let(:options) {{ highlights_on: -> { false } }} + context 'and the call returns false' do + let(:options) {{ highlights_on: -> { false } }} - it 'returns false' do - expect(item.selected?).to be false - end + it 'returns false' do + expect(item.selected?).to be false end end + end - context 'and it is the :subpath symbol' do - let(:options) {{ highlights_on: :subpath }} + context 'and it is the :subpath symbol' do + let(:options) {{ highlights_on: :subpath }} - context "and the current url is a sub path of the item's url" do - before do - allow(adapter).to receive_messages(request_uri: 'url/test') - end + context "and the current url is a sub path of the item's url" do + before do + allow(adapter).to receive_messages(request_uri: 'url/test') + end - it 'returns true' do - expect(item.selected?).to be true - end + it 'returns true' do + expect(item.selected?).to be true end + end - context "and the current url is not a sub path of the item's url" do - before do - allow(adapter).to receive_messages(request_uri: 'other/test') - end + context "and the current url is not a sub path of the item's url" do + before do + allow(adapter).to receive_messages(request_uri: 'other/test') + end - it 'returns false' do - expect(item.selected?).to be false - end + it 'returns false' do + expect(item.selected?).to be false end end + end - context 'and it is non usable' do - let(:options) {{ highlights_on: :hello }} + context 'and it is non usable' do + let(:options) {{ highlights_on: :hello }} - it 'raises an exception' do - expect{ item.selected? }.to raise_error(ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath') - end + it 'raises an exception' do + expect{ item.selected? }.to raise_error(ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath') end end end + end - describe '#selected_class' do - context 'when the item is selected' do - before { allow(item).to receive_messages(selected?: true) } + describe '#selected_class' do + context 'when the item is selected' do + before { allow(item).to receive_messages(selected?: true) } - it 'returns the default selected_class' do - expect(item.selected_class).to eq 'selected' - end + it 'returns the default selected_class' do + expect(item.selected_class).to eq 'selected' + end - context 'and selected_class is defined in the context' do - before { allow(item_container).to receive_messages(selected_class: 'defined') } + context 'and selected_class is defined in the context' do + before { allow(item_container).to receive_messages(selected_class: 'defined') } - it "returns the context's selected_class" do - expect(item.selected_class).to eq 'defined' - end + it "returns the context's selected_class" do + expect(item.selected_class).to eq 'defined' end end + end - context 'when the item is not selected' do - before { allow(item).to receive_messages(selected?: false) } + context 'when the item is not selected' do + before { allow(item).to receive_messages(selected?: false) } - it 'returns nil' do - expect(item.selected_class).to be_nil - end + it 'returns nil' do + expect(item.selected_class).to be_nil end end + end - describe ':html_options argument' do - let(:selected_classes) { 'selected simple-navigation-active-leaf' } + describe ':html_options argument' do + let(:selected_classes) { 'selected simple-navigation-active-leaf' } - context 'when the :class option is given' do - let(:options) {{ html: { class: 'my_class' } }} + context 'when the :class option is given' do + let(:options) {{ html: { class: 'my_class' } }} - context 'and the item is selected' do - before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } + context 'and the item is selected' do + before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } - it "adds the specified class to the item's html classes" do - expect(item.html_options[:class]).to include('my_class') - end + it "adds the specified class to the item's html classes" do + expect(item.html_options[:class]).to include('my_class') + end - it "doesn't replace the default html classes of a selected item" do - expect(item.html_options[:class]).to include(selected_classes) - end + it "doesn't replace the default html classes of a selected item" do + expect(item.html_options[:class]).to include(selected_classes) end + end - context "and the item isn't selected" do - before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } + context "and the item isn't selected" do + before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } - it "sets the specified class as the item's html classes" do - expect(item.html_options[:class]).to include('my_class') - end + it "sets the specified class as the item's html classes" do + expect(item.html_options[:class]).to include('my_class') end end + end - context "when the :class option isn't given" do - context 'and the item is selected' do - before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } + context "when the :class option isn't given" do + context 'and the item is selected' do + before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } - it "sets the default html classes of a selected item" do - expect(item.html_options[:class]).to include(selected_classes) - end + it "sets the default html classes of a selected item" do + expect(item.html_options[:class]).to include(selected_classes) end + end - context "and the item isn't selected" do - before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } + context "and the item isn't selected" do + before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } - it "doesn't set any html class on the item" do - expect(item.html_options[:class]).to be_blank - end - end + it "doesn't set any html class on the item" do + expect(item.html_options[:class]).to be_blank + end end + end - shared_examples 'generating id' do |id| - it "sets the item's html id to the specified id" do - expect(item.html_options[:id]).to eq id - end + shared_examples 'generating id' do |id| + it "sets the item's html id to the specified id" do + expect(item.html_options[:id]).to eq id end + end - describe 'when the :id option is given' do - let(:options) {{ html: { id: 'my_id' } }} + describe 'when the :id option is given' do + let(:options) {{ html: { id: 'my_id' } }} - before do - allow(SimpleNavigation.config).to receive_messages(autogenerate_item_ids: generate_ids) - allow(item).to receive_messages(selected?: false, selected_by_condition?: false) - end + before do + allow(SimpleNavigation.config).to receive_messages(autogenerate_item_ids: generate_ids) + allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + end - context 'and :autogenerate_item_ids is true' do - let(:generate_ids) { true } + context 'and :autogenerate_item_ids is true' do + let(:generate_ids) { true } - it_behaves_like 'generating id', 'my_id' - end + it_behaves_like 'generating id', 'my_id' + end - context 'and :autogenerate_item_ids is false' do - let(:generate_ids) { false } + context 'and :autogenerate_item_ids is false' do + let(:generate_ids) { false } - it_behaves_like 'generating id', 'my_id' - end + it_behaves_like 'generating id', 'my_id' end + end - context "when the :id option isn't given" do - before do - allow(SimpleNavigation.config).to receive_messages(autogenerate_item_ids: generate_ids) - allow(item).to receive_messages(selected?: false, selected_by_condition?: false) - end + context "when the :id option isn't given" do + before do + allow(SimpleNavigation.config).to receive_messages(autogenerate_item_ids: generate_ids) + allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + end - context 'and :autogenerate_item_ids is true' do - let(:generate_ids) { true } + context 'and :autogenerate_item_ids is true' do + let(:generate_ids) { true } - it_behaves_like 'generating id', 'my_key' - end + it_behaves_like 'generating id', 'my_key' + end - context 'and :autogenerate_item_ids is false' do - let(:generate_ids) { false } + context 'and :autogenerate_item_ids is false' do + let(:generate_ids) { false } - it "doesn't set any html id on the item" do - expect(item.html_options[:id]).to be_blank - end + it "doesn't set any html id on the item" do + expect(item.html_options[:id]).to be_blank end end end diff --git a/spec/simple_navigation/items_provider_spec.rb b/spec/simple_navigation/items_provider_spec.rb index fd5022e5..42552818 100644 --- a/spec/simple_navigation/items_provider_spec.rb +++ b/spec/simple_navigation/items_provider_spec.rb @@ -1,43 +1,41 @@ -module SimpleNavigation - describe ItemsProvider do - let(:items_provider) { ItemsProvider.new(provider) } +RSpec.describe SimpleNavigation::ItemsProvider do + let(:items_provider) { SimpleNavigation::ItemsProvider.new(provider) } - describe '#items' do - let(:items) { double(:items) } + describe '#items' do + let(:items) { double(:items) } - context 'when provider is a symbol' do - let(:context) { double(:context, provider_method: items) } - let(:provider) { :provider_method } + context 'when provider is a symbol' do + let(:context) { double(:context, provider_method: items) } + let(:provider) { :provider_method } - before { allow(SimpleNavigation).to receive_messages(context_for_eval: context) } + before { allow(SimpleNavigation).to receive_messages(context_for_eval: context) } - it 'retrieves the items from the evaluation context' do - expect(items_provider.items).to eq items - end + it 'retrieves the items from the evaluation context' do + expect(items_provider.items).to eq items end + end - context 'when provider responds to :items' do - let(:provider) { double(:provider, items: items) } + context 'when provider responds to :items' do + let(:provider) { double(:provider, items: items) } - it 'retrieves the items from the provider object' do - expect(items_provider.items).to eq items - end + it 'retrieves the items from the provider object' do + expect(items_provider.items).to eq items end + end - context 'provider is a collection' do - let(:provider) { [] } + context 'provider is a collection' do + let(:provider) { [] } - it 'retrieves the items by returning the provider' do - expect(items_provider.items).to eq provider - end + it 'retrieves the items by returning the provider' do + expect(items_provider.items).to eq provider end + end - context 'when provider is something else' do - let(:provider) { double(:provider) } + context 'when provider is something else' do + let(:provider) { double(:provider) } - it 'raises an exception' do - expect{ items_provider.items }.to raise_error(RuntimeError, /items_provider either must be a symbol .*, an object .* or an enumerable/) - end + it 'raises an exception' do + expect{ items_provider.items }.to raise_error(RuntimeError, /items_provider either must be a symbol .*, an object .* or an enumerable/) end end end diff --git a/spec/simple_navigation/renderer/base_spec.rb b/spec/simple_navigation/renderer/base_spec.rb index 704a17cd..6d6da5eb 100644 --- a/spec/simple_navigation/renderer/base_spec.rb +++ b/spec/simple_navigation/renderer/base_spec.rb @@ -1,238 +1,234 @@ -module SimpleNavigation - module Renderer - describe Base do - subject(:base) { Base.new(options) } +RSpec.describe SimpleNavigation::Renderer::Base do + subject(:base) { SimpleNavigation::Renderer::Base.new(options) } - let(:adapter) { double(:adapter) } - let(:options) { Hash.new } + let(:adapter) { double(:adapter) } + let(:options) { Hash.new } - before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } + before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } - it 'delegates the :link_to method to adapter' do - allow(adapter).to receive_messages(link_to: 'link_to') - expect(base.link_to).to eq 'link_to' - end + it 'delegates the :link_to method to adapter' do + allow(adapter).to receive_messages(link_to: 'link_to') + expect(base.link_to).to eq 'link_to' + end - it 'delegates the :content_tag method to adapter' do - allow(adapter).to receive_messages(content_tag: 'content_tag') - expect(base.content_tag).to eq 'content_tag' - end + it 'delegates the :content_tag method to adapter' do + allow(adapter).to receive_messages(content_tag: 'content_tag') + expect(base.content_tag).to eq 'content_tag' + end - describe '#initialize' do - it "sets the renderer adapter to the SimpleNavigation one" do - expect(base.adapter).to be adapter - end - end + describe '#initialize' do + it "sets the renderer adapter to the SimpleNavigation one" do + expect(base.adapter).to be adapter + end + end + + describe '#options' do + it "returns the renderer's options" do + expect(base.options).to be options + end + end + + describe '#render' do + it "raise an exception to indicate it's a subclass responsibility" do + expect{ base.render(:container) }.to raise_error(NotImplementedError, 'subclass responsibility') + end + end + + describe '#expand_all?' do + context 'when :options is set' do + context 'and the :expand_all option is true' do + let(:options) {{ expand_all: true }} - describe '#options' do - it "returns the renderer's options" do - expect(base.options).to be options + it 'returns true' do + expect(base.expand_all?).to be true end end - describe '#render' do - it "raise an exception to indicate it's a subclass responsibility" do - expect{ base.render(:container) }.to raise_error(NotImplementedError, 'subclass responsibility') + context 'and the :expand_all option is false' do + let(:options) {{ expand_all: false }} + + it 'returns false' do + expect(base.expand_all?).to be false end end + end - describe '#expand_all?' do - context 'when :options is set' do - context 'and the :expand_all option is true' do - let(:options) {{ expand_all: true }} + context "when :options isn't set" do + let(:options) { Hash.new } - it 'returns true' do - expect(base.expand_all?).to be true - end - end + it 'returns false' do + expect(base.expand_all?).to be false + end + end + end - context 'and the :expand_all option is false' do - let(:options) {{ expand_all: false }} + describe '#skip_if_empty?' do + context 'when :options is set' do + context 'and the :skip_if_empty option is true' do + let(:options) {{ skip_if_empty: true }} - it 'returns false' do - expect(base.expand_all?).to be false - end - end + it 'returns true' do + expect(base.skip_if_empty?).to be true end + end - context "when :options isn't set" do - let(:options) { Hash.new } + context 'and the :skip_if_empty option is false' do + let(:options) {{ skip_if_empty: false }} - it 'returns false' do - expect(base.expand_all?).to be false - end + it 'returns true' do + expect(base.skip_if_empty?).to be false end end + end - describe '#skip_if_empty?' do - context 'when :options is set' do - context 'and the :skip_if_empty option is true' do - let(:options) {{ skip_if_empty: true }} + context "when :options isn't set" do + let(:options) { Hash.new } - it 'returns true' do - expect(base.skip_if_empty?).to be true - end - end + it 'returns true' do + expect(base.skip_if_empty?).to be false + end + end + end - context 'and the :skip_if_empty option is false' do - let(:options) {{ skip_if_empty: false }} + describe '#level' do + context 'and the :level option is set' do + let(:options) {{ level: 1 }} - it 'returns true' do - expect(base.skip_if_empty?).to be false - end - end - end + it 'returns the specified level' do + expect(base.level).to eq 1 + end + end - context "when :options isn't set" do - let(:options) { Hash.new } + context "and the :level option isn't set" do + let(:options) { Hash.new } - it 'returns true' do - expect(base.skip_if_empty?).to be false - end - end + it 'returns :all' do + expect(base.level).to eq :all end + end + end - describe '#level' do - context 'and the :level option is set' do - let(:options) {{ level: 1 }} + describe '#consider_sub_navigation?' do + let(:item) { double(:item) } - it 'returns the specified level' do - expect(base.level).to eq 1 - end - end + before { allow(item).to receive_messages(sub_navigation: sub_navigation) } - context "and the :level option isn't set" do - let(:options) { Hash.new } + context 'when the item has no sub navigation' do + let(:sub_navigation) { nil } - it 'returns :all' do - expect(base.level).to eq :all - end - end + it 'returns false' do + expect(base.send(:consider_sub_navigation?, item)).to be false end + end - describe '#consider_sub_navigation?' do - let(:item) { double(:item) } - - before { allow(item).to receive_messages(sub_navigation: sub_navigation) } + context 'when the item has sub navigation' do + let(:sub_navigation) { double(:sub_navigation) } - context 'when the item has no sub navigation' do - let(:sub_navigation) { nil } + context 'and the renderer has an unknown level' do + before { allow(base).to receive_messages(level: 'unknown') } - it 'returns false' do - expect(base.send(:consider_sub_navigation?, item)).to be false - end + it 'returns false' do + expect(base.send(:consider_sub_navigation?, item)).to be false end + end - context 'when the item has sub navigation' do - let(:sub_navigation) { double(:sub_navigation) } + context 'and the renderer has a level set to :all' do + before { allow(base).to receive_messages(level: :all) } - context 'and the renderer has an unknown level' do - before { allow(base).to receive_messages(level: 'unknown') } + it 'returns false' do + expect(base.send(:consider_sub_navigation?, item)).to be true + end + end - it 'returns false' do - expect(base.send(:consider_sub_navigation?, item)).to be false - end - end + context "when the renderer's level is a number" do + before { allow(base).to receive_messages(level: 2) } - context 'and the renderer has a level set to :all' do - before { allow(base).to receive_messages(level: :all) } + it 'returns false' do + expect(base.send(:consider_sub_navigation?, item)).to be false + end + end - it 'returns false' do - expect(base.send(:consider_sub_navigation?, item)).to be true - end - end + context "when the renderer's level is a Range" do + before { allow(base).to receive_messages(level: 2..3) } - context "when the renderer's level is a number" do - before { allow(base).to receive_messages(level: 2) } + context "and sub navigation's level is greater than range.max" do + before { allow(sub_navigation).to receive_messages(level: 4) } - it 'returns false' do - expect(base.send(:consider_sub_navigation?, item)).to be false - end + it 'returns false' do + expect(base.send(:consider_sub_navigation?, item)).to be false end + end - context "when the renderer's level is a Range" do - before { allow(base).to receive_messages(level: 2..3) } - - context "and sub navigation's level is greater than range.max" do - before { allow(sub_navigation).to receive_messages(level: 4) } - - it 'returns false' do - expect(base.send(:consider_sub_navigation?, item)).to be false - end - end - - context "and sub navigation's level is equal to range.max" do - before { allow(sub_navigation).to receive_messages(level: 3) } + context "and sub navigation's level is equal to range.max" do + before { allow(sub_navigation).to receive_messages(level: 3) } - it 'returns true' do - expect(base.send(:consider_sub_navigation?, item)).to be true - end - end + it 'returns true' do + expect(base.send(:consider_sub_navigation?, item)).to be true + end + end - context "and sub navigation's level is equal to range.min" do - before { allow(sub_navigation).to receive_messages(level: 2) } + context "and sub navigation's level is equal to range.min" do + before { allow(sub_navigation).to receive_messages(level: 2) } - it 'returns true' do - expect(base.send(:consider_sub_navigation?, item)).to be true - end - end + it 'returns true' do + expect(base.send(:consider_sub_navigation?, item)).to be true end end end + end + end - describe '#include_sub_navigation?' do - let(:item) { double(:item) } - - context 'when consider_sub_navigation? is true' do - before { allow(base).to receive_messages(consider_sub_navigation?: true) } + describe '#include_sub_navigation?' do + let(:item) { double(:item) } - context 'and expand_sub_navigation? is true' do - before { allow(base).to receive_messages(expand_sub_navigation?: true) } + context 'when consider_sub_navigation? is true' do + before { allow(base).to receive_messages(consider_sub_navigation?: true) } - it 'returns true' do - expect(base.include_sub_navigation?(item)).to be true - end - end + context 'and expand_sub_navigation? is true' do + before { allow(base).to receive_messages(expand_sub_navigation?: true) } - context 'and expand_sub_navigation? is false' do - before { allow(base).to receive_messages(expand_sub_navigation?: false) } - - it 'returns false' do - expect(base.include_sub_navigation?(item)).to be false - end - end + it 'returns true' do + expect(base.include_sub_navigation?(item)).to be true end + end - context 'consider_sub_navigation? is false' do - before { allow(base).to receive_messages(consider_sub_navigation?: false) } + context 'and expand_sub_navigation? is false' do + before { allow(base).to receive_messages(expand_sub_navigation?: false) } - context 'and expand_sub_navigation? is true' do - before { allow(base).to receive_messages(expand_sub_navigation?: true) } + it 'returns false' do + expect(base.include_sub_navigation?(item)).to be false + end + end + end - it 'returns false' do - expect(base.include_sub_navigation?(item)).to be false - end - end + context 'consider_sub_navigation? is false' do + before { allow(base).to receive_messages(consider_sub_navigation?: false) } - context 'and expand_sub_navigation? is false' do - before { allow(base).to receive_messages(expand_sub_navigation?: false) } + context 'and expand_sub_navigation? is true' do + before { allow(base).to receive_messages(expand_sub_navigation?: true) } - it 'returns false' do - expect(base.include_sub_navigation?(item)).to be false - end - end + it 'returns false' do + expect(base.include_sub_navigation?(item)).to be false end end - describe '#render_sub_navigation_for' do - let(:item) { double(:item, sub_navigation: sub_navigation) } - let(:sub_navigation) { double(:sub_navigation) } + context 'and expand_sub_navigation? is false' do + before { allow(base).to receive_messages(expand_sub_navigation?: false) } - it 'renders the sub navigation passing it the options' do - expect(sub_navigation).to receive(:render).with(options) - base.render_sub_navigation_for(item) + it 'returns false' do + expect(base.include_sub_navigation?(item)).to be false end end end end + + describe '#render_sub_navigation_for' do + let(:item) { double(:item, sub_navigation: sub_navigation) } + let(:sub_navigation) { double(:sub_navigation) } + + it 'renders the sub navigation passing it the options' do + expect(sub_navigation).to receive(:render).with(options) + base.render_sub_navigation_for(item) + end + end end diff --git a/spec/simple_navigation/renderer/breadcrumbs_spec.rb b/spec/simple_navigation/renderer/breadcrumbs_spec.rb index 4fa5c2c1..ba3239d2 100644 --- a/spec/simple_navigation/renderer/breadcrumbs_spec.rb +++ b/spec/simple_navigation/renderer/breadcrumbs_spec.rb @@ -1,115 +1,111 @@ -module SimpleNavigation - module Renderer - describe Breadcrumbs do - let!(:navigation) { setup_navigation('nav_id', 'nav_class') } +RSpec.describe SimpleNavigation::Renderer::Breadcrumbs do + let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - let(:item) { nil } - let(:options) {{ level: :all }} - let(:output) { renderer.render(navigation) } - let(:renderer) { Breadcrumbs.new(options) } + let(:item) { nil } + let(:options) {{ level: :all }} + let(:output) { renderer.render(navigation) } + let(:renderer) { SimpleNavigation::Renderer::Breadcrumbs.new(options) } - before { select_an_item(navigation[item]) if item } + before { select_an_item(navigation[item]) if item } - describe '#render' do - it "renders a 'div' tag for the navigation" do - expect(output).to have_css('div') - end + describe '#render' do + it "renders a 'div' tag for the navigation" do + expect(output).to have_css('div') + end - it "sets the right html id on the rendered 'div' tag" do - expect(output).to have_css('div#nav_id') - end + it "sets the right html id on the rendered 'div' tag" do + expect(output).to have_css('div#nav_id') + end - it "sets the right html classes on the rendered 'div' tag" do - expect(output).to have_css('div.nav_class') - end + it "sets the right html classes on the rendered 'div' tag" do + expect(output).to have_css('div.nav_class') + end - context 'when no item is selected' do - it "doesn't render any 'a' tag in the 'div' tag" do - expect(output).not_to have_css('div a') - end - end + context 'when no item is selected' do + it "doesn't render any 'a' tag in the 'div' tag" do + expect(output).not_to have_css('div a') + end + end - context 'when an item is selected' do - let(:item) { :invoices } + context 'when an item is selected' do + let(:item) { :invoices } - it "renders the selected 'a' tag" do - expect(output).to have_css('div a') - end + it "renders the selected 'a' tag" do + expect(output).to have_css('div a') + end - it "remders the 'a' tag without any html id" do - expect(output).not_to have_css('div a[id]') - end + it "remders the 'a' tag without any html id" do + expect(output).not_to have_css('div a[id]') + end - it "renders the 'a' tag without any html class" do - expect(output).not_to have_css('div a[class]') - end + it "renders the 'a' tag without any html class" do + expect(output).not_to have_css('div a[class]') + end - context 'and the :allow_classes_and_ids option is true' do - let(:options) {{ level: :all, allow_classes_and_ids: true }} + context 'and the :allow_classes_and_ids option is true' do + let(:options) {{ level: :all, allow_classes_and_ids: true }} - it "renders the 'a' tag with the selected class" do - expect(output).to have_css('div a.selected') - end + it "renders the 'a' tag with the selected class" do + expect(output).to have_css('div a.selected') + end - context "and the item hasn't any id explicitly set" do - it "renders the 'a' tag without any html id" do - expect(output).not_to have_css('div a[id]') - end - end + context "and the item hasn't any id explicitly set" do + it "renders the 'a' tag without any html id" do + expect(output).not_to have_css('div a[id]') + end + end - context 'and the item has an explicitly set id' do - let(:item) { :users } + context 'and the item has an explicitly set id' do + let(:item) { :users } - it "renders the 'a' tag with an html id" do - expect(output).to have_css('div a#breadcrumb_users_link_id') - end - end + it "renders the 'a' tag with an html id" do + expect(output).to have_css('div a#breadcrumb_users_link_id') end end + end + end - context 'and the :prefix option is set' do - let(:options) {{ prefix: 'You are here: ' }} + context 'and the :prefix option is set' do + let(:options) {{ prefix: 'You are here: ' }} - context 'and there are no items to render' do - let(:item) { nil } + context 'and there are no items to render' do + let(:item) { nil } - it "doesn't render the prefix before the breadcrumbs" do - expect(output).not_to match(/^You are here: /) - end - end + it "doesn't render the prefix before the breadcrumbs" do + expect(output).not_to match(/^You are here: /) + end + end - context 'and there are items to render' do - let(:item) { :invoices } + context 'and there are items to render' do + let(:item) { :invoices } - it 'renders the prefix before the breadcrumbs' do - expect(output).to match(/^You are here: /) - end - end + it 'renders the prefix before the breadcrumbs' do + expect(output).to match(/^You are here: /) end + end + end - context 'when a sub navigation item is selected' do - before do - allow(navigation[:invoices]).to receive_messages(selected?: true) + context 'when a sub navigation item is selected' do + before do + allow(navigation[:invoices]).to receive_messages(selected?: true) - allow(navigation[:invoices].sub_navigation[:unpaid]).to \ - receive_messages(selected?: true, selected_by_condition?: true) - end + allow(navigation[:invoices].sub_navigation[:unpaid]).to \ + receive_messages(selected?: true, selected_by_condition?: true) + end - it 'renders all items as links' do - expect(output).to have_css('div a', 2) - end + it 'renders all items as links' do + expect(output).to have_css('div a', 2) + end - context 'when the :static_leaf option is true' do - let(:options) {{ level: :all, static_leaf: true }} + context 'when the :static_leaf option is true' do + let(:options) {{ level: :all, static_leaf: true }} - it 'renders the items as links' do - expect(output).to have_css('div a') - end + it 'renders the items as links' do + expect(output).to have_css('div a') + end - it 'renders the last item as simple text' do - expect(output).to have_css('div span') - end - end + it 'renders the last item as simple text' do + expect(output).to have_css('div span') end end end diff --git a/spec/simple_navigation/renderer/json_spec.rb b/spec/simple_navigation/renderer/json_spec.rb index bc662ff5..b7edc66d 100644 --- a/spec/simple_navigation/renderer/json_spec.rb +++ b/spec/simple_navigation/renderer/json_spec.rb @@ -1,73 +1,69 @@ -module SimpleNavigation - module Renderer - describe Json do - describe '#render' do - let!(:navigation) { setup_navigation('nav_id', 'nav_class') } +RSpec.describe SimpleNavigation::Renderer::Json do + describe '#render' do + let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - let(:item) { :invoices } - let(:options) {{ level: :all }} - let(:output) { renderer.render(navigation) } - let(:parsed_output) { JSON.parse(output) } - let(:renderer) { Json.new(options) } + let(:item) { :invoices } + let(:options) {{ level: :all }} + let(:output) { renderer.render(navigation) } + let(:parsed_output) { JSON.parse(output) } + let(:renderer) { SimpleNavigation::Renderer::Json.new(options) } - before { select_an_item(navigation[item]) if item } + before { select_an_item(navigation[item]) if item } - context 'when an item is selected' do + context 'when an item is selected' do - it 'renders the selected page' do - invoices_item = parsed_output.find { |item| item['name'] == 'Invoices' } - expect(invoices_item).to include('selected' => true) - end - end - - context 'when the :as_hash option is true' do - let(:options) {{ level: :all, as_hash: true }} + it 'renders the selected page' do + invoices_item = parsed_output.find { |item| item['name'] == 'Invoices' } + expect(invoices_item).to include('selected' => true) + end + end - it 'returns every item as a hash' do - expect(output).to be_an Array + context 'when the :as_hash option is true' do + let(:options) {{ level: :all, as_hash: true }} - output.each do |item| - expect(item).to be_an Hash - end - end + it 'returns every item as a hash' do + expect(output).to be_an Array - it 'renders the selected page' do - invoices_item = output.find { |item| item[:name] == 'Invoices' } - expect(invoices_item).to include(selected: true) - end + output.each do |item| + expect(item).to be_an Hash end + end + + it 'renders the selected page' do + invoices_item = output.find { |item| item[:name] == 'Invoices' } + expect(invoices_item).to include(selected: true) + end + end - context 'with options' do - it 'should render options for each item' do - parsed_output.each do |item| - expect(item).to have_key('options') - end - end + context 'with options' do + it 'should render options for each item' do + parsed_output.each do |item| + expect(item).to have_key('options') end + end + end - context 'when a sub navigation item is selected' do - let(:invoices_item) do - parsed_output.find { |item| item['name'] == 'Invoices' } - end - let(:unpaid_item) do - invoices_item['items'].find { |item| item['name'] == 'Unpaid' } - end + context 'when a sub navigation item is selected' do + let(:invoices_item) do + parsed_output.find { |item| item['name'] == 'Invoices' } + end + let(:unpaid_item) do + invoices_item['items'].find { |item| item['name'] == 'Unpaid' } + end - before do - allow(navigation[:invoices]).to receive_messages(selected?: true) + before do + allow(navigation[:invoices]).to receive_messages(selected?: true) - allow(navigation[:invoices].sub_navigation[:unpaid]).to \ - receive_messages(selected?: true, selected_by_condition?: true) - end + allow(navigation[:invoices].sub_navigation[:unpaid]).to \ + receive_messages(selected?: true, selected_by_condition?: true) + end - it 'marks all the parent items as selected' do - expect(invoices_item).to include('selected' => true) - end + it 'marks all the parent items as selected' do + expect(invoices_item).to include('selected' => true) + end - it 'marks the item as selected' do - expect(unpaid_item).to include('selected' => true) - end - end + it 'marks the item as selected' do + expect(unpaid_item).to include('selected' => true) end end end diff --git a/spec/simple_navigation/renderer/links_spec.rb b/spec/simple_navigation/renderer/links_spec.rb index 4fafd5f4..6b4b35c7 100644 --- a/spec/simple_navigation/renderer/links_spec.rb +++ b/spec/simple_navigation/renderer/links_spec.rb @@ -1,86 +1,82 @@ -module SimpleNavigation - module Renderer - describe Links do - describe '#render' do - let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - - let(:item) { nil } - let(:options) { { level: :all } } - let(:output) { renderer.render(navigation) } - let(:renderer) { Links.new(options) } - - before { select_an_item(navigation[item]) if item } - - it "renders a 'div' tag for the navigation" do - expect(output).to have_css('div') - end - - it "sets the right html id on the rendered 'div' tag" do - expect(output).to have_css('div#nav_id') - end - - it "sets the right html classes on the rendered 'div' tag" do - expect(output).to have_css('div.nav_class') - end - - it "renders an 'a' tag for each item" do - expect(output).to have_css('a', 3) - end - - it "renders the 'a' tags with the corresponding item's :html_options" do - expect(output).to have_css('a[style="float:right"]') - end - - context 'when an item has a specified id' do - it "renders the 'a' tags with the specified id" do - expect(output).to have_css('a#users_id') - end - end - - context 'when an item has no specified id' do - it "uses a default id by stringifying the item's key" do - expect(output).to have_css('a#invoices') - end - end - - context 'when no item is selected' do - it "renders items without the 'selected' class" do - expect(output).not_to have_css('a.selected') - end - end - - context 'when an item is selected' do - let(:item) { :invoices } - - it "renders the selected item with the 'selected' class" do - expect(output).to have_css('a#invoices.selected') - end - end - - context "when the :join_with option is set" do - let(:options) {{ level: :all, join_with: ' | ' }} - - it 'separates the items with the specified separator' do - expect(output.scan(' | ').size).to eq 3 - end - end - - context 'when a sub navigation item is selected' do - before do - allow(navigation[:invoices]).to receive_messages(selected?: true) - - allow(navigation[:invoices].sub_navigation[:unpaid]).to \ - receive_messages(selected?: true, selected_by_condition?: true) - end - - it 'renders the main parent as selected' do - expect(output).to have_css('a#invoices.selected') - end - - it "doesn't render the nested item's link" do - expect(output).not_to have_css('a#unpaid') - end - end +RSpec.describe SimpleNavigation::Renderer::Links do + describe '#render' do + let!(:navigation) { setup_navigation('nav_id', 'nav_class') } + + let(:item) { nil } + let(:options) { { level: :all } } + let(:output) { renderer.render(navigation) } + let(:renderer) { SimpleNavigation::Renderer::Links.new(options) } + + before { select_an_item(navigation[item]) if item } + + it "renders a 'div' tag for the navigation" do + expect(output).to have_css('div') + end + + it "sets the right html id on the rendered 'div' tag" do + expect(output).to have_css('div#nav_id') + end + + it "sets the right html classes on the rendered 'div' tag" do + expect(output).to have_css('div.nav_class') + end + + it "renders an 'a' tag for each item" do + expect(output).to have_css('a', 3) + end + + it "renders the 'a' tags with the corresponding item's :html_options" do + expect(output).to have_css('a[style="float:right"]') + end + + context 'when an item has a specified id' do + it "renders the 'a' tags with the specified id" do + expect(output).to have_css('a#users_id') + end + end + + context 'when an item has no specified id' do + it "uses a default id by stringifying the item's key" do + expect(output).to have_css('a#invoices') + end + end + + context 'when no item is selected' do + it "renders items without the 'selected' class" do + expect(output).not_to have_css('a.selected') + end + end + + context 'when an item is selected' do + let(:item) { :invoices } + + it "renders the selected item with the 'selected' class" do + expect(output).to have_css('a#invoices.selected') + end + end + + context "when the :join_with option is set" do + let(:options) {{ level: :all, join_with: ' | ' }} + + it 'separates the items with the specified separator' do + expect(output.scan(' | ').size).to eq 3 + end + end + + context 'when a sub navigation item is selected' do + before do + allow(navigation[:invoices]).to receive_messages(selected?: true) + + allow(navigation[:invoices].sub_navigation[:unpaid]).to \ + receive_messages(selected?: true, selected_by_condition?: true) + end + + it 'renders the main parent as selected' do + expect(output).to have_css('a#invoices.selected') + end + + it "doesn't render the nested item's link" do + expect(output).not_to have_css('a#unpaid') end end end diff --git a/spec/simple_navigation/renderer/list_spec.rb b/spec/simple_navigation/renderer/list_spec.rb index b0eb89d4..0a1dc904 100644 --- a/spec/simple_navigation/renderer/list_spec.rb +++ b/spec/simple_navigation/renderer/list_spec.rb @@ -1,94 +1,90 @@ -module SimpleNavigation - module Renderer - describe List do - let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - - let(:item) { nil } - let(:options) { { level: :all } } - let(:output) { renderer.render(navigation) } - let(:renderer) { List.new(options) } - - before { select_an_item(navigation[item]) if item } - - describe '#render' do - it "renders an 'ul' tag for the navigation" do - expect(output).to have_css('ul') - end - - it "sets the right html id on the rendered 'ul' tag" do - expect(output).to have_css('ul#nav_id') - end - - it "sets the right html classes on the rendered 'ul' tag" do - expect(output).to have_css('ul.nav_class') - end - - context 'when an item has no specified id' do - it "renders the item's 'li' tag with the item's stingified key as id" do - expect(output).to have_css('li#invoices') - end - end - - context 'when an item has a specified id' do - it "renders the item's 'li' tag with the specified id" do - expect(output).to have_css('li#users_id') - end - end - - context 'when no item is selected' do - it "renders each item as 'li' tag without any selected class" do - expect(output).not_to have_css('ul li.selected') - end - - it "renders each item as 'a' tag without any selected class" do - expect(output).not_to have_css('ul li a.selected') - end - end - - context 'when an item is selected' do - let(:item) { :invoices } - - it "renders the item's 'li' tag with its id and selected classes" do - expect(output).to have_css('li#invoices.selected') - end - - it "renders the item's 'a' tag with the selected classes" do - expect(output).to have_css('li#invoices a.selected') - end - end - - context 'when the :ordered option is true' do - let(:options) {{ level: :all, ordered: true }} - - it "renders an 'ol' tag for the navigation" do - expect(output).to have_css('ol') - end - - it "sets the right html id on the rendered 'ol' tag" do - expect(output).to have_css('ol#nav_id') - end - - it "sets the right html classes on the rendered 'ol' tag" do - expect(output).to have_css('ol.nav_class') - end - end - - context 'when a sub navigation item is selected' do - before do - allow(navigation[:invoices]).to receive_messages(selected?: true) - - allow(navigation[:invoices].sub_navigation[:unpaid]).to \ - receive_messages(selected?: true, selected_by_condition?: true) - end - - it 'renders the parent items as selected' do - expect(output).to have_css('li#invoices.selected') - end - - it "renders the selected nested item's link as selected" do - expect(output).to have_css('li#unpaid.selected') - end - end +RSpec.describe SimpleNavigation::Renderer::List do + let!(:navigation) { setup_navigation('nav_id', 'nav_class') } + + let(:item) { nil } + let(:options) { { level: :all } } + let(:output) { renderer.render(navigation) } + let(:renderer) { SimpleNavigation::Renderer::List.new(options) } + + before { select_an_item(navigation[item]) if item } + + describe '#render' do + it "renders an 'ul' tag for the navigation" do + expect(output).to have_css('ul') + end + + it "sets the right html id on the rendered 'ul' tag" do + expect(output).to have_css('ul#nav_id') + end + + it "sets the right html classes on the rendered 'ul' tag" do + expect(output).to have_css('ul.nav_class') + end + + context 'when an item has no specified id' do + it "renders the item's 'li' tag with the item's stingified key as id" do + expect(output).to have_css('li#invoices') + end + end + + context 'when an item has a specified id' do + it "renders the item's 'li' tag with the specified id" do + expect(output).to have_css('li#users_id') + end + end + + context 'when no item is selected' do + it "renders each item as 'li' tag without any selected class" do + expect(output).not_to have_css('ul li.selected') + end + + it "renders each item as 'a' tag without any selected class" do + expect(output).not_to have_css('ul li a.selected') + end + end + + context 'when an item is selected' do + let(:item) { :invoices } + + it "renders the item's 'li' tag with its id and selected classes" do + expect(output).to have_css('li#invoices.selected') + end + + it "renders the item's 'a' tag with the selected classes" do + expect(output).to have_css('li#invoices a.selected') + end + end + + context 'when the :ordered option is true' do + let(:options) {{ level: :all, ordered: true }} + + it "renders an 'ol' tag for the navigation" do + expect(output).to have_css('ol') + end + + it "sets the right html id on the rendered 'ol' tag" do + expect(output).to have_css('ol#nav_id') + end + + it "sets the right html classes on the rendered 'ol' tag" do + expect(output).to have_css('ol.nav_class') + end + end + + context 'when a sub navigation item is selected' do + before do + allow(navigation[:invoices]).to receive_messages(selected?: true) + + allow(navigation[:invoices].sub_navigation[:unpaid]).to \ + receive_messages(selected?: true, selected_by_condition?: true) + end + + it 'renders the parent items as selected' do + expect(output).to have_css('li#invoices.selected') + end + + it "renders the selected nested item's link as selected" do + expect(output).to have_css('li#unpaid.selected') end end end diff --git a/spec/simple_navigation/renderer/text_spec.rb b/spec/simple_navigation/renderer/text_spec.rb index 4fb5b09a..27e398a1 100644 --- a/spec/simple_navigation/renderer/text_spec.rb +++ b/spec/simple_navigation/renderer/text_spec.rb @@ -1,49 +1,45 @@ -module SimpleNavigation - module Renderer - describe Text do - let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - - let(:item) { nil } - let(:options) {{ level: :all }} - let(:output) { renderer.render(navigation) } - let(:renderer) { Text.new(options) } - - before { select_an_item(navigation[item]) if item } - - describe '#render' do - context 'when no item is selected' do - it 'renders an empty string' do - expect(output).to eq '' - end - end +RSpec.describe SimpleNavigation::Renderer::Text do + let!(:navigation) { setup_navigation('nav_id', 'nav_class') } - context 'when an item is selected' do - let(:item) { :invoices } + let(:item) { nil } + let(:options) {{ level: :all }} + let(:output) { renderer.render(navigation) } + let(:renderer) { SimpleNavigation::Renderer::Text.new(options) } - it "renders the selected item's name" do - expect(output).to eq 'Invoices' - end - end + before { select_an_item(navigation[item]) if item } - context 'when a sub navigation item is selected' do - before do - allow(navigation[:invoices]).to receive_messages(selected?: true) + describe '#render' do + context 'when no item is selected' do + it 'renders an empty string' do + expect(output).to eq '' + end + end - allow(navigation[:invoices].sub_navigation[:unpaid]).to \ - receive_messages(selected?: true, selected_by_condition?: true) - end + context 'when an item is selected' do + let(:item) { :invoices } - it 'separates the items with a space' do - expect(output).to eq 'Invoices Unpaid' - end + it "renders the selected item's name" do + expect(output).to eq 'Invoices' + end + end + + context 'when a sub navigation item is selected' do + before do + allow(navigation[:invoices]).to receive_messages(selected?: true) + + allow(navigation[:invoices].sub_navigation[:unpaid]).to \ + receive_messages(selected?: true, selected_by_condition?: true) + end + + it 'separates the items with a space' do + expect(output).to eq 'Invoices Unpaid' + end - context "and the :join_with option is set" do - let(:options) {{ level: :all, join_with: ' | ' }} + context "and the :join_with option is set" do + let(:options) {{ level: :all, join_with: ' | ' }} - it 'separates the items with the specified separator' do - expect(output).to eq 'Invoices | Unpaid' - end - end + it 'separates the items with the specified separator' do + expect(output).to eq 'Invoices | Unpaid' end end end diff --git a/spec/simple_navigation_spec.rb b/spec/simple_navigation_spec.rb index da708875..5d4abb8b 100644 --- a/spec/simple_navigation_spec.rb +++ b/spec/simple_navigation_spec.rb @@ -1,4 +1,4 @@ -describe SimpleNavigation do +RSpec.describe SimpleNavigation do before { subject.config_file_path = 'path_to_config' } describe 'config_file_path=' do From 8df5d1cd533fc523e26f0983efe0a89465c51107 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 04:07:01 +0100 Subject: [PATCH 07/30] Remove useless dependency to bundler --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f5bae82d..f328c8a4 100644 --- a/Gemfile +++ b/Gemfile @@ -2,8 +2,8 @@ source 'https://rubygems.org' gemspec +# Dev libs gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git' -gem 'bundler' gem 'capybara' gem 'coveralls', '~> 0.7' gem 'guard-rspec', '~> 4.2' From 11fcef1f7bf813fc5e4f3575786a540ce16b36f2 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 04:14:37 +0100 Subject: [PATCH 08/30] Add Rubocop, fix basic Rubocop offenses --- .rspec | 1 - .rubocop.yml | 15 ++++++++ Gemfile | 9 +++++ Guardfile | 6 ++- Rakefile | 2 + bin/rubocop | 16 ++++++++ gemfiles/rails_3.2.gemfile | 6 ++- gemfiles/rails_4.1.gemfile | 6 ++- gemfiles/rails_4.2.gemfile | 6 ++- gemfiles/rails_5.2.gemfile | 6 ++- gemfiles/rails_6.0.gemfile | 6 ++- gemfiles/rails_6.1.gemfile | 6 ++- gemfiles/rails_7.0.gemfile | 6 ++- gemfiles/rails_7.1.gemfile | 6 ++- gemfiles/rails_7.2.gemfile | 6 ++- gemfiles/rails_8.0.gemfile | 6 ++- gemfiles/rails_8.1.gemfile | 6 ++- .../navigation_config_generator.rb | 2 + .../templates/config/navigation.rb | 37 ++++++++++--------- .../navigation_config_generator.rb | 2 + lib/simple-navigation.rb | 2 + lib/simple_navigation.rb | 27 ++++++++------ lib/simple_navigation/adapters.rb | 2 + lib/simple_navigation/adapters/base.rb | 2 + lib/simple_navigation/adapters/nanoc.rb | 4 +- lib/simple_navigation/adapters/padrino.rb | 4 +- lib/simple_navigation/adapters/rails.rb | 16 ++++---- lib/simple_navigation/adapters/sinatra.rb | 8 ++-- lib/simple_navigation/config_file.rb | 2 + lib/simple_navigation/config_file_finder.rb | 6 ++- lib/simple_navigation/configuration.rb | 10 +++-- lib/simple_navigation/helpers.rb | 20 +++++----- lib/simple_navigation/item.rb | 36 +++++++++--------- lib/simple_navigation/item_adapter.rb | 4 +- lib/simple_navigation/item_container.rb | 18 ++++++--- lib/simple_navigation/items_provider.rb | 8 ++-- lib/simple_navigation/railtie.rb | 4 +- lib/simple_navigation/renderer.rb | 2 + lib/simple_navigation/renderer/base.rb | 8 ++-- lib/simple_navigation/renderer/breadcrumbs.rb | 7 ++-- lib/simple_navigation/renderer/json.rb | 2 + lib/simple_navigation/renderer/links.rb | 2 + lib/simple_navigation/renderer/list.rb | 10 ++--- lib/simple_navigation/renderer/text.rb | 4 +- lib/simple_navigation/version.rb | 2 + simple-navigation.gemspec | 28 +++++++------- 46 files changed, 266 insertions(+), 128 deletions(-) create mode 100644 .rubocop.yml create mode 100755 bin/rubocop diff --git a/.rspec b/.rspec index 1892c522..83e16f80 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,2 @@ --color ---format=documentation --require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..579177df --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,15 @@ +--- +plugins: + - rubocop-capybara + - rubocop-performance + - rubocop-rake + - rubocop-rspec + +AllCops: + NewCops: enable + SuggestExtensions: true + TargetRubyVersion: 2.6 + Exclude: + - bin/* + - gemfiles/* + - spec/**/* diff --git a/Gemfile b/Gemfile index f328c8a4..6da62684 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec @@ -12,3 +14,10 @@ gem 'rake' gem 'rdoc' gem 'rspec', '~> 3.0' gem 'tzinfo', '>= 0' + +# Dev tools / linter +gem 'rubocop', require: false +gem 'rubocop-capybara', require: false +gem 'rubocop-performance', require: false +gem 'rubocop-rake', require: false +gem 'rubocop-rspec', require: false diff --git a/Guardfile b/Guardfile index f4357e62..881f7b62 100644 --- a/Guardfile +++ b/Guardfile @@ -1,7 +1,9 @@ +# frozen_string_literal: true + guard :rspec, all_after_pass: true, failed_mode: :none do watch(%r{^spec/.+_spec\.rb$}) - watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^spec/fake_app/.+\.rb$}) { 'spec/integration' } - watch('spec/spec_helper.rb') { 'spec' } + watch('spec/spec_helper.rb') { 'spec' } end diff --git a/Rakefile b/Rakefile index 08012be7..8092bd3a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'bundler' Bundler::GemHelper.install_tasks diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 00000000..d73598dc --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rubocop' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rubocop", "rubocop") diff --git a/gemfiles/rails_3.2.gemfile b/gemfiles/rails_3.2.gemfile index e41f15a6..1bde4767 100644 --- a/gemfiles/rails_3.2.gemfile +++ b/gemfiles/rails_3.2.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 3.2.0" gem "rspec-rails" diff --git a/gemfiles/rails_4.1.gemfile b/gemfiles/rails_4.1.gemfile index 72884a0b..476a25eb 100644 --- a/gemfiles/rails_4.1.gemfile +++ b/gemfiles/rails_4.1.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 4.1.0" gem "rspec-rails" diff --git a/gemfiles/rails_4.2.gemfile b/gemfiles/rails_4.2.gemfile index 42d1a482..ff8e32bc 100644 --- a/gemfiles/rails_4.2.gemfile +++ b/gemfiles/rails_4.2.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 4.2.0" gem "rspec-rails" diff --git a/gemfiles/rails_5.2.gemfile b/gemfiles/rails_5.2.gemfile index e95a2b7f..1a66547e 100644 --- a/gemfiles/rails_5.2.gemfile +++ b/gemfiles/rails_5.2.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 5.2.0" gem "rspec-rails" diff --git a/gemfiles/rails_6.0.gemfile b/gemfiles/rails_6.0.gemfile index 3f510c6b..dd9ea02d 100644 --- a/gemfiles/rails_6.0.gemfile +++ b/gemfiles/rails_6.0.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 6.0.0" gem "rspec-rails" gem "concurrent-ruby", "1.3.4" diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index 6439a1e9..eb5e6493 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 6.1.0" gem "rspec-rails" gem "concurrent-ruby", "1.3.4" diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index 98a7a080..0d824ee4 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 7.0.0" gem "rspec-rails" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index a26b86bf..9c4d3e5a 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 7.1.0" gem "rspec-rails" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 79861158..21e9507a 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 7.2.0" gem "rspec-rails" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index eb700a36..15a94d7b 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 8.0.0" gem "rspec-rails" diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index bcfaf633..ccf88a71 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "bundler" gem "capybara" gem "coveralls", "~> 0.7" gem "guard-rspec", "~> 4.2" @@ -12,6 +11,11 @@ gem "rake" gem "rdoc" gem "rspec", "~> 3.0" gem "tzinfo", ">= 0" +gem "rubocop", require: false +gem "rubocop-capybara", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false gem "railties", "~> 8.1.0" gem "rspec-rails" diff --git a/generators/navigation_config/navigation_config_generator.rb b/generators/navigation_config/navigation_config_generator.rb index be81087f..449cb454 100644 --- a/generators/navigation_config/navigation_config_generator.rb +++ b/generators/navigation_config/navigation_config_generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NavigationConfigGenerator < Rails::Generator::Base def manifest record do |m| diff --git a/generators/navigation_config/templates/config/navigation.rb b/generators/navigation_config/templates/config/navigation.rb index 2a11b180..48589406 100644 --- a/generators/navigation_config/templates/config/navigation.rb +++ b/generators/navigation_config/templates/config/navigation.rb @@ -1,41 +1,42 @@ -# -*- coding: utf-8 -*- +# frozen_string_literal: true + # Configures your navigation SimpleNavigation::Configuration.run do |navigation| # Specify a custom renderer if needed. # The default renderer is SimpleNavigation::Renderer::List which renders HTML lists. # The renderer can also be specified as option in the render_navigation call. - #navigation.renderer = Your::Custom::Renderer + # navigation.renderer = Your::Custom::Renderer - # Specify the class that will be applied to active navigation items. Defaults to 'selected' - #navigation.selected_class = 'selected' + # Specify the class that will be applied to active navigation items. Defaults to 'selected' + # navigation.selected_class = 'selected' # Specify the class that will be applied to the current leaf of # active navigation items. Defaults to 'simple-navigation-active-leaf' - #navigation.active_leaf_class = 'simple-navigation-active-leaf' + # navigation.active_leaf_class = 'simple-navigation-active-leaf' # Specify if item keys are added to navigation items as id. Defaults to true - #navigation.autogenerate_item_ids = true + # navigation.autogenerate_item_ids = true # You can override the default logic that is used to autogenerate the item ids. # To do this, define a Proc which takes the key of the current item as argument. # The example below would add a prefix to each key. - #navigation.id_generator = Proc.new {|key| "my-prefix-#{key}"} + # navigation.id_generator = Proc.new {|key| "my-prefix-#{key}"} # If you need to add custom html around item names, you can define a proc that # will be called with the name you pass in to the navigation. # The example below shows how to wrap items spans. - #navigation.name_generator = Proc.new {|name, item| tag.span(name) } + # navigation.name_generator = Proc.new {|name, item| tag.span(name) } # Specify if the auto highlight feature is turned on (globally, for the whole navigation). Defaults to true - #navigation.auto_highlight = true - - # Specifies whether auto highlight should ignore query params and/or anchors when - # comparing the navigation items with the current URL. Defaults to true - #navigation.ignore_query_params_on_auto_highlight = true - #navigation.ignore_anchors_on_auto_highlight = true - + # navigation.auto_highlight = true + + # Specifies whether auto highlight should ignore query params and/or anchors when + # comparing the navigation items with the current URL. Defaults to true + # navigation.ignore_query_params_on_auto_highlight = true + # navigation.ignore_anchors_on_auto_highlight = true + # If this option is set to true, all item names will be considered as safe (passed through html_safe). Defaults to false. - #navigation.consider_item_names_as_safe = false + # navigation.consider_item_names_as_safe = false # Define the primary navigation navigation.items do |primary| @@ -73,9 +74,9 @@ # you can also specify html attributes to attach to this particular level # works for all levels of the menu - #primary.dom_attributes = {id: 'menu-id', class: 'menu-class'} + # primary.dom_attributes = {id: 'menu-id', class: 'menu-class'} # You can turn off auto highlighting for a specific level - #primary.auto_highlight = false + # primary.auto_highlight = false end end diff --git a/lib/generators/navigation_config/navigation_config_generator.rb b/lib/generators/navigation_config/navigation_config_generator.rb index f56ddf4d..c3004d9a 100644 --- a/lib/generators/navigation_config/navigation_config_generator.rb +++ b/lib/generators/navigation_config/navigation_config_generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NavigationConfigGenerator < Rails::Generators::Base def self.source_root @source_root ||= begin diff --git a/lib/simple-navigation.rb b/lib/simple-navigation.rb index dc7685de..ce4c0a02 100644 --- a/lib/simple-navigation.rb +++ b/lib/simple-navigation.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require 'simple_navigation' diff --git a/lib/simple_navigation.rb b/lib/simple_navigation.rb index 1522fb20..70c9ff4e 100644 --- a/lib/simple_navigation.rb +++ b/lib/simple_navigation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # cherry picking active_support stuff require 'active_support/core_ext/array' require 'active_support/core_ext/hash' @@ -12,7 +14,7 @@ require 'simple_navigation/renderer' require 'simple_navigation/adapters' require 'simple_navigation/config_file_finder' -require 'simple_navigation/railtie' if defined?(::Rails::Railtie) +require 'simple_navigation/railtie' if defined?(Rails::Railtie) require 'forwardable' @@ -38,21 +40,21 @@ module SimpleNavigation # Maps renderer keys to classes. The keys serve as shortcut in the # render_navigation calls (renderer: :list) self.registered_renderers = { - list: SimpleNavigation::Renderer::List, - links: SimpleNavigation::Renderer::Links, + list: SimpleNavigation::Renderer::List, + links: SimpleNavigation::Renderer::Links, breadcrumbs: SimpleNavigation::Renderer::Breadcrumbs, - text: SimpleNavigation::Renderer::Text, - json: SimpleNavigation::Renderer::Json + text: SimpleNavigation::Renderer::Text, + json: SimpleNavigation::Renderer::Json } class << self extend Forwardable def_delegators :adapter, :context_for_eval, - :current_page?, - :request, - :request_path, - :request_uri + :current_page?, + :request, + :request_path, + :request_uri def_delegators :adapter_class, :register @@ -70,8 +72,9 @@ def framework return :padrino if defined?(Padrino) return :sinatra if defined?(Sinatra) return :nanoc if defined?(Nanoc3) - fail 'simple_navigation currently only works for Rails, Sinatra and ' \ - 'Padrino apps' + + raise 'simple_navigation currently only works for Rails, Sinatra and ' \ + 'Padrino apps' end # Loads the adapter for the current framework @@ -138,7 +141,7 @@ def active_item_container_for(level) when Integer then primary_navigation.active_item_container_for(level) when Range then primary_navigation.active_item_container_for(level.min) else - fail ArgumentError, "Invalid navigation level: #{level}" + raise ArgumentError, "Invalid navigation level: #{level}" end end diff --git a/lib/simple_navigation/adapters.rb b/lib/simple_navigation/adapters.rb index 84764597..21049216 100644 --- a/lib/simple_navigation/adapters.rb +++ b/lib/simple_navigation/adapters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'simple_navigation/adapters/base' module SimpleNavigation diff --git a/lib/simple_navigation/adapters/base.rb b/lib/simple_navigation/adapters/base.rb index 7ff57798..e8324861 100644 --- a/lib/simple_navigation/adapters/base.rb +++ b/lib/simple_navigation/adapters/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Adapters # This is the base class for all adapters. diff --git a/lib/simple_navigation/adapters/nanoc.rb b/lib/simple_navigation/adapters/nanoc.rb index a3b83682..fbe679d2 100644 --- a/lib/simple_navigation/adapters/nanoc.rb +++ b/lib/simple_navigation/adapters/nanoc.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module SimpleNavigation module Adapters class Nanoc < Base class << self def register(root) SimpleNavigation.set_env(root, 'development') - Nanoc3::Context.send(:include, SimpleNavigation::Helpers) + Nanoc3::Context.include SimpleNavigation::Helpers end end diff --git a/lib/simple_navigation/adapters/padrino.rb b/lib/simple_navigation/adapters/padrino.rb index 4dd7359d..28b0e4b9 100644 --- a/lib/simple_navigation/adapters/padrino.rb +++ b/lib/simple_navigation/adapters/padrino.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module SimpleNavigation module Adapters class Padrino < Sinatra - def self.register(app) + def self.register(_app) SimpleNavigation.set_env(::Padrino.root, ::Padrino.env) ::Padrino::Application.send(:helpers, SimpleNavigation::Helpers) end diff --git a/lib/simple_navigation/adapters/rails.rb b/lib/simple_navigation/adapters/rails.rb index 7b513da7..c5a3253c 100644 --- a/lib/simple_navigation/adapters/rails.rb +++ b/lib/simple_navigation/adapters/rails.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Adapters class Rails < Base @@ -20,7 +22,7 @@ def self.register end def self.register_controller_helpers - ActionController::Base.send(:include, SimpleNavigation::Helpers) + ActionController::Base.include SimpleNavigation::Helpers SimpleNavigation::Helpers.instance_methods.each do |m| ActionController::Base.send(:helper_method, m.to_sym) end @@ -47,21 +49,21 @@ def request_path end def context_for_eval - template || - controller || - fail('no context set for evaluation the config file') + template || + controller || + raise('no context set for evaluation the config file') end def current_page?(url) - template && template.current_page?(url) + template&.current_page?(url) end def link_to(name, url, options = {}) - template && template.link_to(link_title(name), url, options) + template&.link_to(link_title(name), url, options) end def content_tag(type, content, options = {}) - template && template.content_tag(type, html_safe(content), options) + template&.content_tag(type, html_safe(content), options) end protected diff --git a/lib/simple_navigation/adapters/sinatra.rb b/lib/simple_navigation/adapters/sinatra.rb index 44f04689..d327a204 100644 --- a/lib/simple_navigation/adapters/sinatra.rb +++ b/lib/simple_navigation/adapters/sinatra.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cgi' module SimpleNavigation @@ -13,7 +15,7 @@ def initialize(context) end def context_for_eval - context || fail('no context set for evaluation the config file') + context || raise('no context set for evaluation the config file') end def request_uri @@ -32,9 +34,7 @@ def current_page?(url) request_uri.split('?').first end - if url_string =~ %r(^\w+://) - uri = "#{request.scheme}://#{request.host_with_port}#{uri}" - end + uri = "#{request.scheme}://#{request.host_with_port}#{uri}" if %r{^\w+://}.match?(url_string) url_string == CGI.unescape(uri) end diff --git a/lib/simple_navigation/config_file.rb b/lib/simple_navigation/config_file.rb index a392a9b7..d071d20e 100644 --- a/lib/simple_navigation/config_file.rb +++ b/lib/simple_navigation/config_file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_support/core_ext/string' module SimpleNavigation diff --git a/lib/simple_navigation/config_file_finder.rb b/lib/simple_navigation/config_file_finder.rb index a82965f5..9be010f0 100644 --- a/lib/simple_navigation/config_file_finder.rb +++ b/lib/simple_navigation/config_file_finder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'simple_navigation/config_file' module SimpleNavigation @@ -22,8 +24,8 @@ def find(context) config_file_name = config_file_name_for_context(context) find_config_file(config_file_name) || - fail("Config file '#{config_file_name}' not found in " \ - "path(s) #{paths.join(', ')}!") + raise("Config file '#{config_file_name}' not found in " \ + "path(s) #{paths.join(', ')}!") end private diff --git a/lib/simple_navigation/configuration.rb b/lib/simple_navigation/configuration.rb index 3126dab7..60656ef7 100644 --- a/lib/simple_navigation/configuration.rb +++ b/lib/simple_navigation/configuration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'singleton' module SimpleNavigation @@ -27,8 +29,8 @@ def self.eval_config(navigation_context = :default) end # Starts processing the configuration - def self.run(&block) - block.call Configuration.instance + def self.run + yield Configuration.instance end # Sets the config's default-settings @@ -75,13 +77,13 @@ def id_generator # def items(items_provider = nil, &block) if (items_provider && block) || (items_provider.nil? && block.nil?) - fail('please specify either items_provider or block, but not both') + raise('please specify either items_provider or block, but not both') end self.primary_navigation = ItemContainer.new if block - block.call primary_navigation + yield primary_navigation else primary_navigation.items = ItemsProvider.new(items_provider).items end diff --git a/lib/simple_navigation/helpers.rb b/lib/simple_navigation/helpers.rb index 8f5454db..fc13341f 100644 --- a/lib/simple_navigation/helpers.rb +++ b/lib/simple_navigation/helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation # View helpers to render the navigation. # @@ -30,14 +32,12 @@ def self.load_config(options, includer, &block) SimpleNavigation.load_config context SimpleNavigation::Configuration.eval_config context - if block_given? || options[:items] - SimpleNavigation.config.items(options[:items], &block) - end + SimpleNavigation.config.items(options[:items], &block) if block_given? || options[:items] - unless SimpleNavigation.primary_navigation - fail 'no primary navigation defined, either use a navigation config ' \ - 'file or pass items directly to render_navigation' - end + return if SimpleNavigation.primary_navigation + + raise 'no primary navigation defined, either use a navigation config ' \ + 'file or pass items directly to render_navigation' end def self.apply_defaults(options) @@ -85,7 +85,7 @@ def self.apply_defaults(options) # def render_navigation(options = {}, &block) container = active_navigation_item_container(options, &block) - container && container.render(options) + container&.render(options) end # Returns the name of the currently active navigation item belonging to the @@ -138,9 +138,7 @@ def active_navigation_item_key(options = {}) # by default) if no active item can be found for the specified # options def active_navigation_item(options = {}, value_for_nil = nil) - if options[:level].nil? || options[:level] == :all - options[:level] = :leaves - end + options[:level] = :leaves if options[:level].nil? || options[:level] == :all container = active_navigation_item_container(options) if container && (item = container.selected_item) block_given? ? yield(item) : item diff --git a/lib/simple_navigation/item.rb b/lib/simple_navigation/item.rb index 49235406..8102c13e 100644 --- a/lib/simple_navigation/item.rb +++ b/lib/simple_navigation/item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation # Represents an item in your navigation. # Gets generated by the item method in the config-file. @@ -50,7 +52,7 @@ def selected? # for this item in the config-file. # It also adds the 'selected' class to the list of classes if necessary. def html_options - html_opts = options.fetch(:html) { Hash.new } + html_opts = options.fetch(:html) { {} } html_opts[:id] ||= autogenerated_item_id classes = [html_opts[:class], selected_class, active_leaf_class] @@ -63,17 +65,17 @@ def html_options # Returns the configured active_leaf_class if the item is the selected leaf, # nil otherwise def active_leaf_class - if !selected_by_subnav? && selected_by_condition? - config.active_leaf_class - end + return unless !selected_by_subnav? && selected_by_condition? + + config.active_leaf_class end # Returns the configured selected_class if the item is selected, # nil otherwise def selected_class - if selected? - container.selected_class || config.selected_class - end + return unless selected? + + container.selected_class || config.selected_class end # Returns the :highlights_on option as set at initialization @@ -97,7 +99,7 @@ def link_html_options # Returns true if item has a subnavigation and # the sub_navigation is selected def selected_by_subnav? - sub_navigation && sub_navigation.selected? + sub_navigation&.selected? end # Returns true if the item's url matches the request's current url. @@ -139,26 +141,26 @@ def request_uri end def remove_anchors(url_with_anchors) - url_with_anchors && url_with_anchors.split('#').first + url_with_anchors&.split('#')&.first end def remove_query_params(url_with_params) - url_with_params && url_with_params.split('?').first + url_with_params&.split('?')&.first end def url_for_autohighlight - relevant_url = remove_anchors(self.url) if config.ignore_anchors_on_auto_highlight + relevant_url = remove_anchors(url) if config.ignore_anchors_on_auto_highlight relevant_url = remove_query_params(relevant_url) if config.ignore_query_params_on_auto_highlight relevant_url end def selected_by_autohighlight? return false unless auto_highlight? - return false unless self.url + return false unless url root_path_match? || - (url_for_autohighlight && SimpleNavigation.current_page?(url_for_autohighlight)) || - autohighlight_by_subpath? + (url_for_autohighlight && SimpleNavigation.current_page?(url_for_autohighlight)) || + autohighlight_by_subpath? end def autohighlight_by_subpath? @@ -171,13 +173,13 @@ def selected_by_highlights_on? when Proc then highlights_on.call when :subpath then selected_by_subpath? else - fail ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath' + raise ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath' end end def selected_by_subpath? escaped_url = Regexp.escape(url_for_autohighlight) - !!(request_uri =~ /^#{escaped_url}(\/|$||\?)/i) + !!(request_uri =~ %r{^#{escaped_url}(/|$||\?)}i) end def setup_sub_navigation(items = nil, &sub_nav_block) @@ -186,7 +188,7 @@ def setup_sub_navigation(items = nil, &sub_nav_block) self.sub_navigation = ItemContainer.new(container.level + 1) if sub_nav_block - sub_nav_block.call sub_navigation + yield sub_navigation else sub_navigation.items = items end diff --git a/lib/simple_navigation/item_adapter.rb b/lib/simple_navigation/item_adapter.rb index 0593a8a8..39c30df2 100644 --- a/lib/simple_navigation/item_adapter.rb +++ b/lib/simple_navigation/item_adapter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' require 'ostruct' @@ -43,7 +45,7 @@ def options # Returns the items (subnavigation) for this item if it responds to :items # and the items-collection is not empty. Returns nil otherwise. def items - item.items if item.respond_to?(:items) && item.items && item.items.any? + item.items if item.respond_to?(:items) && item.items&.any? end # Converts this Item into a SimpleNavigation::Item diff --git a/lib/simple_navigation/item_container.rb b/lib/simple_navigation/item_container.rb index eebfde3c..6b22702d 100644 --- a/lib/simple_navigation/item_container.rb +++ b/lib/simple_navigation/item_container.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation # Holds the Items for a navigation 'level'. class ItemContainer @@ -11,7 +13,7 @@ class ItemContainer attr_writer :dom_attributes - def initialize(level = 1) #:nodoc: + def initialize(level = 1) # :nodoc: @level = level @items ||= [] @renderer = SimpleNavigation.config.renderer @@ -24,7 +26,7 @@ def dom_attributes dom_id_and_class = { id: dom_id, class: dom_class - }.reject { |_, v| v.nil? } + }.compact @dom_attributes.merge(dom_id_and_class) end @@ -63,6 +65,7 @@ def dom_attributes # The block - if specified - will hold the item's sub_navigation. def item(key, name, url = nil, options = {}, &block) return unless should_add_item?(options) + item = Item.new(self, key, name, url, options, &block) add_item item, options end @@ -71,6 +74,7 @@ def items=(new_items) new_items.each do |item| item_adapter = ItemAdapter.new(item) next unless should_add_item?(item_adapter.options) + add_item item_adapter.to_simple_navigation_item(self), item_adapter.options end end @@ -91,10 +95,11 @@ def level_for_item(navi_key) items.each do |item| next unless item.sub_navigation + level = item.sub_navigation.level_for_item(navi_key) return level if level end - return nil + nil end # Renders the items in this ItemContainer using the configured renderer. @@ -152,7 +157,8 @@ def add_item(item, options) end def modify_dom_attributes(options) - return unless container_options = options[:container] + return unless (container_options = options[:container]) + self.dom_attributes = container_options.fetch(:attributes) { dom_attributes } self.dom_class = container_options.fetch(:class) { dom_class } self.dom_id = container_options.fetch(:id) { dom_id } @@ -178,13 +184,13 @@ def selected_sub_navigation? def should_add_item?(options) [options[:if]].flatten.compact.all? { |m| evaluate_method(m) } && - [options[:unless]].flatten.compact.none? { |m| evaluate_method(m) } + [options[:unless]].flatten.compact.none? { |m| evaluate_method(m) } end def evaluate_method(method) case method when Proc, Method then method.call - else fail(ArgumentError, ':if or :unless must be procs or lambdas') + else raise(ArgumentError, ':if or :unless must be procs or lambdas') end end end diff --git a/lib/simple_navigation/items_provider.rb b/lib/simple_navigation/items_provider.rb index 30d56c90..0b3e2d29 100644 --- a/lib/simple_navigation/items_provider.rb +++ b/lib/simple_navigation/items_provider.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation # Acts as a proxy to navigation items that are passed into the # SimpleNavigation::Configuration#items method. @@ -28,9 +30,9 @@ def items elsif provider.respond_to?(:each) provider else - fail('items_provider either must be a symbol specifying the ' \ - 'helper-method to call, an object with an items-method defined ' \ - 'or an enumerable representing the items') + raise('items_provider either must be a symbol specifying the ' \ + 'helper-method to call, an object with an items-method defined ' \ + 'or an enumerable representing the items') end end end diff --git a/lib/simple_navigation/railtie.rb b/lib/simple_navigation/railtie.rb index 473a2cf5..5a50b49c 100644 --- a/lib/simple_navigation/railtie.rb +++ b/lib/simple_navigation/railtie.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module SimpleNavigation class Railtie < ::Rails::Railtie - initializer 'simple_navigation.register' do |app| + initializer 'simple_navigation.register' do |_app| SimpleNavigation.register end end diff --git a/lib/simple_navigation/renderer.rb b/lib/simple_navigation/renderer.rb index ebbe3207..734ba369 100644 --- a/lib/simple_navigation/renderer.rb +++ b/lib/simple_navigation/renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'simple_navigation/helpers' require 'simple_navigation/renderer/base' diff --git a/lib/simple_navigation/renderer/base.rb b/lib/simple_navigation/renderer/base.rb index 85625fef..bf41c9c9 100644 --- a/lib/simple_navigation/renderer/base.rb +++ b/lib/simple_navigation/renderer/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' module SimpleNavigation @@ -13,7 +15,7 @@ class Base def_delegators :adapter, :link_to, :content_tag - def initialize(options) #:nodoc: + def initialize(options) # :nodoc: @options = options @adapter = SimpleNavigation.adapter end @@ -44,7 +46,7 @@ def render_sub_navigation_for(item) # include_sub_navigation? to determine whether an item's sub_navigation # should be rendered or not. def render(item_container) - fail NotImplementedError, 'subclass responsibility' + raise NotImplementedError, 'subclass responsibility' end protected @@ -93,7 +95,7 @@ def link_options_for(item) special_options = { method: item.method, class: item.selected_class - }.reject { |_, v| v.nil? } + }.compact link_options = item.link_html_options diff --git a/lib/simple_navigation/renderer/breadcrumbs.rb b/lib/simple_navigation/renderer/breadcrumbs.rb index c2e64800..3537411b 100644 --- a/lib/simple_navigation/renderer/breadcrumbs.rb +++ b/lib/simple_navigation/renderer/breadcrumbs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Renderer # Renders an ItemContainer as a
element and its containing items as @@ -25,11 +27,10 @@ def render(item_container) def a_tags(item_container) item_container.items.each_with_object([]) do |item, list| next unless item.selected? + list << tag_for(item) - if include_sub_navigation?(item) - list.concat a_tags(item.sub_navigation) - end + list.concat a_tags(item.sub_navigation) if include_sub_navigation?(item) end end diff --git a/lib/simple_navigation/renderer/json.rb b/lib/simple_navigation/renderer/json.rb index 31dba364..3344a73c 100644 --- a/lib/simple_navigation/renderer/json.rb +++ b/lib/simple_navigation/renderer/json.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'json' module SimpleNavigation diff --git a/lib/simple_navigation/renderer/links.rb b/lib/simple_navigation/renderer/links.rb index 0dbc8f42..281739a5 100644 --- a/lib/simple_navigation/renderer/links.rb +++ b/lib/simple_navigation/renderer/links.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Renderer # Renders an ItemContainer as a
element and its containing items as diff --git a/lib/simple_navigation/renderer/list.rb b/lib/simple_navigation/renderer/list.rb index f37bbe7a..a681c9c2 100644 --- a/lib/simple_navigation/renderer/list.rb +++ b/lib/simple_navigation/renderer/list.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Renderer # Renders an ItemContainer as a
    element and its containing items as @@ -28,14 +30,12 @@ def render(item_container) private def list_content(item_container) - item_container.items.map { |item| + item_container.items.map do |item| li_options = item.html_options.except(:link) li_content = tag_for(item) - if include_sub_navigation?(item) - li_content << render_sub_navigation_for(item) - end + li_content << render_sub_navigation_for(item) if include_sub_navigation?(item) content_tag(:li, li_content, li_options) - }.join + end.join end end end diff --git a/lib/simple_navigation/renderer/text.rb b/lib/simple_navigation/renderer/text.rb index bdf55b3d..731849e4 100644 --- a/lib/simple_navigation/renderer/text.rb +++ b/lib/simple_navigation/renderer/text.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation module Renderer # Renders the 'chain' of selected navigation items as simple text items, @@ -13,7 +15,7 @@ def render(item_container) def list(item_container) item_container.items.keep_if(&:selected?).map do |item| [item.name(apply_generator: false)] + - (include_sub_navigation?(item) ? list(item.sub_navigation) : []) + (include_sub_navigation?(item) ? list(item.sub_navigation) : []) end end end diff --git a/lib/simple_navigation/version.rb b/lib/simple_navigation/version.rb index 21c96a15..1daeac5d 100644 --- a/lib/simple_navigation/version.rb +++ b/lib/simple_navigation/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SimpleNavigation VERSION = '4.4.1' end diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index 88ef9091..a155bbec 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -1,5 +1,6 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) +# frozen_string_literal: true + +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'simple_navigation/version' @@ -8,24 +9,23 @@ Gem::Specification.new do |spec| spec.version = SimpleNavigation::VERSION spec.authors = ['Andi Schacke', 'Mark J. Titorenko', 'Simon Courtois'] spec.email = ['andi@codeplant.ch'] - spec.description = "With the simple-navigation gem installed you can easily " \ - "create multilevel navigations for your Rails, Sinatra or "\ - "Padrino applications. The navigation is defined in a " \ - "single configuration file. It supports automatic as well "\ - "as explicit highlighting of the currently active " \ - "navigation through regular expressions." - spec.summary = "simple-navigation is a ruby library for creating navigations "\ - "(with multiple levels) for your Rails, Sinatra or " \ - "Padrino application." + spec.description = 'With the simple-navigation gem installed you can easily ' \ + 'create multilevel navigations for your Rails, Sinatra or ' \ + 'Padrino applications. The navigation is defined in a ' \ + 'single configuration file. It supports automatic as well ' \ + 'as explicit highlighting of the currently active ' \ + 'navigation through regular expressions.' + spec.summary = 'simple-navigation is a ruby library for creating navigations ' \ + '(with multiple levels) for your Rails, Sinatra or ' \ + 'Padrino application.' spec.homepage = 'http://github.com/codeplant/simple-navigation' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.rdoc_options = ['--inline-source', '--charset=UTF-8'] - spec.add_runtime_dependency 'activesupport', '>= 2.3.2' - spec.add_runtime_dependency 'ostruct' + spec.add_dependency 'activesupport', '>= 2.3.2' + spec.add_dependency 'ostruct' end From e33e49c4865ea078af9044d4e963444cf682aab6 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Wed, 28 Jan 2026 04:15:07 +0100 Subject: [PATCH 09/30] Remove useless files --- init.rb | 1 - install.rb | 5 ----- uninstall.rb | 1 - 3 files changed, 7 deletions(-) delete mode 100644 init.rb delete mode 100644 install.rb delete mode 100644 uninstall.rb diff --git a/init.rb b/init.rb deleted file mode 100644 index 470d4902..00000000 --- a/init.rb +++ /dev/null @@ -1 +0,0 @@ -require File.dirname(__FILE__) + '/rails/init' diff --git a/install.rb b/install.rb deleted file mode 100644 index a62f1560..00000000 --- a/install.rb +++ /dev/null @@ -1,5 +0,0 @@ -begin - puts IO.read(File.join(File.dirname(__FILE__), 'README')) -rescue Exception => e - puts "The following error ocurred while installing the plugin: #{e.message}" -end diff --git a/uninstall.rb b/uninstall.rb deleted file mode 100644 index 97383334..00000000 --- a/uninstall.rb +++ /dev/null @@ -1 +0,0 @@ -# Uninstall hook code here From 534886b98af3629833b5db9fc11971e026f4d91f Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Thu, 29 Jan 2026 17:02:50 +0100 Subject: [PATCH 10/30] Fix Rubocop offenses in specs --- .rubocop.yml | 53 ++++++++++++ spec/fake_app/config/navigation.rb | 6 +- spec/fake_app/rails_app.rb | 8 +- spec/initializers/coveralls.rb | 2 + spec/initializers/have_css_matcher.rb | 6 +- spec/initializers/memfs.rb | 2 + spec/initializers/rails.rb | 4 +- spec/initializers/rspec.rb | 2 + spec/integration/rendering_navigation_spec.rb | 8 +- .../adapters/padrino_spec.rb | 16 ++-- spec/simple_navigation/adapters/rails_spec.rb | 25 +++--- .../adapters/sinatra_spec.rb | 9 +- .../config_file_finder_spec.rb | 8 +- spec/simple_navigation/config_file_spec.rb | 4 +- spec/simple_navigation/configuration_spec.rb | 32 +++---- spec/simple_navigation/helpers_spec.rb | 40 +++++---- spec/simple_navigation/item_adapter_spec.rb | 14 +-- spec/simple_navigation/item_container_spec.rb | 85 ++++++++++--------- spec/simple_navigation/item_spec.rb | 66 +++++++------- spec/simple_navigation/items_provider_spec.rb | 6 +- spec/simple_navigation/renderer/base_spec.rb | 26 +++--- .../renderer/breadcrumbs_spec.rb | 12 +-- spec/simple_navigation/renderer/json_spec.rb | 21 ++--- spec/simple_navigation/renderer/links_spec.rb | 8 +- spec/simple_navigation/renderer/list_spec.rb | 6 +- spec/simple_navigation/renderer/text_spec.rb | 10 ++- spec/simple_navigation_spec.rb | 8 +- spec/spec_helper.rb | 14 +-- 28 files changed, 293 insertions(+), 208 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 579177df..817c8476 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -12,4 +12,57 @@ AllCops: Exclude: - bin/* - gemfiles/* + +Gemspec/RequireMFA: + Enabled: false + +Style/Documentation: + Enabled: false + +Metrics/ClassLength: + Exclude: - spec/**/* + +Metrics/MethodLength: + Exclude: + - spec/**/* + +RSpec/SubjectStub: + Enabled: false + +RSpec/NamedSubject: + Enabled: false + +RSpec/NestedGroups: + Max: 6 + +RSpec/MultipleMemoizedHelpers: + Max: 8 + +RSpec/VerifiedDoubles: + Enabled: false + +RSpec/ContextWording: + Enabled: false + +RSpec/MessageSpies: + Enabled: false + +RSpec/IndexedLet: + Enabled: false + +Layout/LineLength: + Enabled: false + +Naming/VariableNumber: + Enabled: false + +Naming/FileName: + Exclude: + - lib/simple-navigation.rb + +Style/BlockDelimiters: + Enabled: false + +Capybara/NegationMatcher: + Enabled: false diff --git a/spec/fake_app/config/navigation.rb b/spec/fake_app/config/navigation.rb index 417d1d49..2c138969 100644 --- a/spec/fake_app/config/navigation.rb +++ b/spec/fake_app/config/navigation.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + SimpleNavigation::Configuration.run do |navigation| navigation.items do |nav| - nav.item :item_1, 'Item 1', '/item_1', html: {class: 'item_1'}, link_html: {id: 'link_1'} - nav.item :item_2, 'Item 2', '/item_2', html: {class: 'item_2'}, link_html: {id: 'link_2'} + nav.item :item_1, 'Item 1', '/item_1', html: { class: 'item_1' }, link_html: { id: 'link_1' } + nav.item :item_2, 'Item 2', '/item_2', html: { class: 'item_2' }, link_html: { id: 'link_2' } end end diff --git a/spec/fake_app/rails_app.rb b/spec/fake_app/rails_app.rb index 7f074c5c..a0d514b8 100644 --- a/spec/fake_app/rails_app.rb +++ b/spec/fake_app/rails_app.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV['RAILS_ENV'] ||= 'test' require 'action_controller/railtie' @@ -9,20 +11,20 @@ class Application < Rails::Application config.cache_classes = true config.eager_load = false config.root = __dir__ - config.secret_token = 'x'*100 + config.secret_token = 'x' * 100 config.session_store :cookie_store, key: '_myapp_session' end class TestsController < ActionController::Base def base - render inline: <<-END + render inline: <<-HTML <%= render_navigation %> - END + HTML end end end diff --git a/spec/initializers/coveralls.rb b/spec/initializers/coveralls.rb index fe39ae25..0c54d7b3 100644 --- a/spec/initializers/coveralls.rb +++ b/spec/initializers/coveralls.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'coveralls' Coveralls.wear! diff --git a/spec/initializers/have_css_matcher.rb b/spec/initializers/have_css_matcher.rb index ed60041a..9fe225d4 100644 --- a/spec/initializers/have_css_matcher.rb +++ b/spec/initializers/have_css_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_css do |expected, times| match do |actual| selector = Nokogiri::HTML(actual).css(expected) @@ -10,10 +12,10 @@ end failure_message do |actual| - "expected #{actual.to_s} to have #{times || 1} elements matching '#{expected}'" + "expected #{actual} to have #{times || 1} elements matching '#{expected}'" end failure_message_when_negated do |actual| - "expected #{actual.to_s} not to have #{times || 1} elements matching '#{expected}'" + "expected #{actual} not to have #{times || 1} elements matching '#{expected}'" end end diff --git a/spec/initializers/memfs.rb b/spec/initializers/memfs.rb index 6369794c..8873fefd 100644 --- a/spec/initializers/memfs.rb +++ b/spec/initializers/memfs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'memfs' RSpec.configure do |config| diff --git a/spec/initializers/rails.rb b/spec/initializers/rails.rb index 59201bb3..c3b029c6 100644 --- a/spec/initializers/rails.rb +++ b/spec/initializers/rails.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + begin require 'rails' -rescue LoadError +rescue LoadError # rubocop:disable Lint/SuppressedException end diff --git a/spec/initializers/rspec.rb b/spec/initializers/rspec.rb index c08eb59f..9be249c7 100644 --- a/spec/initializers/rspec.rb +++ b/spec/initializers/rspec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.expect_with(:rspec) do |c| c.syntax = :expect diff --git a/spec/integration/rendering_navigation_spec.rb b/spec/integration/rendering_navigation_spec.rb index 1cf76ed3..4d6cc5ec 100644 --- a/spec/integration/rendering_navigation_spec.rb +++ b/spec/integration/rendering_navigation_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + RSpec.feature 'Rendering navigation' do background do SimpleNavigation.set_env(RailsApp::Application.root, 'test') end - scenario 'Rendering basic navigation', type: :feature do + scenario 'Rendering basic navigation', type: :feature do # rubocop:disable RSpec/MultipleExpectations visit '/base_spec' expect(page).to have_content('Item 1') expect(page).to have_content('Item 2') - expect(page).to have_selector('li.item_1 a#link_1') - expect(page).to have_selector('li.item_2 a#link_2') + expect(page).to have_css('li.item_1 a#link_1') + expect(page).to have_css('li.item_2 a#link_2') end end diff --git a/spec/simple_navigation/adapters/padrino_spec.rb b/spec/simple_navigation/adapters/padrino_spec.rb index 48841147..0c07a21b 100644 --- a/spec/simple_navigation/adapters/padrino_spec.rb +++ b/spec/simple_navigation/adapters/padrino_spec.rb @@ -1,22 +1,22 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Adapters::Padrino do - let(:adapter) { SimpleNavigation::Adapters::Padrino.new(context) } + let(:adapter) { described_class.new(context) } let(:content) { double(:content) } let(:context) { double(:context, request: request) } let(:request) { double(:request) } describe '#link_to' do it 'delegates to context' do - expect(context).to receive(:link_to) - .with('name', 'url', {:my_option => true}) - adapter.link_to('name', 'url', :my_option => true) + expect(context).to receive(:link_to).with('name', 'url', { my_option: true }) + adapter.link_to('name', 'url', my_option: true) end end describe '#content_tag' do - it 'delegates to context' do - expect(content).to receive(:html_safe).and_return('content') - expect(context).to receive(:content_tag) - .with('type', 'content', {my_option: true}) + it 'delegates to context' do # rubocop:disable RSpec/MultipleExpectations + expect(content).to receive(:html_safe).and_return('content') # rubocop:disable RSpec/StubbedMock + expect(context).to receive(:content_tag).with('type', 'content', { my_option: true }) adapter.content_tag('type', content, my_option: true) end end diff --git a/spec/simple_navigation/adapters/rails_spec.rb b/spec/simple_navigation/adapters/rails_spec.rb index 1d9c6704..52340a6c 100644 --- a/spec/simple_navigation/adapters/rails_spec.rb +++ b/spec/simple_navigation/adapters/rails_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Adapters::Rails do let(:action_controller) { ActionController::Base } - let(:adapter) { SimpleNavigation::Adapters::Rails.new(context) } + let(:adapter) { described_class.new(context) } let(:context) { double(:context, controller: controller) } let(:controller) { double(:controller) } let(:request) { double(:request) } @@ -17,8 +19,7 @@ end it 'extends the ActionController::Base with the Helpers' do - expect(action_controller).to receive(:include) - .with(SimpleNavigation::Helpers) + expect(action_controller).to receive(:include).with(SimpleNavigation::Helpers) simple_navigation.register end @@ -184,7 +185,7 @@ before { adapter.instance_variable_set(:@template, nil) } it 'raises an exception' do - expect{ adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') + expect { adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') end end end @@ -204,13 +205,13 @@ before { allow(adapter).to receive_messages(template: nil) } it 'returns false' do - expect(adapter.current_page?(:page)).to be_falsey + expect(adapter).not_to be_current_page(:page) end end context 'when the given url is nil' do it 'returns false' do - expect(adapter.current_page?(nil)).to be_falsey + expect(adapter).not_to be_current_page(nil) end end end @@ -226,21 +227,17 @@ after { SimpleNavigation.config.consider_item_names_as_safe = false } it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:link_to) - .with('safe_text', 'url', options) + expect(template).to receive(:link_to).with('safe_text', 'url', options) adapter.link_to('text', 'url', options) end end context 'with considering item names as UNsafe (default)' do - it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:link_to) - .with('text', 'url', options) + expect(template).to receive(:link_to).with('text', 'url', options) adapter.link_to('text', 'url', options) end end - end context "when the adapter's template is not set" do @@ -259,8 +256,7 @@ before { allow(adapter).to receive_messages(template: template, html_safe: 'safe_text') } it 'delegates the call to the template (with html_safe text)' do - expect(template).to receive(:content_tag) - .with(:div, 'safe_text', options) + expect(template).to receive(:content_tag).with(:div, 'safe_text', options) adapter.content_tag(:div, 'text', options) end end @@ -273,5 +269,4 @@ end end end - end diff --git a/spec/simple_navigation/adapters/sinatra_spec.rb b/spec/simple_navigation/adapters/sinatra_spec.rb index 14a0534f..1a014790 100644 --- a/spec/simple_navigation/adapters/sinatra_spec.rb +++ b/spec/simple_navigation/adapters/sinatra_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Adapters::Sinatra do - let(:adapter) { SimpleNavigation::Adapters::Sinatra.new(context) } + let(:adapter) { described_class.new(context) } let(:context) { double(:context) } let(:request) { double(:request, fullpath: '/full?param=true', path: '/full') } @@ -9,7 +11,7 @@ context "when adapter's context is not set" do it 'raises an exception' do allow(adapter).to receive_messages(context: nil) - expect{ adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') + expect { adapter.context_for_eval }.to raise_error(RuntimeError, 'no context set for evaluation the config file') end end @@ -57,8 +59,7 @@ context 'when URL is encoded' do before do - allow(request).to receive_messages(fullpath: '/full%20with%20spaces?param=true', - path: '/full%20with%20spaces') + allow(request).to receive_messages(fullpath: '/full%20with%20spaces?param=true', path: '/full%20with%20spaces') end it_behaves_like 'detecting current page', '/full%20with%20spaces?param=true', true diff --git a/spec/simple_navigation/config_file_finder_spec.rb b/spec/simple_navigation/config_file_finder_spec.rb index bee7111a..de992fcd 100644 --- a/spec/simple_navigation/config_file_finder_spec.rb +++ b/spec/simple_navigation/config_file_finder_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + require 'fileutils' require 'simple_navigation/config_file_finder' RSpec.describe SimpleNavigation::ConfigFileFinder do - subject(:finder) { SimpleNavigation::ConfigFileFinder.new(paths) } + subject(:finder) { described_class.new(paths) } let(:paths) { ['/path/one', '/path/two'] } - describe '#find', memfs: true do + describe '#find', :memfs do before { FileUtils.mkdir_p(paths) } context 'when the context is :default' do @@ -40,7 +42,7 @@ context 'and no other_navigation.rb file is found in the paths' do it 'raise an exception' do - expect{ finder.find(context) }.to raise_error(RuntimeError, /Config file 'other_navigation.rb' not found in path\(s\)/) + expect { finder.find(context) }.to raise_error(RuntimeError, /Config file 'other_navigation.rb' not found in path\(s\)/) end end end diff --git a/spec/simple_navigation/config_file_spec.rb b/spec/simple_navigation/config_file_spec.rb index b70c4251..54ec7037 100644 --- a/spec/simple_navigation/config_file_spec.rb +++ b/spec/simple_navigation/config_file_spec.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + require 'simple_navigation/config_file' RSpec.describe SimpleNavigation::ConfigFile do - subject(:config_file) { SimpleNavigation::ConfigFile.new(context) } + subject(:config_file) { described_class.new(context) } let(:context) { :default } diff --git a/spec/simple_navigation/configuration_spec.rb b/spec/simple_navigation/configuration_spec.rb index f8f405c4..29aea865 100644 --- a/spec/simple_navigation/configuration_spec.rb +++ b/spec/simple_navigation/configuration_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Configuration do - subject(:config) { SimpleNavigation::Configuration.instance } + subject(:config) { described_class.instance } describe '.run' do - it "yields the singleton Configuration object" do - expect{ |blk| SimpleNavigation::Configuration.run(&blk) }.to yield_with_args(config) + it 'yields the singleton Configuration object' do + expect { |blk| described_class.run(&blk) }.to yield_with_args(config) end end describe '.eval_config' do - let(:config_files) {{ default: 'default', my_context: 'my_context' }} + let(:config_files) { { default: 'default', my_context: 'my_context' } } let(:eval_context) { double(:eval_context) } before do @@ -17,17 +19,17 @@ receive_messages(context_for_eval: eval_context, config_files: config_files) end - context "with default navigation context" do - it "calls instance_eval with the default config_file-string inside the context" do + context 'with default navigation context' do + it 'calls instance_eval with the default config_file-string inside the context' do expect(eval_context).to receive(:instance_eval).with('default') - SimpleNavigation::Configuration.eval_config + described_class.eval_config end end context 'with non default navigation context' do - it "calls instance_eval with the specified config_file-string inside the context" do + it 'calls instance_eval with the specified config_file-string inside the context' do expect(eval_context).to receive(:instance_eval).with('my_context') - SimpleNavigation::Configuration.eval_config(:my_context) + described_class.eval_config(:my_context) end end end @@ -88,23 +90,23 @@ let(:provider) { double(:provider) } it 'raises an exception' do - expect{ config.items(provider) {} }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') + expect { config.items(provider) {} }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') # rubocop:disable Lint/EmptyBlock end end context 'when no items_provider is specified' do it 'yields an new ItemContainer' do - expect{ |blk| config.items(&blk) }.to yield_with_args(container) + expect { |blk| config.items(&blk) }.to yield_with_args(container) end it 'assigns the ItemContainer to an instance-var' do - config.items {} + config.items {} # rubocop:disable Lint/EmptyBlock expect(config.primary_navigation).to be container end it "doesn't set the items on the container" do expect(container).not_to receive(:items=) - config.items {} + config.items {} # rubocop:disable Lint/EmptyBlock end end end @@ -137,8 +139,8 @@ end context 'when items_provider is not specified' do - it "raises an exception" do - expect{ config.items }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') + it 'raises an exception' do + expect { config.items }.to raise_error(RuntimeError, 'please specify either items_provider or block, but not both') end end end diff --git a/spec/simple_navigation/helpers_spec.rb b/spec/simple_navigation/helpers_spec.rb index 2fbf1213..802086ce 100644 --- a/spec/simple_navigation/helpers_spec.rb +++ b/spec/simple_navigation/helpers_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Helpers do subject(:controller) { test_controller_class.new } @@ -12,9 +14,9 @@ before do allow(SimpleNavigation::Configuration).to receive(:eval_config) allow(SimpleNavigation).to receive_messages(load_config: nil, - primary_navigation: navigation, - config_file?: true, - context_for_eval: controller) + primary_navigation: navigation, + config_file?: true, + context_for_eval: controller) select_an_item(navigation[item]) if item end @@ -25,7 +27,7 @@ expect(controller.active_navigation_item_name).to eq '' end - it "returns an empty string for level: 1" do + it 'returns an empty string for level: 1' do item_name = controller.active_navigation_item_name(level: 1) expect(item_name).to eq '' end @@ -95,15 +97,11 @@ describe '#active_navigation_item_key' do context 'when no item is selected' do - it 'returns nil' do - expect(controller.active_navigation_item_key).to be_nil - end - it 'returns nil for no parameters' do expect(controller.active_navigation_item_key).to be_nil end - it "returns nil for level: 1" do + it 'returns nil for level: 1' do item_key = controller.active_navigation_item_key(level: 1) expect(item_key).to be_nil end @@ -177,7 +175,7 @@ expect(controller.active_navigation_item).to be_nil end - it "returns nil for level: 1" do + it 'returns nil for level: 1' do item_key = controller.active_navigation_item(level: 1) expect(item_key).to be_nil end @@ -251,7 +249,7 @@ expect(controller.active_navigation_item_container).to be navigation end - it "returns the primary navigation for level: 1" do + it 'returns the primary navigation for level: 1' do item_container = controller.active_navigation_item_container(level: 1) expect(item_container).to be navigation end @@ -320,7 +318,7 @@ it 'looks up the active_item_container based on the level' do expect(SimpleNavigation).to receive(:active_item_container_for) - .with(:all) + .with(:all) controller.render_navigation end @@ -343,7 +341,7 @@ context 'when the :level option is set' do context 'and its value is 1' do it 'calls render on the primary navigation' do - expect(navigation).to receive(:render).with({level: 1}) + expect(navigation).to receive(:render).with({ level: 1 }) controller.render_navigation(level: 1) end end @@ -358,19 +356,19 @@ it 'finds the selected sub navigation for the specified level' do expect(SimpleNavigation).to receive(:active_item_container_for) - .with(2) + .with(2) controller.render_navigation(level: 2) end it 'calls render on the active item_container' do - expect(item_container).to receive(:render).with({level: 2}) + expect(item_container).to receive(:render).with({ level: 2 }) controller.render_navigation(level: 2) end end context "and the active_item_container isn't set" do it "doesn't raise an exception" do - expect{ + expect { controller.render_navigation(level: 2) }.not_to raise_error end @@ -379,7 +377,7 @@ context "and its value isn't a valid level" do it 'raises an exception' do - expect{ + expect { controller.render_navigation(level: :invalid) }.to raise_error(ArgumentError, 'Invalid navigation level: invalid') end @@ -390,14 +388,14 @@ before { allow(SimpleNavigation).to receive_messages(active_item_container_for: navigation) } it 'treats it like the :level option' do - expect(navigation).to receive(:render).with({level: 2}) + expect(navigation).to receive(:render).with({ level: 2 }) controller.render_navigation(levels: 2) end end context 'when a block is given' do it 'calls the block passing it an item container' do - expect{ |blk| + expect { |blk| controller.render_navigation(&blk) }.to yield_with_args(SimpleNavigation::ItemContainer) end @@ -407,11 +405,11 @@ before { allow(SimpleNavigation).to receive_messages(primary_navigation: nil) } it 'raises an exception' do - expect{controller.render_navigation}.to raise_error(RuntimeError, 'no primary navigation defined, either use a navigation config file or pass items directly to render_navigation') + expect { controller.render_navigation }.to raise_error(RuntimeError, 'no primary navigation defined, either use a navigation config file or pass items directly to render_navigation') end end - context "when active_item_container is set" do + context 'when active_item_container is set' do let(:active_item_container) { double(:container).as_null_object } before do diff --git a/spec/simple_navigation/item_adapter_spec.rb b/spec/simple_navigation/item_adapter_spec.rb index a54c3505..a32fd537 100644 --- a/spec/simple_navigation/item_adapter_spec.rb +++ b/spec/simple_navigation/item_adapter_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::ItemAdapter do - let(:item_adapter) { SimpleNavigation::ItemAdapter.new(item) } + let(:item_adapter) { described_class.new(item) } context 'when item is an object' do let(:item) { double(:item, key: 'key', name: 'name', url: 'url') } @@ -84,15 +86,13 @@ it 'creates an Item' do expect(SimpleNavigation::Item).to receive(:new) - .with(container, 'key', 'name', 'url', {}) + .with(container, 'key', 'name', 'url', {}) item_adapter.to_simple_navigation_item(container) end end end context 'when item is a kind of hash' do - class ModifiedHash < Hash; end - let(:item) { ModifiedHash[key: 'key', url: 'url', name: 'name'] } shared_examples 'delegating to item' do |meth| @@ -150,10 +150,10 @@ class ModifiedHash < Hash; end end context 'and items is not empty' do - before { item[:items] = ['not', 'empty'] } + before { item[:items] = %w[not empty] } it 'returns the items' do - expect(item_adapter.items).to eq ['not', 'empty'] + expect(item_adapter.items).to eq %w[not empty] end end end @@ -173,7 +173,7 @@ class ModifiedHash < Hash; end it 'passes the right arguments to Item' do expect(SimpleNavigation::Item).to receive(:new) - .with(container, 'key', 'name', 'url', {}) + .with(container, 'key', 'name', 'url', {}) item_adapter.to_simple_navigation_item(container) end diff --git a/spec/simple_navigation/item_container_spec.rb b/spec/simple_navigation/item_container_spec.rb index 464946c0..f7f23e0d 100644 --- a/spec/simple_navigation/item_container_spec.rb +++ b/spec/simple_navigation/item_container_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::ItemContainer do - subject(:item_container) { SimpleNavigation::ItemContainer.new } + subject(:item_container) { described_class.new } shared_examples 'adding the item to the list' do it 'adds the item to the list' do @@ -24,7 +26,7 @@ end describe '#dom_attributes' do - let(:dom_attributes) {{ id: 'test_id', class: 'test_class' }} + let(:dom_attributes) { { id: 'test_id', class: 'test_class' } } before { item_container.dom_attributes = dom_attributes } @@ -33,7 +35,7 @@ end context 'when the dom_attributes do not contain any id or class' do - let(:dom_attributes) {{ test: 'test' }} + let(:dom_attributes) { { test: 'test' } } context "and the container hasn't any dom_id" do it "returns the contaier's dom_attributes without any id" do @@ -66,7 +68,7 @@ end describe '#items=' do - let(:item) {{ key: :my_key, name: 'test', url: '/' }} + let(:item) { { key: :my_key, name: 'test', url: '/' } } let(:items) { [item] } let(:item_adapter) { double(:item_adapter).as_null_object } let(:real_item) { double(:real_item) } @@ -74,8 +76,8 @@ before do allow(SimpleNavigation::ItemAdapter).to receive_messages(new: item_adapter) allow(item_adapter).to receive(:to_simple_navigation_item) - .with(item_container) - .and_return(real_item) + .with(item_container) + .and_return(real_item) end context 'when the item should be added' do @@ -123,7 +125,7 @@ let(:item_1) { double(:item, selected?: false) } let(:item_2) { double(:item, selected?: false) } - before(:each) do + before do allow(SimpleNavigation).to receive_messages(current_navigation_for: :nav) allow(item_container).to receive_messages(:[] => nil) item_container.instance_variable_set(:@items, [item_1, item_2]) @@ -174,7 +176,7 @@ it 'calls recursively on the sub_navigation' do expect(sub_navigation).to receive(:active_item_container_for) - .with(2) + .with(2) item_container.active_item_container_for(2) end end @@ -188,7 +190,7 @@ before do allow(item_container).to receive_messages(selected_sub_navigation?: true, - selected_item: selected_item) + selected_item: selected_item) allow(selected_item).to receive_messages(sub_navigation: sub_navigation) end @@ -208,32 +210,31 @@ end describe '#item' do - let(:options) { Hash.new } + let(:options) { {} } let(:item) { double(:item) } context 'when a block is given' do - let(:block) { proc{} } + let(:block) { proc {} } let(:sub_container) { double(:sub_container) } it 'yields a new ItemContainer' do - allow_any_instance_of(SimpleNavigation::Item).to \ - receive_messages(sub_navigation: sub_container) + allow_any_instance_of(SimpleNavigation::Item).to receive_messages(sub_navigation: sub_container) # rubocop:disable RSpec/AnyInstance - expect{ |blk| + expect { |blk| item_container.item('key', 'name', 'url', options, &blk) }.to yield_with_args(sub_container) end - it "creates a new Item with the given params and block" do + it 'creates a new Item with the given params and block' do allow(SimpleNavigation::Item).to receive(:new) - .with(item_container, 'key', 'name', 'url', options, &block) - .and_return(item) + .with(item_container, 'key', 'name', 'url', options, &block) + .and_return(item) item_container.item('key', 'name', 'url', options, &block) expect(item_container.items).to include(item) end it 'adds the created item to the list of items' do - item_container.item('key', 'name', 'url', options) {} + item_container.item('key', 'name', 'url', options) {} # rubocop:disable Lint/EmptyBlock expect(item_container.items).not_to include(item) end end @@ -241,15 +242,15 @@ context 'when no block is given' do it 'creates a new Item with the given params and no sub navigation' do allow(SimpleNavigation::Item).to receive(:new) - .with(item_container, 'key', 'name', 'url', options) - .and_return(item) + .with(item_container, 'key', 'name', 'url', options) + .and_return(item) item_container.item('key', 'name', 'url', options) expect(item_container.items).to include(item) end it 'adds the created item to the list of items' do allow(SimpleNavigation::Item).to receive_messages(new: item) - item_container.item('key', 'name', 'url', options) {} + item_container.item('key', 'name', 'url', options) {} # rubocop:disable Lint/EmptyBlock expect(item_container.items).to include(item) end end @@ -257,13 +258,13 @@ describe 'Optional url and optional options' do context 'when item specifed without url or options' do it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name'] } + let(:args) { %w[key name] } end end context 'when item is specified with only a url' do it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', 'url'] } + let(:args) { %w[key name url] } end end @@ -276,13 +277,13 @@ context 'and options contains a negative condition' do it_behaves_like 'not adding the item to the list' do - let(:args) { ['key', 'name', nil, { if: ->{ false }, option: true }] } + let(:args) { ['key', 'name', nil, { if: -> { false }, option: true }] } end end context 'and options contains a positive condition' do it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', nil, { if: ->{ true }, option: true }] } + let(:args) { ['key', 'name', nil, { if: -> { true }, option: true }] } end end end @@ -296,13 +297,13 @@ context 'and options contains a negative condition' do it_behaves_like 'not adding the item to the list' do - let(:args) { ['key', 'name', 'url', { if: ->{ false }, option: true }] } + let(:args) { ['key', 'name', 'url', { if: -> { false }, option: true }] } end end context 'and options contains a positive condition' do it_behaves_like 'adding the item to the list' do - let(:args) { ['key', 'name', 'url', { if: ->{ true }, option: true }] } + let(:args) { ['key', 'name', 'url', { if: -> { true }, option: true }] } end end end @@ -313,20 +314,20 @@ end it 'does not raise an exception' do - expect{ + expect { item_container.item('key', 'name', 'url', options) }.not_to raise_error end end - describe "container options" do + describe 'container options' do before do allow(item_container).to receive_messages(should_add_item?: add_item) item_container.item :key, 'name', 'url', options end context 'when the container :id option is specified' do - let(:options) {{ container: { id: 'c_id' } }} + let(:options) { { container: { id: 'c_id' } } } context 'and the item should be added' do let(:add_item) { true } @@ -346,7 +347,7 @@ end context 'when the container :class option is specified' do - let(:options) {{ container: { class: 'c_class' } }} + let(:options) { { container: { class: 'c_class' } } } context 'and the item should be added' do let(:add_item) { true } @@ -366,7 +367,7 @@ end context 'when the container :attributes option is specified' do - let(:options) {{ container: { attributes: { option: true } } }} + let(:options) { { container: { attributes: { option: true } } } } context 'and the item should be added' do let(:add_item) { true } @@ -386,7 +387,7 @@ end context 'when the container :selected_class option is specified' do - let(:options) {{ container: { selected_class: 'sel_class' } }} + let(:options) { { container: { selected_class: 'sel_class' } } } context 'and the item should be added' do let(:add_item) { true } @@ -409,7 +410,7 @@ describe 'Conditions' do context 'when an :if option is given' do - let(:options) {{ if: proc{condition} }} + let(:options) { { if: proc { condition } } } let(:condition) { nil } context 'and it evals to true' do @@ -432,7 +433,7 @@ context 'and it is not a proc or a method' do it 'raises an error' do - expect{ + expect { item_container.item('key', 'name', 'url', { if: 'text' }) }.to raise_error(ArgumentError, ':if or :unless must be procs or lambdas') end @@ -440,7 +441,7 @@ end context 'when an :unless option is given' do - let(:options) {{ unless: proc{condition} }} + let(:options) { { unless: proc { condition } } } let(:condition) { nil } context 'and it evals to false' do @@ -491,7 +492,7 @@ context 'and is specified as a class' do it 'instantiates the passed renderer_class with the options' do expect(renderer_class).to receive(:new) - .with({renderer: renderer_class}) + .with({ renderer: renderer_class }) item_container.render(renderer: renderer_class) end @@ -508,8 +509,8 @@ } end - it "instantiates the passed renderer_class with the options" do - expect(renderer_class).to receive(:new).with({renderer: :my_renderer}) + it 'instantiates the passed renderer_class with the options' do + expect(renderer_class).to receive(:new).with({ renderer: :my_renderer }) item_container.render(renderer: :my_renderer) end @@ -521,7 +522,7 @@ end context 'when no renderer is specified' do - let(:options) { Hash.new } + let(:options) { {} } before { allow(item_container).to receive_messages(renderer: renderer_class) } @@ -556,7 +557,7 @@ end describe '#level_for_item' do - before(:each) do + before do item_container.item(:p1, 'p1', 'p1') item_container.item(:p2, 'p2', 'p2') do |p2| p2.item(:s1, 's1', 's1') @@ -570,7 +571,7 @@ end shared_examples 'returning the level of an item' do |item, level| - specify{ expect(item_container.level_for_item(item)).to eq level } + specify { expect(item_container.level_for_item(item)).to eq level } end it_behaves_like 'returning the level of an item', :p1, 1 diff --git a/spec/simple_navigation/item_spec.rb b/spec/simple_navigation/item_spec.rb index 12bd2533..aef7de86 100644 --- a/spec/simple_navigation/item_spec.rb +++ b/spec/simple_navigation/item_spec.rb @@ -1,16 +1,18 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Item do let!(:item_container) { SimpleNavigation::ItemContainer.new } let(:adapter) { double(:adapter) } let(:item_args) { [item_container, :my_key, 'name', url, options] } - let(:item) { SimpleNavigation::Item.new(*item_args) } - let(:options) { Hash.new } + let(:item) { described_class.new(*item_args) } + let(:options) { {} } let(:url) { 'url' } before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } describe '#highlights_on' do - let(:options) {{ highlights_on: :test }} + let(:options) { { highlights_on: :test } } it "returns the item's highlights_on option" do expect(item.highlights_on).to eq :test @@ -29,14 +31,14 @@ context 'when a block is given' do it_behaves_like 'creating sub navigation container' do - let(:item) { SimpleNavigation::Item.new(*item_args) {} } + let(:item) { described_class.new(*item_args) {} } # rubocop:disable Lint/EmptyBlock end it 'calls the block' do allow(SimpleNavigation::ItemContainer).to receive_messages(new: subnav_container) - expect{ |blk| - SimpleNavigation::Item.new(*item_args, &blk) + expect { |blk| + described_class.new(*item_args, &blk) }.to yield_with_args(subnav_container) end end @@ -44,18 +46,18 @@ context 'when no block is given' do context 'and items are given' do let(:items) { [] } - let(:options) {{ items: items }} + let(:options) { { items: items } } it_behaves_like 'creating sub navigation container' - it "sets the items on the subnav_container" do + it 'sets the items on the subnav_container' do expect(item.sub_navigation.items).to eq items end end context 'and no items are given' do it "doesn't create a new ItemContainer" do - item = SimpleNavigation::Item.new(*item_args) + item = described_class.new(*item_args) expect(item.sub_navigation).to be_nil end end @@ -63,7 +65,7 @@ end context 'when a :method option is given' do - let(:options) {{ method: :delete }} + let(:options) { { method: :delete } } it "sets the item's method" do expect(item.method).to eq :delete @@ -78,7 +80,7 @@ context 'when an :highlights_on option is given' do let(:highlights_on) { double(:highlights_on) } - let(:options) {{ highlights_on: highlights_on }} + let(:options) { { highlights_on: highlights_on } } it "sets the item's highlights_on" do expect(item.highlights_on).to eq highlights_on @@ -99,7 +101,7 @@ end context 'and it is a proc' do - let(:url) { proc{ "my_" + "url" } } + let(:url) { proc { 'my_url' } } it "sets the item's url accordingly" do expect(item.url).to eq 'my_url' @@ -132,7 +134,7 @@ end context 'when url and options are given' do - let(:options) {{ html: { option: true } }} + let(:options) { { html: { option: true } } } before { allow(adapter).to receive_messages(current_page?: false) } @@ -150,7 +152,7 @@ end describe '#link_html_options' do - let(:options) {{ link_html: :test }} + let(:options) { { link_html: :test } } it "returns the item's link_html option" do expect(item.link_html_options).to eq :test @@ -158,7 +160,7 @@ end describe '#method' do - let(:options) {{ method: :test }} + let(:options) { { method: :test } } it "returns the item's method option" do expect(item.method).to eq :test @@ -168,7 +170,7 @@ describe '#name' do before do allow(SimpleNavigation.config).to \ - receive_messages(name_generator: proc{ |name| "#{name}" }) + receive_messages(name_generator: proc { |name| "#{name}" }) end context 'when no option is given' do @@ -181,7 +183,7 @@ context 'and the name_generator uses only the item itself' do before do allow(SimpleNavigation.config).to \ - receive_messages(name_generator: proc{ |name, item| "#{item.key}" }) + receive_messages(name_generator: proc { |_name, item| "#{item.key}" }) end it 'uses the default name_generator' do @@ -245,7 +247,7 @@ double(:config, auto_highlight: true, highlight_on_subpath: true, ignore_query_params_on_auto_highlight: true, ignore_anchors_on_auto_highlight: true) end - context "but item has no url" do + context 'but item has no url' do let(:url) { nil } it 'returns false' do @@ -283,7 +285,7 @@ before { allow(adapter).to receive_messages(request_uri: '/test') } context 'and the current url matches the expression' do - let(:options) {{ highlights_on: /test/ }} + let(:options) { { highlights_on: /test/ } } it 'returns true' do expect(item.selected?).to be true @@ -291,7 +293,7 @@ end context 'and the current url does not match the expression' do - let(:options) {{ highlights_on: /other/ }} + let(:options) { { highlights_on: /other/ } } it 'returns false' do expect(item.selected?).to be false @@ -301,7 +303,7 @@ context 'and it is a callable object' do context 'and the call returns true' do - let(:options) {{ highlights_on: -> { true } }} + let(:options) { { highlights_on: -> { true } } } it 'returns true' do expect(item.selected?).to be true @@ -309,7 +311,7 @@ end context 'and the call returns false' do - let(:options) {{ highlights_on: -> { false } }} + let(:options) { { highlights_on: -> { false } } } it 'returns false' do expect(item.selected?).to be false @@ -318,7 +320,7 @@ end context 'and it is the :subpath symbol' do - let(:options) {{ highlights_on: :subpath }} + let(:options) { { highlights_on: :subpath } } context "and the current url is a sub path of the item's url" do before do @@ -342,10 +344,10 @@ end context 'and it is non usable' do - let(:options) {{ highlights_on: :hello }} + let(:options) { { highlights_on: :hello } } it 'raises an exception' do - expect{ item.selected? }.to raise_error(ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath') + expect { item.selected? }.to raise_error(ArgumentError, ':highlights_on must be a Regexp, Proc or :subpath') end end end @@ -381,7 +383,7 @@ let(:selected_classes) { 'selected simple-navigation-active-leaf' } context 'when the :class option is given' do - let(:options) {{ html: { class: 'my_class' } }} + let(:options) { { html: { class: 'my_class' } } } context 'and the item is selected' do before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } @@ -408,17 +410,17 @@ context 'and the item is selected' do before { allow(item).to receive_messages(selected?: true, selected_by_condition?: true) } - it "sets the default html classes of a selected item" do + it 'sets the default html classes of a selected item' do expect(item.html_options[:class]).to include(selected_classes) end end context "and the item isn't selected" do - before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } + before { allow(item).to receive_messages(selected?: false, selected_by_condition?: false) } - it "doesn't set any html class on the item" do - expect(item.html_options[:class]).to be_blank - end + it "doesn't set any html class on the item" do + expect(item.html_options[:class]).to be_blank + end end end @@ -429,7 +431,7 @@ end describe 'when the :id option is given' do - let(:options) {{ html: { id: 'my_id' } }} + let(:options) { { html: { id: 'my_id' } } } before do allow(SimpleNavigation.config).to receive_messages(autogenerate_item_ids: generate_ids) diff --git a/spec/simple_navigation/items_provider_spec.rb b/spec/simple_navigation/items_provider_spec.rb index 42552818..a3623a2a 100644 --- a/spec/simple_navigation/items_provider_spec.rb +++ b/spec/simple_navigation/items_provider_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::ItemsProvider do - let(:items_provider) { SimpleNavigation::ItemsProvider.new(provider) } + let(:items_provider) { described_class.new(provider) } describe '#items' do let(:items) { double(:items) } @@ -35,7 +37,7 @@ let(:provider) { double(:provider) } it 'raises an exception' do - expect{ items_provider.items }.to raise_error(RuntimeError, /items_provider either must be a symbol .*, an object .* or an enumerable/) + expect { items_provider.items }.to raise_error(RuntimeError, /items_provider either must be a symbol .*, an object .* or an enumerable/) end end end diff --git a/spec/simple_navigation/renderer/base_spec.rb b/spec/simple_navigation/renderer/base_spec.rb index 6d6da5eb..f5d0c802 100644 --- a/spec/simple_navigation/renderer/base_spec.rb +++ b/spec/simple_navigation/renderer/base_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::Base do - subject(:base) { SimpleNavigation::Renderer::Base.new(options) } + subject(:base) { described_class.new(options) } let(:adapter) { double(:adapter) } - let(:options) { Hash.new } + let(:options) { {} } before { allow(SimpleNavigation).to receive_messages(adapter: adapter) } @@ -17,7 +19,7 @@ end describe '#initialize' do - it "sets the renderer adapter to the SimpleNavigation one" do + it 'sets the renderer adapter to the SimpleNavigation one' do expect(base.adapter).to be adapter end end @@ -30,14 +32,14 @@ describe '#render' do it "raise an exception to indicate it's a subclass responsibility" do - expect{ base.render(:container) }.to raise_error(NotImplementedError, 'subclass responsibility') + expect { base.render(:container) }.to raise_error(NotImplementedError, 'subclass responsibility') end end describe '#expand_all?' do context 'when :options is set' do context 'and the :expand_all option is true' do - let(:options) {{ expand_all: true }} + let(:options) { { expand_all: true } } it 'returns true' do expect(base.expand_all?).to be true @@ -45,7 +47,7 @@ end context 'and the :expand_all option is false' do - let(:options) {{ expand_all: false }} + let(:options) { { expand_all: false } } it 'returns false' do expect(base.expand_all?).to be false @@ -54,7 +56,7 @@ end context "when :options isn't set" do - let(:options) { Hash.new } + let(:options) { {} } it 'returns false' do expect(base.expand_all?).to be false @@ -65,7 +67,7 @@ describe '#skip_if_empty?' do context 'when :options is set' do context 'and the :skip_if_empty option is true' do - let(:options) {{ skip_if_empty: true }} + let(:options) { { skip_if_empty: true } } it 'returns true' do expect(base.skip_if_empty?).to be true @@ -73,7 +75,7 @@ end context 'and the :skip_if_empty option is false' do - let(:options) {{ skip_if_empty: false }} + let(:options) { { skip_if_empty: false } } it 'returns true' do expect(base.skip_if_empty?).to be false @@ -82,7 +84,7 @@ end context "when :options isn't set" do - let(:options) { Hash.new } + let(:options) { {} } it 'returns true' do expect(base.skip_if_empty?).to be false @@ -92,7 +94,7 @@ describe '#level' do context 'and the :level option is set' do - let(:options) {{ level: 1 }} + let(:options) { { level: 1 } } it 'returns the specified level' do expect(base.level).to eq 1 @@ -100,7 +102,7 @@ end context "and the :level option isn't set" do - let(:options) { Hash.new } + let(:options) { {} } it 'returns :all' do expect(base.level).to eq :all diff --git a/spec/simple_navigation/renderer/breadcrumbs_spec.rb b/spec/simple_navigation/renderer/breadcrumbs_spec.rb index ba3239d2..33cd1e8a 100644 --- a/spec/simple_navigation/renderer/breadcrumbs_spec.rb +++ b/spec/simple_navigation/renderer/breadcrumbs_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::Breadcrumbs do let!(:navigation) { setup_navigation('nav_id', 'nav_class') } let(:item) { nil } - let(:options) {{ level: :all }} + let(:options) { { level: :all } } let(:output) { renderer.render(navigation) } - let(:renderer) { SimpleNavigation::Renderer::Breadcrumbs.new(options) } + let(:renderer) { described_class.new(options) } before { select_an_item(navigation[item]) if item } @@ -43,7 +45,7 @@ end context 'and the :allow_classes_and_ids option is true' do - let(:options) {{ level: :all, allow_classes_and_ids: true }} + let(:options) { { level: :all, allow_classes_and_ids: true } } it "renders the 'a' tag with the selected class" do expect(output).to have_css('div a.selected') @@ -66,7 +68,7 @@ end context 'and the :prefix option is set' do - let(:options) {{ prefix: 'You are here: ' }} + let(:options) { { prefix: 'You are here: ' } } context 'and there are no items to render' do let(:item) { nil } @@ -98,7 +100,7 @@ end context 'when the :static_leaf option is true' do - let(:options) {{ level: :all, static_leaf: true }} + let(:options) { { level: :all, static_leaf: true } } it 'renders the items as links' do expect(output).to have_css('div a') diff --git a/spec/simple_navigation/renderer/json_spec.rb b/spec/simple_navigation/renderer/json_spec.rb index b7edc66d..a69d8e36 100644 --- a/spec/simple_navigation/renderer/json_spec.rb +++ b/spec/simple_navigation/renderer/json_spec.rb @@ -1,17 +1,18 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::Json do describe '#render' do let!(:navigation) { setup_navigation('nav_id', 'nav_class') } let(:item) { :invoices } - let(:options) {{ level: :all }} + let(:options) { { level: :all } } let(:output) { renderer.render(navigation) } let(:parsed_output) { JSON.parse(output) } - let(:renderer) { SimpleNavigation::Renderer::Json.new(options) } + let(:renderer) { described_class.new(options) } before { select_an_item(navigation[item]) if item } context 'when an item is selected' do - it 'renders the selected page' do invoices_item = parsed_output.find { |item| item['name'] == 'Invoices' } expect(invoices_item).to include('selected' => true) @@ -19,14 +20,12 @@ end context 'when the :as_hash option is true' do - let(:options) {{ level: :all, as_hash: true }} + let(:options) { { level: :all, as_hash: true } } - it 'returns every item as a hash' do + it 'returns every item as a hash' do # rubocop:disable RSpec/MultipleExpectations expect(output).to be_an Array - output.each do |item| - expect(item).to be_an Hash - end + expect(output).to all(be_an Hash) end it 'renders the selected page' do @@ -36,10 +35,8 @@ end context 'with options' do - it 'should render options for each item' do - parsed_output.each do |item| - expect(item).to have_key('options') - end + it 'renders options for each item' do + expect(parsed_output).to all(have_key('options')) end end diff --git a/spec/simple_navigation/renderer/links_spec.rb b/spec/simple_navigation/renderer/links_spec.rb index 6b4b35c7..6eef103a 100644 --- a/spec/simple_navigation/renderer/links_spec.rb +++ b/spec/simple_navigation/renderer/links_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::Links do describe '#render' do let!(:navigation) { setup_navigation('nav_id', 'nav_class') } @@ -5,7 +7,7 @@ let(:item) { nil } let(:options) { { level: :all } } let(:output) { renderer.render(navigation) } - let(:renderer) { SimpleNavigation::Renderer::Links.new(options) } + let(:renderer) { described_class.new(options) } before { select_an_item(navigation[item]) if item } @@ -55,8 +57,8 @@ end end - context "when the :join_with option is set" do - let(:options) {{ level: :all, join_with: ' | ' }} + context 'when the :join_with option is set' do + let(:options) { { level: :all, join_with: ' | ' } } it 'separates the items with the specified separator' do expect(output.scan(' | ').size).to eq 3 diff --git a/spec/simple_navigation/renderer/list_spec.rb b/spec/simple_navigation/renderer/list_spec.rb index 0a1dc904..d28f1edf 100644 --- a/spec/simple_navigation/renderer/list_spec.rb +++ b/spec/simple_navigation/renderer/list_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::List do let!(:navigation) { setup_navigation('nav_id', 'nav_class') } let(:item) { nil } let(:options) { { level: :all } } let(:output) { renderer.render(navigation) } - let(:renderer) { SimpleNavigation::Renderer::List.new(options) } + let(:renderer) { described_class.new(options) } before { select_an_item(navigation[item]) if item } @@ -56,7 +58,7 @@ end context 'when the :ordered option is true' do - let(:options) {{ level: :all, ordered: true }} + let(:options) { { level: :all, ordered: true } } it "renders an 'ol' tag for the navigation" do expect(output).to have_css('ol') diff --git a/spec/simple_navigation/renderer/text_spec.rb b/spec/simple_navigation/renderer/text_spec.rb index 27e398a1..583afb20 100644 --- a/spec/simple_navigation/renderer/text_spec.rb +++ b/spec/simple_navigation/renderer/text_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation::Renderer::Text do let!(:navigation) { setup_navigation('nav_id', 'nav_class') } let(:item) { nil } - let(:options) {{ level: :all }} + let(:options) { { level: :all } } let(:output) { renderer.render(navigation) } - let(:renderer) { SimpleNavigation::Renderer::Text.new(options) } + let(:renderer) { described_class.new(options) } before { select_an_item(navigation[item]) if item } @@ -35,8 +37,8 @@ expect(output).to eq 'Invoices Unpaid' end - context "and the :join_with option is set" do - let(:options) {{ level: :all, join_with: ' | ' }} + context 'and the :join_with option is set' do + let(:options) { { level: :all, join_with: ' | ' } } it 'separates the items with the specified separator' do expect(output).to eq 'Invoices | Unpaid' diff --git a/spec/simple_navigation_spec.rb b/spec/simple_navigation_spec.rb index 5d4abb8b..5c21ca46 100644 --- a/spec/simple_navigation_spec.rb +++ b/spec/simple_navigation_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.describe SimpleNavigation do before { subject.config_file_path = 'path_to_config' } @@ -53,7 +55,7 @@ end end - describe '.load_config', memfs: true do + describe '.load_config', :memfs do let(:paths) { ['/path/one', '/path/two'] } before do @@ -104,7 +106,7 @@ context "when the config file for the context doesn't exists" do it 'raises an exception' do - expect{ subject.load_config }.to raise_error(RuntimeError, /Config file 'navigation.rb' not found in path\(s\)/) + expect { subject.load_config }.to raise_error(RuntimeError, /Config file 'navigation.rb' not found in path\(s\)/) end end end @@ -150,7 +152,7 @@ context 'when level is something else' do it 'raises an exception' do - expect{ + expect { subject.active_item_container_for('something else') }.to raise_error(ArgumentError, 'Invalid navigation level: something else') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 25a2102f..358a3044 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + require 'initializers/have_css_matcher' require 'initializers/memfs' -#require 'initializers/coveralls' +# require 'initializers/coveralls' require 'initializers/rails' require 'initializers/rspec' require 'capybara/rspec' @@ -55,7 +57,7 @@ def setup_navigation(dom_id, dom_class) # FIXME: adding the :link option for the list renderer messes up the other # renderers -def setup_items(container) +def setup_items(container) # rubocop:disable Metrics/AbcSize container.item :users, 'Users', '/users', html: { id: 'users_id' }, link_html: { id: 'users_link_id' } container.item :invoices, 'Invoices', '/invoices' do |invoices| invoices.item :paid, 'Paid', '/invoices/paid' @@ -67,10 +69,10 @@ def setup_items(container) container.items.each do |item| allow(item).to receive_messages(selected?: false, selected_by_condition?: false) - if item.sub_navigation - item.sub_navigation.items.each do |item| - allow(item).to receive_messages(selected?: false, selected_by_condition?: false) - end + item.sub_navigation&.items&.each do |item| + allow(item).to receive_messages(selected?: false, selected_by_condition?: false) end end end + +class ModifiedHash < Hash; end From 6c360c2cb0fef41bd9e6b6adf09dd519d21289fd Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 01:25:52 +0100 Subject: [PATCH 11/30] Fix last Rubocop offenses --- lib/simple_navigation/adapters/nanoc.rb | 2 +- lib/simple_navigation/adapters/rails.rb | 2 +- lib/simple_navigation/adapters/sinatra.rb | 2 +- lib/simple_navigation/configuration.rb | 4 +--- lib/simple_navigation/item.rb | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/simple_navigation/adapters/nanoc.rb b/lib/simple_navigation/adapters/nanoc.rb index fbe679d2..17949b39 100644 --- a/lib/simple_navigation/adapters/nanoc.rb +++ b/lib/simple_navigation/adapters/nanoc.rb @@ -10,7 +10,7 @@ def register(root) end end - def initialize(ctx) + def initialize(ctx) # rubocop:disable Lint/MissingSuper @context = ctx end diff --git a/lib/simple_navigation/adapters/rails.rb b/lib/simple_navigation/adapters/rails.rb index c5a3253c..a4e44f4f 100644 --- a/lib/simple_navigation/adapters/rails.rb +++ b/lib/simple_navigation/adapters/rails.rb @@ -28,7 +28,7 @@ def self.register_controller_helpers end end - def initialize(context) + def initialize(context) # rubocop:disable Lint/MissingSuper @controller = extract_controller_from context @template = template_from @controller @request = @template.request if @template diff --git a/lib/simple_navigation/adapters/sinatra.rb b/lib/simple_navigation/adapters/sinatra.rb index d327a204..6a33d56a 100644 --- a/lib/simple_navigation/adapters/sinatra.rb +++ b/lib/simple_navigation/adapters/sinatra.rb @@ -9,7 +9,7 @@ def self.register(app) SimpleNavigation.set_env(app.root, app.environment) end - def initialize(context) + def initialize(context) # rubocop:disable Lint/MissingSuper @context = context @request = context.request end diff --git a/lib/simple_navigation/configuration.rb b/lib/simple_navigation/configuration.rb index 60656ef7..d54c717b 100644 --- a/lib/simple_navigation/configuration.rb +++ b/lib/simple_navigation/configuration.rb @@ -76,9 +76,7 @@ def id_generator # See SimpleNavigation::ItemAdapter for more details. # def items(items_provider = nil, &block) - if (items_provider && block) || (items_provider.nil? && block.nil?) - raise('please specify either items_provider or block, but not both') - end + raise('please specify either items_provider or block, but not both') if (items_provider && block) || (items_provider.nil? && block.nil?) self.primary_navigation = ItemContainer.new diff --git a/lib/simple_navigation/item.rb b/lib/simple_navigation/item.rb index 8102c13e..a3a65009 100644 --- a/lib/simple_navigation/item.rb +++ b/lib/simple_navigation/item.rb @@ -3,7 +3,7 @@ module SimpleNavigation # Represents an item in your navigation. # Gets generated by the item method in the config-file. - class Item + class Item # rubocop:disable Metrics/ClassLength attr_reader :key, :name, :sub_navigation, From 82015de5673f0f4096d946fea3c6c4f9a3cf0516 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 01:07:32 +0100 Subject: [PATCH 12/30] Improve RSpec config --- .rspec | 1 - spec/initializers/coveralls.rb | 5 - spec/initializers/have_css_matcher.rb | 21 ---- spec/initializers/memfs.rb | 9 -- spec/initializers/rails.rb | 6 - spec/initializers/rspec.rb | 13 --- spec/spec_helper.rb | 155 +++++++++++++++++--------- 7 files changed, 103 insertions(+), 107 deletions(-) delete mode 100644 spec/initializers/coveralls.rb delete mode 100644 spec/initializers/have_css_matcher.rb delete mode 100644 spec/initializers/memfs.rb delete mode 100644 spec/initializers/rails.rb delete mode 100644 spec/initializers/rspec.rb diff --git a/.rspec b/.rspec index 83e16f80..c99d2e73 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1 @@ ---color --require spec_helper diff --git a/spec/initializers/coveralls.rb b/spec/initializers/coveralls.rb deleted file mode 100644 index 0c54d7b3..00000000 --- a/spec/initializers/coveralls.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -require 'coveralls' - -Coveralls.wear! diff --git a/spec/initializers/have_css_matcher.rb b/spec/initializers/have_css_matcher.rb deleted file mode 100644 index 9fe225d4..00000000 --- a/spec/initializers/have_css_matcher.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -RSpec::Matchers.define :have_css do |expected, times| - match do |actual| - selector = Nokogiri::HTML(actual).css(expected) - - if times - expect(selector.size).to eq times - else - expect(selector.size).to be >= 1 - end - end - - failure_message do |actual| - "expected #{actual} to have #{times || 1} elements matching '#{expected}'" - end - - failure_message_when_negated do |actual| - "expected #{actual} not to have #{times || 1} elements matching '#{expected}'" - end -end diff --git a/spec/initializers/memfs.rb b/spec/initializers/memfs.rb deleted file mode 100644 index 8873fefd..00000000 --- a/spec/initializers/memfs.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require 'memfs' - -RSpec.configure do |config| - config.around(memfs: true) do |example| - MemFs.activate { example.run } - end -end diff --git a/spec/initializers/rails.rb b/spec/initializers/rails.rb deleted file mode 100644 index c3b029c6..00000000 --- a/spec/initializers/rails.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -begin - require 'rails' -rescue LoadError # rubocop:disable Lint/SuppressedException -end diff --git a/spec/initializers/rspec.rb b/spec/initializers/rspec.rb deleted file mode 100644 index 9be249c7..00000000 --- a/spec/initializers/rspec.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -RSpec.configure do |config| - config.expect_with(:rspec) do |c| - c.syntax = :expect - end - - config.order = :random - - # disable monkey patching - # see: https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ - config.disable_monkey_patching! -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 358a3044..52d00a89 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,76 +1,127 @@ # frozen_string_literal: true -require 'initializers/have_css_matcher' -require 'initializers/memfs' -# require 'initializers/coveralls' -require 'initializers/rails' -require 'initializers/rspec' +# Load test gems +require 'memfs' require 'capybara/rspec' -require 'bundler/setup' -Bundler.require +# Define our own spec helper +module SimpleNavigationTest + extend RSpec::Mocks::ExampleMethods -if defined? Rails - require 'fake_app/rails_app' - require 'rspec/rails' + module_function - Capybara.app = RailsApp::Application + def setup_adapter_for(framework, context = double(:context)) + if framework == :rails + # Rails 6.0 and 6.1 provide ActionView::Base.empty method that creates ActionView with an empty LookupContext. + # The method is not available on older versions + view_context = ActionView::Base.respond_to?(:empty) ? ActionView::Base.empty : ActionView::Base.new + allow(context).to receive_messages(view_context: view_context) + end - RSpec.configure do |config| - config.before do - SimpleNavigation.config_files.clear - setup_adapter_for :rails + allow(SimpleNavigation).to receive_messages(framework: framework) + SimpleNavigation.load_adapter + SimpleNavigation.init_adapter_from(context) + end + + def select_an_item(item) + allow(item).to receive_messages(selected?: true) + end + + def setup_container(dom_id, dom_class) + container = SimpleNavigation::ItemContainer.new(1) + container.dom_id = dom_id + container.dom_class = dom_class + container + end + + def setup_navigation(dom_id, dom_class) + setup_adapter_for :rails + container = setup_container(dom_id, dom_class) + setup_items(container) + container + end + + # FIXME: adding the :link option for the list renderer messes up the other + # renderers + def setup_items(container) # rubocop:disable Metrics/AbcSize + container.item :users, 'Users', '/users', html: { id: 'users_id' }, link_html: { id: 'users_link_id' } + container.item :invoices, 'Invoices', '/invoices' do |invoices| + invoices.item :paid, 'Paid', '/invoices/paid' + invoices.item :unpaid, 'Unpaid', '/invoices/unpaid' + end + container.item :accounts, 'Accounts', '/accounts', html: { style: 'float:right' } + container.item :miscellany, 'Miscellany' + + container.items.each do |item| + allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + + item.sub_navigation&.items&.each do |item| + allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + end end end end -def setup_adapter_for(framework, context = double(:context)) - if framework == :rails - # Rails 6.0 and 6.1 provide ActionView::Base.empty method that creates ActionView with an empty LookupContext. - # The method is not available on older versions - view_context = ActionView::Base.respond_to?(:empty) ? ActionView::Base.empty : ActionView::Base.new - allow(context).to receive_messages(view_context: view_context) +# Define our own matcher +RSpec::Matchers.define :have_css do |expected, times| + match do |actual| + selector = Nokogiri::HTML(actual).css(expected) + + if times + expect(selector.size).to eq times + else + expect(selector.size).to be >= 1 + end end - allow(SimpleNavigation).to receive_messages(framework: framework) - SimpleNavigation.load_adapter - SimpleNavigation.init_adapter_from(context) -end + failure_message do |actual| + "expected #{actual} to have #{times || 1} elements matching '#{expected}'" + end -def select_an_item(item) - allow(item).to receive_messages(selected?: true) + failure_message_when_negated do |actual| + "expected #{actual} not to have #{times || 1} elements matching '#{expected}'" + end end -def setup_container(dom_id, dom_class) - container = SimpleNavigation::ItemContainer.new(1) - container.dom_id = dom_id - container.dom_class = dom_class - container -end +# Configure RSpec +RSpec.configure do |config| + config.include SimpleNavigationTest -def setup_navigation(dom_id, dom_class) - setup_adapter_for :rails - container = setup_container(dom_id, dom_class) - setup_items(container) - container -end + config.color = true + config.fail_fast = false + + config.order = :random + Kernel.srand config.seed -# FIXME: adding the :link option for the list renderer messes up the other -# renderers -def setup_items(container) # rubocop:disable Metrics/AbcSize - container.item :users, 'Users', '/users', html: { id: 'users_id' }, link_html: { id: 'users_link_id' } - container.item :invoices, 'Invoices', '/invoices' do |invoices| - invoices.item :paid, 'Paid', '/invoices/paid' - invoices.item :unpaid, 'Unpaid', '/invoices/unpaid' + config.expect_with(:rspec) do |c| + c.syntax = :expect end - container.item :accounts, 'Accounts', '/accounts', html: { style: 'float:right' } - container.item :miscellany, 'Miscellany' - container.items.each do |item| - allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + # disable monkey patching + # see: https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! - item.sub_navigation&.items&.each do |item| - allow(item).to receive_messages(selected?: false, selected_by_condition?: false) + config.raise_errors_for_deprecations! + + config.around(memfs: true) do |example| + MemFs.activate { example.run } + end +end + +# Configure RSpec with Rails +begin + require 'rails' +rescue LoadError # rubocop:disable Lint/SuppressedException +else + require 'fake_app/rails_app' + require 'rspec/rails' + + Capybara.app = RailsApp::Application + + RSpec.configure do |config| + config.before do + SimpleNavigation.config_files.clear + SimpleNavigationTest.setup_adapter_for :rails end end end From a83fc908ddd9d986bcf4de3355e5e89b2baab895 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 01:12:29 +0100 Subject: [PATCH 13/30] Cleanup gemspec --- simple-navigation.gemspec | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index a155bbec..b1219a7e 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -1,8 +1,6 @@ # frozen_string_literal: true -lib = File.expand_path('lib', __dir__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'simple_navigation/version' +require_relative 'lib/simple_navigation/version' Gem::Specification.new do |spec| spec.name = 'simple-navigation' @@ -22,7 +20,6 @@ Gem::Specification.new do |spec| spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") - spec.require_paths = ['lib'] spec.rdoc_options = ['--inline-source', '--charset=UTF-8'] From 043a3c4553444aa1c77d0141e5cabf20734f3efe Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 01:36:49 +0100 Subject: [PATCH 14/30] Fix last Rubocop offenses (Part II) --- lib/simple_navigation/item.rb | 2 +- lib/simple_navigation/item_adapter.rb | 2 +- lib/simple_navigation/item_container.rb | 2 +- lib/simple_navigation/items_provider.rb | 2 +- lib/simple_navigation/renderer/base.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/simple_navigation/item.rb b/lib/simple_navigation/item.rb index a3a65009..580428f3 100644 --- a/lib/simple_navigation/item.rb +++ b/lib/simple_navigation/item.rb @@ -29,7 +29,7 @@ def initialize(container, key, name, url = nil, opts = {}, &sub_nav_block) # the name will be passed to the name_generator specified # in the configuration. # - def name(options = {}) + def name(options = {}) # rubocop:disable Lint/DuplicateMethods options = { apply_generator: true }.merge(options) if options[:apply_generator] config.name_generator.call(@name, self) diff --git a/lib/simple_navigation/item_adapter.rb b/lib/simple_navigation/item_adapter.rb index 39c30df2..9a6272e9 100644 --- a/lib/simple_navigation/item_adapter.rb +++ b/lib/simple_navigation/item_adapter.rb @@ -33,7 +33,7 @@ class ItemAdapter attr_reader :item def initialize(item) - @item = item.is_a?(Hash) ? OpenStruct.new(item) : item + @item = item.is_a?(Hash) ? OpenStruct.new(item) : item # rubocop:disable Style/OpenStructUse end # Returns the options for this item. If the wrapped item does not implement diff --git a/lib/simple_navigation/item_container.rb b/lib/simple_navigation/item_container.rb index 6b22702d..0e9a3b39 100644 --- a/lib/simple_navigation/item_container.rb +++ b/lib/simple_navigation/item_container.rb @@ -2,7 +2,7 @@ module SimpleNavigation # Holds the Items for a navigation 'level'. - class ItemContainer + class ItemContainer # rubocop:disable Metrics/ClassLength attr_accessor :auto_highlight, :dom_class, :dom_id, diff --git a/lib/simple_navigation/items_provider.rb b/lib/simple_navigation/items_provider.rb index 0b3e2d29..3de1eaa3 100644 --- a/lib/simple_navigation/items_provider.rb +++ b/lib/simple_navigation/items_provider.rb @@ -22,7 +22,7 @@ def initialize(provider) end # Returns the navigation items - def items + def items # rubocop:disable Metrics/MethodLength if provider.is_a?(Symbol) SimpleNavigation.context_for_eval.send(provider) elsif provider.respond_to?(:items) diff --git a/lib/simple_navigation/renderer/base.rb b/lib/simple_navigation/renderer/base.rb index bf41c9c9..3dd22e6b 100644 --- a/lib/simple_navigation/renderer/base.rb +++ b/lib/simple_navigation/renderer/base.rb @@ -91,7 +91,7 @@ def options_for(item) end # Extracts the options relevant for the generated link - def link_options_for(item) + def link_options_for(item) # rubocop:disable Metrics/MethodLength special_options = { method: item.method, class: item.selected_class From bc51e292631e35083b9519e1579710d587c57f5a Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 01:38:35 +0100 Subject: [PATCH 15/30] Run Rubocop in CI, drop support of Rails 6.0 and older, drop support of Ruby 2.x --- .github/workflows/ci.yml | 41 +++++++++++-------------- .rubocop.yml | 2 +- Appraisals | 39 ----------------------- gemfiles/rails_3.2.gemfile | 22 ------------- gemfiles/rails_4.1.gemfile | 22 ------------- gemfiles/rails_4.2.gemfile | 22 ------------- gemfiles/rails_5.2.gemfile | 22 ------------- gemfiles/rails_6.0.gemfile | 33 -------------------- lib/simple_navigation/adapters/nanoc.rb | 2 +- simple-navigation.gemspec | 4 ++- 10 files changed, 23 insertions(+), 186 deletions(-) delete mode 100644 gemfiles/rails_3.2.gemfile delete mode 100644 gemfiles/rails_4.1.gemfile delete mode 100644 gemfiles/rails_4.2.gemfile delete mode 100644 gemfiles/rails_5.2.gemfile delete mode 100644 gemfiles/rails_6.0.gemfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38064d78..a7cc11f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,24 @@ on: - cron: '0 4 1 * *' jobs: + rubocop: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + + - name: Bundler + run: bundle install + + - name: Rubocop + run: bin/rubocop + rspec: runs-on: ubuntu-latest @@ -25,8 +43,6 @@ jobs: - '3.2' - '3.1' - '3.0' - - '2.7' - - '2.6' rails: - rails_8.1 - rails_8.0 @@ -34,29 +50,8 @@ jobs: - rails_7.1 - rails_7.0 - rails_6.1 - - rails_6.0 exclude: - - ruby: '2.6' - rails: 'rails_7.0' - - ruby: '2.6' - rails: 'rails_7.1' - - ruby: '2.6' - rails: 'rails_7.2' - - ruby: '2.6' - rails: 'rails_8.0' - - ruby: '2.6' - rails: 'rails_8.1' - - - ruby: '2.7' - rails: 'rails_7.2' - - ruby: '2.7' - rails: 'rails_8.0' - - ruby: '2.7' - rails: 'rails_8.1' - - - ruby: '3.0' - rails: 'rails_6.0' - ruby: '3.0' rails: 'rails_7.2' - ruby: '3.0' diff --git a/.rubocop.yml b/.rubocop.yml index 817c8476..82d32d82 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,7 @@ plugins: AllCops: NewCops: enable SuggestExtensions: true - TargetRubyVersion: 2.6 + TargetRubyVersion: 3.0 Exclude: - bin/* - gemfiles/* diff --git a/Appraisals b/Appraisals index 30560506..4d3a428c 100644 --- a/Appraisals +++ b/Appraisals @@ -1,44 +1,5 @@ # frozen_string_literal: true -appraise 'rails_3.2' do - gem 'railties', '~> 3.2.0' - gem 'rspec-rails' -end - -appraise 'rails_4.1' do - gem 'railties', '~> 4.1.0' - gem 'rspec-rails' -end - -appraise 'rails_4.2' do - gem 'railties', '~> 4.2.0' - gem 'rspec-rails' -end - -appraise 'rails_5.2' do - gem 'railties', '~> 5.2.0' - gem 'rspec-rails' -end - -appraise 'rails_6.0' do - gem 'railties', '~> 6.0.0' - gem 'rspec-rails' - gem 'concurrent-ruby', '1.3.4' - - # Fix: - # warning: drb was loaded from the standard library, but is not part of the default gems starting from Ruby 3.4.0. - # You can add drb to your Gemfile or gemspec to silence this warning. - install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do - gem 'base64' - gem 'drb' - gem 'mutex_m' - end - - install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") }' do - gem 'benchmark' - end -end - appraise 'rails_6.1' do gem 'railties', '~> 6.1.0' gem 'rspec-rails' diff --git a/gemfiles/rails_3.2.gemfile b/gemfiles/rails_3.2.gemfile deleted file mode 100644 index 1bde4767..00000000 --- a/gemfiles/rails_3.2.gemfile +++ /dev/null @@ -1,22 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" -gem "memfs", "~> 0.4.1" -gem "rake" -gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" -gem "rubocop", require: false -gem "rubocop-capybara", require: false -gem "rubocop-performance", require: false -gem "rubocop-rake", require: false -gem "rubocop-rspec", require: false -gem "railties", "~> 3.2.0" -gem "rspec-rails" - -gemspec path: "../" diff --git a/gemfiles/rails_4.1.gemfile b/gemfiles/rails_4.1.gemfile deleted file mode 100644 index 476a25eb..00000000 --- a/gemfiles/rails_4.1.gemfile +++ /dev/null @@ -1,22 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" -gem "memfs", "~> 0.4.1" -gem "rake" -gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" -gem "rubocop", require: false -gem "rubocop-capybara", require: false -gem "rubocop-performance", require: false -gem "rubocop-rake", require: false -gem "rubocop-rspec", require: false -gem "railties", "~> 4.1.0" -gem "rspec-rails" - -gemspec path: "../" diff --git a/gemfiles/rails_4.2.gemfile b/gemfiles/rails_4.2.gemfile deleted file mode 100644 index ff8e32bc..00000000 --- a/gemfiles/rails_4.2.gemfile +++ /dev/null @@ -1,22 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" -gem "memfs", "~> 0.4.1" -gem "rake" -gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" -gem "rubocop", require: false -gem "rubocop-capybara", require: false -gem "rubocop-performance", require: false -gem "rubocop-rake", require: false -gem "rubocop-rspec", require: false -gem "railties", "~> 4.2.0" -gem "rspec-rails" - -gemspec path: "../" diff --git a/gemfiles/rails_5.2.gemfile b/gemfiles/rails_5.2.gemfile deleted file mode 100644 index 1a66547e..00000000 --- a/gemfiles/rails_5.2.gemfile +++ /dev/null @@ -1,22 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" -gem "memfs", "~> 0.4.1" -gem "rake" -gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" -gem "rubocop", require: false -gem "rubocop-capybara", require: false -gem "rubocop-performance", require: false -gem "rubocop-rake", require: false -gem "rubocop-rspec", require: false -gem "railties", "~> 5.2.0" -gem "rspec-rails" - -gemspec path: "../" diff --git a/gemfiles/rails_6.0.gemfile b/gemfiles/rails_6.0.gemfile deleted file mode 100644 index dd9ea02d..00000000 --- a/gemfiles/rails_6.0.gemfile +++ /dev/null @@ -1,33 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" -gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" -gem "memfs", "~> 0.4.1" -gem "rake" -gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" -gem "rubocop", require: false -gem "rubocop-capybara", require: false -gem "rubocop-performance", require: false -gem "rubocop-rake", require: false -gem "rubocop-rspec", require: false -gem "railties", "~> 6.0.0" -gem "rspec-rails" -gem "concurrent-ruby", "1.3.4" - -install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") } do - gem "base64" - gem "drb" - gem "mutex_m" -end - -install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0") } do - gem "benchmark" -end - -gemspec path: "../" diff --git a/lib/simple_navigation/adapters/nanoc.rb b/lib/simple_navigation/adapters/nanoc.rb index 17949b39..ba7811fb 100644 --- a/lib/simple_navigation/adapters/nanoc.rb +++ b/lib/simple_navigation/adapters/nanoc.rb @@ -41,7 +41,7 @@ def content_tag(type, content, options = {}) private def to_attributes(options) - options.map { |k, v| v.nil? ? nil : "#{k}='#{v}'" }.compact.join(' ') + options.filter_map { |k, v| v.nil? ? nil : "#{k}='#{v}'" }.join(' ') end end end diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index b1219a7e..0b570e33 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -23,6 +23,8 @@ Gem::Specification.new do |spec| spec.rdoc_options = ['--inline-source', '--charset=UTF-8'] - spec.add_dependency 'activesupport', '>= 2.3.2' + spec.required_ruby_version = '>= 3.0.0' + + spec.add_dependency 'activesupport', '>= 6.1.0' spec.add_dependency 'ostruct' end From 3256597d2e27f85696a25122f19ea5ec09b3a184 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:08:06 +0100 Subject: [PATCH 16/30] Use zeitwerk to load gem --- lib/simple-navigation.rb | 2 +- lib/simple_navigation.rb | 31 ++++++++++++--------- lib/simple_navigation/adapters.rb | 12 -------- lib/simple_navigation/adapters/sinatra.rb | 2 -- lib/simple_navigation/config_file.rb | 2 -- lib/simple_navigation/config_file_finder.rb | 2 -- lib/simple_navigation/configuration.rb | 2 -- lib/simple_navigation/item_adapter.rb | 3 -- lib/simple_navigation/renderer.rb | 14 ---------- lib/simple_navigation/renderer/base.rb | 2 -- lib/simple_navigation/renderer/json.rb | 2 -- simple-navigation.gemspec | 1 + 12 files changed, 20 insertions(+), 55 deletions(-) delete mode 100644 lib/simple_navigation/adapters.rb delete mode 100644 lib/simple_navigation/renderer.rb diff --git a/lib/simple-navigation.rb b/lib/simple-navigation.rb index ce4c0a02..4a0f8b31 100644 --- a/lib/simple-navigation.rb +++ b/lib/simple-navigation.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require 'simple_navigation' +require_relative 'simple_navigation' diff --git a/lib/simple_navigation.rb b/lib/simple_navigation.rb index 70c9ff4e..299dd583 100644 --- a/lib/simple_navigation.rb +++ b/lib/simple_navigation.rb @@ -1,26 +1,31 @@ # frozen_string_literal: true -# cherry picking active_support stuff +# require ruby dependencies +require 'cgi' +require 'forwardable' +require 'json' +require 'ostruct' +require 'singleton' + +# require external dependencies require 'active_support/core_ext/array' require 'active_support/core_ext/hash' +require 'active_support/core_ext/string' require 'active_support/core_ext/module/attribute_accessors' +require 'zeitwerk' -require 'simple_navigation/version' -require 'simple_navigation/configuration' -require 'simple_navigation/item_adapter' -require 'simple_navigation/item' -require 'simple_navigation/item_container' -require 'simple_navigation/items_provider' -require 'simple_navigation/renderer' -require 'simple_navigation/adapters' -require 'simple_navigation/config_file_finder' -require 'simple_navigation/railtie' if defined?(Rails::Railtie) - -require 'forwardable' +# load zeitwerk +Zeitwerk::Loader.for_gem.tap do |loader| + loader.ignore("#{__dir__}/generators") + loader.ignore("#{__dir__}/simple-navigation.rb") + loader.setup +end # A plugin for generating a simple navigation. See README for resources on # usage instructions. module SimpleNavigation + require_relative 'simple_navigation/railtie' if defined?(Rails::Railtie) + mattr_accessor :adapter, :adapter_class, :config_files, diff --git a/lib/simple_navigation/adapters.rb b/lib/simple_navigation/adapters.rb deleted file mode 100644 index 21049216..00000000 --- a/lib/simple_navigation/adapters.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require 'simple_navigation/adapters/base' - -module SimpleNavigation - module Adapters - autoload :Rails, 'simple_navigation/adapters/rails' - autoload :Padrino, 'simple_navigation/adapters/padrino' - autoload :Sinatra, 'simple_navigation/adapters/sinatra' - autoload :Nanoc, 'simple_navigation/adapters/nanoc' - end -end diff --git a/lib/simple_navigation/adapters/sinatra.rb b/lib/simple_navigation/adapters/sinatra.rb index 6a33d56a..1d65ccd7 100644 --- a/lib/simple_navigation/adapters/sinatra.rb +++ b/lib/simple_navigation/adapters/sinatra.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'cgi' - module SimpleNavigation module Adapters class Sinatra < Base diff --git a/lib/simple_navigation/config_file.rb b/lib/simple_navigation/config_file.rb index d071d20e..92de1bd0 100644 --- a/lib/simple_navigation/config_file.rb +++ b/lib/simple_navigation/config_file.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'active_support/core_ext/string' - module SimpleNavigation # Internal: Encapsulates the config file naming knowledge. class ConfigFile diff --git a/lib/simple_navigation/config_file_finder.rb b/lib/simple_navigation/config_file_finder.rb index 9be010f0..1004f92e 100644 --- a/lib/simple_navigation/config_file_finder.rb +++ b/lib/simple_navigation/config_file_finder.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'simple_navigation/config_file' - module SimpleNavigation # Internal: Encapsulates the configuration file finding logic. class ConfigFileFinder diff --git a/lib/simple_navigation/configuration.rb b/lib/simple_navigation/configuration.rb index d54c717b..b0478bca 100644 --- a/lib/simple_navigation/configuration.rb +++ b/lib/simple_navigation/configuration.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'singleton' - module SimpleNavigation # Responsible for evaluating and handling the config/navigation.rb file. class Configuration diff --git a/lib/simple_navigation/item_adapter.rb b/lib/simple_navigation/item_adapter.rb index 9a6272e9..52d5c21b 100644 --- a/lib/simple_navigation/item_adapter.rb +++ b/lib/simple_navigation/item_adapter.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require 'forwardable' -require 'ostruct' - module SimpleNavigation # This class acts as an adapter to items that are not defined using the DSL # in the config/navigation.rb, but directly provided inside the application. diff --git a/lib/simple_navigation/renderer.rb b/lib/simple_navigation/renderer.rb deleted file mode 100644 index 734ba369..00000000 --- a/lib/simple_navigation/renderer.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'simple_navigation/helpers' -require 'simple_navigation/renderer/base' - -module SimpleNavigation - module Renderer - autoload :List, 'simple_navigation/renderer/list' - autoload :Links, 'simple_navigation/renderer/links' - autoload :Breadcrumbs, 'simple_navigation/renderer/breadcrumbs' - autoload :Text, 'simple_navigation/renderer/text' - autoload :Json, 'simple_navigation/renderer/json' - end -end diff --git a/lib/simple_navigation/renderer/base.rb b/lib/simple_navigation/renderer/base.rb index 3dd22e6b..5d3ce7d3 100644 --- a/lib/simple_navigation/renderer/base.rb +++ b/lib/simple_navigation/renderer/base.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'forwardable' - module SimpleNavigation module Renderer # This is the base class for all renderers. diff --git a/lib/simple_navigation/renderer/json.rb b/lib/simple_navigation/renderer/json.rb index 3344a73c..d546bfd7 100644 --- a/lib/simple_navigation/renderer/json.rb +++ b/lib/simple_navigation/renderer/json.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'json' - module SimpleNavigation module Renderer # Renders the navigation items as a object tree serialized as a json string, diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index 0b570e33..ad615a8b 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -27,4 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '>= 6.1.0' spec.add_dependency 'ostruct' + spec.add_dependency 'zeitwerk' end From 124d4c0c93b9d60b9ffa6fb466ec22affd420600 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:11:52 +0100 Subject: [PATCH 17/30] We depend on json lib which is now a gem, so declare it explicitly --- simple-navigation.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index ad615a8b..b3ead3fb 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -26,6 +26,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 3.0.0' spec.add_dependency 'activesupport', '>= 6.1.0' + spec.add_dependency 'json' spec.add_dependency 'ostruct' spec.add_dependency 'zeitwerk' end From f73c11009efa89cb33fffb9a9f3e939fdc022182 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:14:50 +0100 Subject: [PATCH 18/30] Coding style --- lib/simple_navigation.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/simple_navigation.rb b/lib/simple_navigation.rb index 299dd583..803de47e 100644 --- a/lib/simple_navigation.rb +++ b/lib/simple_navigation.rb @@ -78,8 +78,7 @@ def framework return :sinatra if defined?(Sinatra) return :nanoc if defined?(Nanoc3) - raise 'simple_navigation currently only works for Rails, Sinatra and ' \ - 'Padrino apps' + raise 'simple_navigation currently only works for Rails, Sinatra and Padrino apps' end # Loads the adapter for the current framework From 4e7c96a2dc4d8b5824ae8cb1ae1c1c21f5ca9cec Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:25:56 +0100 Subject: [PATCH 19/30] Relax dev dependencies --- Gemfile | 7 +++---- gemfiles/rails_6.1.gemfile | 7 +++---- gemfiles/rails_7.0.gemfile | 7 +++---- gemfiles/rails_7.1.gemfile | 7 +++---- gemfiles/rails_7.2.gemfile | 7 +++---- gemfiles/rails_8.0.gemfile | 7 +++---- gemfiles/rails_8.1.gemfile | 7 +++---- 7 files changed, 21 insertions(+), 28 deletions(-) diff --git a/Gemfile b/Gemfile index 6da62684..5704c796 100644 --- a/Gemfile +++ b/Gemfile @@ -7,15 +7,14 @@ gemspec # Dev libs gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git' gem 'capybara' -gem 'coveralls', '~> 0.7' -gem 'guard-rspec', '~> 4.2' gem 'memfs', '~> 0.4.1' gem 'rake' gem 'rdoc' -gem 'rspec', '~> 3.0' -gem 'tzinfo', '>= 0' +gem 'rspec' +gem 'tzinfo' # Dev tools / linter +gem 'guard-rspec', require: false gem 'rubocop', require: false gem 'rubocop-capybara', require: false gem 'rubocop-performance', require: false diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index eb5e6493..d3451ed1 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index 0d824ee4..69b3a9ec 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 9c4d3e5a..84fab37b 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 21e9507a..7c8141d0 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index 15a94d7b..b3c064b5 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index ccf88a71..63a6866a 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -4,13 +4,12 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "coveralls", "~> 0.7" -gem "guard-rspec", "~> 4.2" gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" -gem "rspec", "~> 3.0" -gem "tzinfo", ">= 0" +gem "rspec" +gem "tzinfo" +gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false gem "rubocop-performance", require: false From 6584ae281144b5bcc81a25af89153f3fcdb7331f Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:28:56 +0100 Subject: [PATCH 20/30] Add simplecov gem --- Appraisals | 1 + Gemfile | 1 + gemfiles/rails_6.1.gemfile | 2 ++ gemfiles/rails_7.0.gemfile | 1 + gemfiles/rails_7.1.gemfile | 1 + gemfiles/rails_7.2.gemfile | 1 + gemfiles/rails_8.0.gemfile | 1 + gemfiles/rails_8.1.gemfile | 1 + spec/spec_helper.rb | 9 +++++++++ 9 files changed, 18 insertions(+) diff --git a/Appraisals b/Appraisals index 4d3a428c..f46d30c6 100644 --- a/Appraisals +++ b/Appraisals @@ -10,6 +10,7 @@ appraise 'rails_6.1' do # You can add drb to your Gemfile or gemspec to silence this warning. install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do gem 'base64' + gem 'bigdecimal' gem 'drb' gem 'mutex_m' end diff --git a/Gemfile b/Gemfile index 5704c796..3e7ba8fb 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem 'memfs', '~> 0.4.1' gem 'rake' gem 'rdoc' gem 'rspec' +gem 'simplecov' gem 'tzinfo' # Dev tools / linter diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index d3451ed1..cb0509ca 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false @@ -21,6 +22,7 @@ gem "concurrent-ruby", "1.3.4" install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") } do gem "base64" + gem "bigdecimal" gem "drb" gem "mutex_m" end diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index 69b3a9ec..ec5b8b57 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 84fab37b..44bc85ea 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 7c8141d0..42880db5 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index b3c064b5..92e324c9 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index 63a6866a..27233d34 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -8,6 +8,7 @@ gem "memfs", "~> 0.4.1" gem "rake" gem "rdoc" gem "rspec" +gem "simplecov" gem "tzinfo" gem "guard-rspec", require: false gem "rubocop", require: false diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 52d00a89..cf85eff7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,14 @@ # frozen_string_literal: true +require 'simplecov' +require 'simplecov_json_formatter' + +# Start SimpleCov +SimpleCov.start do + formatter SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::JSONFormatter]) + add_filter 'spec/' +end + # Load test gems require 'memfs' require 'capybara/rspec' From 71a5d76e0e17346bb79601368fd1cb32aae4344e Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:41:56 +0100 Subject: [PATCH 21/30] Add warning gem to manage Ruby warnings --- .rspec | 1 + Gemfile | 1 + gemfiles/rails_6.1.gemfile | 1 + gemfiles/rails_7.0.gemfile | 1 + gemfiles/rails_7.1.gemfile | 1 + gemfiles/rails_7.2.gemfile | 1 + gemfiles/rails_8.0.gemfile | 1 + gemfiles/rails_8.1.gemfile | 1 + spec/spec_helper.rb | 18 ++++++++++++++++++ 9 files changed, 26 insertions(+) diff --git a/.rspec b/.rspec index c99d2e73..ce0f67a2 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,2 @@ --require spec_helper +--warnings diff --git a/Gemfile b/Gemfile index 3e7ba8fb..60a42f15 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,7 @@ gem 'rdoc' gem 'rspec' gem 'simplecov' gem 'tzinfo' +gem 'warning' # Dev tools / linter gem 'guard-rspec', require: false diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index cb0509ca..b19628dd 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index ec5b8b57..fbbfb924 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 44bc85ea..7a57b74f 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 42880db5..739cc493 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index 92e324c9..2932d871 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index 27233d34..9e2da595 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -10,6 +10,7 @@ gem "rdoc" gem "rspec" gem "simplecov" gem "tzinfo" +gem "warning" gem "guard-rspec", require: false gem "rubocop", require: false gem "rubocop-capybara", require: false diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cf85eff7..2ef6b9d9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,23 @@ # frozen_string_literal: true +# Load ruby-warning gem +require 'warning' + +Warning[:deprecated] = true +Warning[:experimental] = true +Warning[:performance] = true if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3.0') + +# Ignore all warnings in Gem dependencies +Gem.path.each do |path| + Warning.ignore(//, path) +end + +# Ignore method redefinitions +Warning.ignore(/warning: previous definition of/) +Warning.ignore(/warning: method redefined;/) +Warning.ignore(/warning: OpenStruct use is discouraged for performance reasons/) + +# Load simplecov require 'simplecov' require 'simplecov_json_formatter' From f58539d3a112f4ce0c8b113e9e4029df4e1b01ac Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:43:52 +0100 Subject: [PATCH 22/30] Use cgi/escape (cgi is removed in Ruby 4.0) --- lib/simple_navigation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/simple_navigation.rb b/lib/simple_navigation.rb index 803de47e..5cf2efc3 100644 --- a/lib/simple_navigation.rb +++ b/lib/simple_navigation.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # require ruby dependencies -require 'cgi' +require 'cgi/escape' require 'forwardable' require 'json' require 'ostruct' From b71f5d678b7d177533626c30d0bb859afdf49992 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:47:10 +0100 Subject: [PATCH 23/30] Remove useless condition check --- lib/simple_navigation/adapters/rails.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/simple_navigation/adapters/rails.rb b/lib/simple_navigation/adapters/rails.rb index a4e44f4f..b809a247 100644 --- a/lib/simple_navigation/adapters/rails.rb +++ b/lib/simple_navigation/adapters/rails.rb @@ -12,12 +12,8 @@ def self.register # This delays the hook initialization using the on_load # hooks, but does not change behaviour for existing # rails versions. - if ::Rails::VERSION::MAJOR >= 6 - ActiveSupport.on_load(:action_controller_base) do - SimpleNavigation::Adapters::Rails.register_controller_helpers - end - else - register_controller_helpers + ActiveSupport.on_load(:action_controller_base) do + SimpleNavigation::Adapters::Rails.register_controller_helpers end end From 044d90cdbf89937595e79ed276e7ab07e2fe53b1 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 02:59:21 +0100 Subject: [PATCH 24/30] Get rid of OStruct --- lib/simple_navigation.rb | 1 - lib/simple_navigation/item_adapter.rb | 18 +++++++++++++++--- simple-navigation.gemspec | 1 - spec/simple_navigation/item_adapter_spec.rb | 15 +-------------- spec/spec_helper.rb | 1 - 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/lib/simple_navigation.rb b/lib/simple_navigation.rb index 5cf2efc3..eaf57fe9 100644 --- a/lib/simple_navigation.rb +++ b/lib/simple_navigation.rb @@ -4,7 +4,6 @@ require 'cgi/escape' require 'forwardable' require 'json' -require 'ostruct' require 'singleton' # require external dependencies diff --git a/lib/simple_navigation/item_adapter.rb b/lib/simple_navigation/item_adapter.rb index 52d5c21b..7a0c6000 100644 --- a/lib/simple_navigation/item_adapter.rb +++ b/lib/simple_navigation/item_adapter.rb @@ -29,20 +29,32 @@ class ItemAdapter attr_reader :item + class Item + attr_reader :key, :name, :url, :options, :items + + def initialize(item) + @key = item[:key] + @name = item[:name] + @url = item[:url] + @options = item[:options] || {} + @items = item[:items] || [] + end + end + def initialize(item) - @item = item.is_a?(Hash) ? OpenStruct.new(item) : item # rubocop:disable Style/OpenStructUse + @item = item.is_a?(Hash) ? Item.new(item) : item end # Returns the options for this item. If the wrapped item does not implement # an options method, an empty hash is returned. def options - item.respond_to?(:options) ? item.options : {} + item.options end # Returns the items (subnavigation) for this item if it responds to :items # and the items-collection is not empty. Returns nil otherwise. def items - item.items if item.respond_to?(:items) && item.items&.any? + item.items if item.items&.any? end # Converts this Item into a SimpleNavigation::Item diff --git a/simple-navigation.gemspec b/simple-navigation.gemspec index b3ead3fb..938fcb2a 100644 --- a/simple-navigation.gemspec +++ b/simple-navigation.gemspec @@ -27,6 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '>= 6.1.0' spec.add_dependency 'json' - spec.add_dependency 'ostruct' spec.add_dependency 'zeitwerk' end diff --git a/spec/simple_navigation/item_adapter_spec.rb b/spec/simple_navigation/item_adapter_spec.rb index a32fd537..9ecbf55f 100644 --- a/spec/simple_navigation/item_adapter_spec.rb +++ b/spec/simple_navigation/item_adapter_spec.rb @@ -33,12 +33,6 @@ expect(item_adapter.options).to be options end end - - context 'item does not respond to options' do - it 'returns an empty hash' do - expect(item_adapter.options).to eq({}) - end - end end describe '#items' do @@ -71,12 +65,6 @@ end end end - - context "when item doesn't respond to items" do - it 'returns nil' do - expect(item_adapter.items).to be_nil - end - end end describe '#to_simple_navigation_item' do @@ -172,8 +160,7 @@ before { item.merge(options: {}) } it 'passes the right arguments to Item' do - expect(SimpleNavigation::Item).to receive(:new) - .with(container, 'key', 'name', 'url', {}) + expect(SimpleNavigation::Item).to receive(:new).with(container, 'key', 'name', 'url', {}) item_adapter.to_simple_navigation_item(container) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2ef6b9d9..d90c9fae 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,7 +15,6 @@ # Ignore method redefinitions Warning.ignore(/warning: previous definition of/) Warning.ignore(/warning: method redefined;/) -Warning.ignore(/warning: OpenStruct use is discouraged for performance reasons/) # Load simplecov require 'simplecov' From 4e0206a74736cad7c8533680e71b421c73163585 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 03:10:42 +0100 Subject: [PATCH 25/30] Minor change --- spec/spec_helper.rb | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d90c9fae..c41e5996 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -88,27 +88,6 @@ def setup_items(container) # rubocop:disable Metrics/AbcSize end end -# Define our own matcher -RSpec::Matchers.define :have_css do |expected, times| - match do |actual| - selector = Nokogiri::HTML(actual).css(expected) - - if times - expect(selector.size).to eq times - else - expect(selector.size).to be >= 1 - end - end - - failure_message do |actual| - "expected #{actual} to have #{times || 1} elements matching '#{expected}'" - end - - failure_message_when_negated do |actual| - "expected #{actual} not to have #{times || 1} elements matching '#{expected}'" - end -end - # Configure RSpec RSpec.configure do |config| config.include SimpleNavigationTest @@ -134,6 +113,27 @@ def setup_items(container) # rubocop:disable Metrics/AbcSize end end +# Define our own matcher +RSpec::Matchers.define :have_css do |expected, times| + match do |actual| + selector = Nokogiri::HTML(actual).css(expected) + + if times + expect(selector.size).to eq times + else + expect(selector.size).to be >= 1 + end + end + + failure_message do |actual| + "expected #{actual} to have #{times || 1} elements matching '#{expected}'" + end + + failure_message_when_negated do |actual| + "expected #{actual} not to have #{times || 1} elements matching '#{expected}'" + end +end + # Configure RSpec with Rails begin require 'rails' From 94bcfb5cfc3c652553cddae670e97ca38f775d2e Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 03:10:57 +0100 Subject: [PATCH 26/30] Cleanup Rakefile --- Rakefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Rakefile b/Rakefile index 8092bd3a..1a7ff9b6 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,7 @@ # frozen_string_literal: true -require 'bundler' -Bundler::GemHelper.install_tasks - -require 'rspec/core' +require 'bundler/gem_tasks' require 'rspec/core/rake_task' - require 'rdoc/task' RSpec::Core::RakeTask.new(:spec) From f5407ec019751c01f1089738c1925a6756a608d8 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 03:15:10 +0100 Subject: [PATCH 27/30] Add .rubocop_todo.yml --- .rubocop.yml | 47 ++++++++++++++--------------------------------- .rubocop_todo.yml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 .rubocop_todo.yml diff --git a/.rubocop.yml b/.rubocop.yml index 82d32d82..6cd43a2c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,9 @@ plugins: - rubocop-rake - rubocop-rspec +inherit_from: + - .rubocop_todo.yml + AllCops: NewCops: enable SuggestExtensions: true @@ -19,6 +22,14 @@ Gemspec/RequireMFA: Style/Documentation: Enabled: false +Naming/FileName: + Exclude: + - lib/simple-navigation.rb + +########### +# METRICS # +########### + Metrics/ClassLength: Exclude: - spec/**/* @@ -27,42 +38,12 @@ Metrics/MethodLength: Exclude: - spec/**/* -RSpec/SubjectStub: - Enabled: false - -RSpec/NamedSubject: - Enabled: false +######### +# RSPEC # +######### RSpec/NestedGroups: Max: 6 RSpec/MultipleMemoizedHelpers: Max: 8 - -RSpec/VerifiedDoubles: - Enabled: false - -RSpec/ContextWording: - Enabled: false - -RSpec/MessageSpies: - Enabled: false - -RSpec/IndexedLet: - Enabled: false - -Layout/LineLength: - Enabled: false - -Naming/VariableNumber: - Enabled: false - -Naming/FileName: - Exclude: - - lib/simple-navigation.rb - -Style/BlockDelimiters: - Enabled: false - -Capybara/NegationMatcher: - Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..252b05b5 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,39 @@ +--- + +Layout/LineLength: + Enabled: false + +Naming/VariableNumber: + Enabled: false + +Style/BlockDelimiters: + Enabled: false + +######### +# RSPEC # +######### + +RSpec/SubjectStub: + Enabled: false + +RSpec/NamedSubject: + Enabled: false + +RSpec/VerifiedDoubles: + Enabled: false + +RSpec/ContextWording: + Enabled: false + +RSpec/MessageSpies: + Enabled: false + +RSpec/IndexedLet: + Enabled: false + +############ +# CAPYBARA # +############ + +Capybara/NegationMatcher: + Enabled: false From 822296b3e09bb5bb2ab4c83b3fec57be4124ddf7 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 03:17:40 +0100 Subject: [PATCH 28/30] Add comments --- Appraisals | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Appraisals b/Appraisals index f46d30c6..dcdd1cb2 100644 --- a/Appraisals +++ b/Appraisals @@ -1,5 +1,14 @@ # frozen_string_literal: true +# To update files in gemfiles/ directory: +# rm gemfiles/* +# bin/appraisal generate + +# To run tests: +# bin/appraisal rails_8.1 bundle install +# bin/appraisal rails_8.1 rake +# bin/appraisal rails_8.1 rspec + appraise 'rails_6.1' do gem 'railties', '~> 6.1.0' gem 'rspec-rails' From 39128aacf322468a57be83af4e4c6845ee230211 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 21:46:31 +0100 Subject: [PATCH 29/30] Use upstream version of memfs --- .github/workflows/ci.yml | 5 ++--- Gemfile | 2 +- gemfiles/rails_6.1.gemfile | 2 +- gemfiles/rails_7.0.gemfile | 2 +- gemfiles/rails_7.1.gemfile | 2 +- gemfiles/rails_7.2.gemfile | 2 +- gemfiles/rails_8.0.gemfile | 2 +- gemfiles/rails_8.1.gemfile | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7cc11f7..7f69394c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,9 +67,8 @@ jobs: env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.rails }}.gemfile - - # reenable me later - # RUBYOPT: "--enable=frozen-string-literal --debug=frozen-string-literal" + # Check for frozen strings + RUBYOPT: "--enable=frozen-string-literal --debug=frozen-string-literal" steps: - name: Checkout diff --git a/Gemfile b/Gemfile index 60a42f15..ad06fe67 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gemspec # Dev libs gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git' gem 'capybara' -gem 'memfs', '~> 0.4.1' +gem 'memfs', git: 'https://github.com/simonc/memfs.git' gem 'rake' gem 'rdoc' gem 'rspec' diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile index b19628dd..6a8fd9aa 100644 --- a/gemfiles/rails_6.1.gemfile +++ b/gemfiles/rails_6.1.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile index fbbfb924..6ce788a9 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.0.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 7a57b74f..fefba441 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 739cc493..bc5f4ab6 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index 2932d871..7869e509 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index 9e2da595..f9b31152 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git" gem "capybara" -gem "memfs", "~> 0.4.1" +gem "memfs", git: "https://github.com/simonc/memfs.git" gem "rake" gem "rdoc" gem "rspec" From f26d900c6e2046bc48892fd98bd00903d0b1d1d0 Mon Sep 17 00:00:00 2001 From: Nicolas Rodriguez Date: Fri, 30 Jan 2026 22:20:23 +0100 Subject: [PATCH 30/30] Update README --- README.md | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a12b96b4..422c587b 100644 --- a/README.md +++ b/README.md @@ -31,30 +31,19 @@ Fork, fix, then send a Pull Request. To run the test suite locally against all supported frameworks: - % bundle install - % rake spec:all +```sh +bundle install +bin/appraisal bundle install +bin/appraisal rspec +bin/appraisal rspec ./spec/requests/users_spec.rb +``` To target the test suite against one framework: - % rake spec:rails-4-2-stable - -You can find a list of supported spec tasks by running rake -T. You may also find it useful to run a specific test for a specific framework. To do so, you'll have to first make sure you have bundled everything for that configuration, then you can run the specific test: - -% BUNDLE_GEMFILE='gemfiles/rails-4-2-stable.gemfile' bundle install -j 4 -% BUNDLE_GEMFILE='gemfiles/rails-4-2-stable.gemfile' bundle exec rspec ./spec/requests/users_spec.rb - -### Rake and Bundler - -If you use a shell plugin (like oh-my-zsh:bundler) that auto-prefixes commands with `bundle exec` using the `rake` command will fail. - -Get the original command with `type -a rake`: - - % type -a rake - rake is an alias for bundled_rake - rake is /Users/username/.rubies/ruby-2.2.3/bin/rake - rake is /usr/bin/rake - -In this situation `/Users/username/.rubies/ruby-2.2.3/bin/rake` is the command you should use. +```sh +bin/appraisal rails_8.1 rspec +bin/appraisal rails_8.1 rspec ./spec/requests/users_spec.rb +``` ## License