diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a33009f..a42e059 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest name: Lint steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -21,19 +21,25 @@ jobs: name: Ruby ${{ matrix.ruby }} - Rails ${{ matrix.rails }} strategy: matrix: - ruby: [3.2, 3.3, 3.4] - rails: [rails_7.0, rails_7.1, rails_8.0] + ruby: [3.1, 3.2, 3.3, 3.4, 4.0] + rails: [rails_7.2, rails_8.0, rails_8.1] + exclude: + - ruby: 3.1 + rails: rails_8.0 + - ruby: 3.1 + rails: rails_8.1 env: RUBY_VERSION: ${{ matrix.ruby }} RAILS_VERSION: ${{ matrix.rails }} + BUNDLE_GEMFILE: gemfiles/${{ matrix.rails }}.gemfile steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Install dependencies - run: bundle exec appraisal install + run: bundle install - name: Run specs - run: bundle exec appraisal ${{ matrix.rails }} rspec + run: bundle exec rspec diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3a2e34d..5807571 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,36 +1,73 @@ name: Publish Gem on: - release: - types: [published] + push: + tags: + - "v*" + +permissions: + contents: read jobs: - build: + publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 with: - ruby-version: '3.4' + ruby-version: "3.4" bundler-cache: true - - name: Release Gem - if: contains(github.ref, 'refs/tags/v') + + - name: Build gem + run: | + set -euo pipefail + gemspec="$(ls *.gemspec | head -n 1)" + gem_name="${gemspec%.gemspec}" + version="${GITHUB_REF_NAME#v}" + gem_file="pkg/${gem_name}-${version}.gem" + + echo "Building ${gem_name} ${version}..." + mkdir -p pkg + bundle exec gem build "$gemspec" --output "$gem_file" + test -f "$gem_file" + + - name: Publish to RubyGems env: - RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}} - TAG: ${{ github.event.release.tag_name }} + RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} run: | - echo "Setting up gem credentials..." - mkdir -p ~/.gem + set -euo pipefail + + if [[ -z "${RUBYGEMS_API_KEY:-}" ]]; then + echo "::error::Missing RUBYGEMS_API_KEY secret." + exit 1 + fi - cat << EOF > ~/.gem/credentials - --- - :rubygems_api_key: ${RUBYGEMS_API_KEY} - EOF + gem_file="$(ls pkg/*.gem | head -n 1)" + echo "Publishing ${gem_file}..." + mkdir -p ~/.gem + printf -- "---\n:rubygems_api_key: %s\n" "$RUBYGEMS_API_KEY" > ~/.gem/credentials chmod 0600 ~/.gem/credentials - bundle exec rake build + gem push "$gem_file" + - name: Create GitHub release + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + prerelease_flag=() + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + prerelease_flag=(--prerelease) + fi - echo "Running gem release task..." - gem push pkg/typical_situation-${TAG#v}.gem + echo "Creating GitHub release ${GITHUB_REF_NAME}..." + gh release create "$GITHUB_REF_NAME" pkg/*.gem \ + --verify-tag \ + --title "$GITHUB_REF_NAME" \ + --notes "Published to RubyGems." \ + "${prerelease_flag[@]}" diff --git a/Appraisals b/Appraisals index c2be193..649b3fc 100644 --- a/Appraisals +++ b/Appraisals @@ -1,23 +1,23 @@ # frozen_string_literal: true -appraise "rails_7.0" do - gem "rails", "~> 7.0" - gem "rspec", "~> 3.12" - gem "rspec-rails", "~> 6.0" +appraise "rails_7.2" do + gem "rails", "~> 7.2" + gem "rspec", "~> 3.13" + gem "rspec-rails", "~> 7.0" gem "rails-controller-testing" - gem "sqlite3", "~> 1.4" + gem "sqlite3", "~> 2.1" end -appraise "rails_7.1" do - gem "rails", "~> 7.1" - gem "rspec", "~> 3.12" - gem "rspec-rails", "~> 6.1" +appraise "rails_8.0" do + gem "rails", "~> 8.0" + gem "rspec", "~> 3.13" + gem "rspec-rails", "~> 8.0" gem "rails-controller-testing" - gem "sqlite3", "~> 1.4" + gem "sqlite3", "~> 2.1" end -appraise "rails_8.0" do - gem "rails", "~> 8.0" +appraise "rails_8.1" do + gem "rails", "~> 8.1" gem "rspec", "~> 3.13" gem "rspec-rails", "~> 8.0" gem "rails-controller-testing" diff --git a/README.md b/README.md index c44e09f..9891750 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,17 @@ A Ruby mixin (module) providing the seven standard resource actions & responses Tested in: -- Rails 7.0 -- Rails 7.1 +- Rails 7.2 - Rails 8.0 +- Rails 8.1 Against Ruby versions: +- 3.1 - 3.2 - 3.3 - 3.4 +- 4.0 Add to your **Gemfile**: @@ -109,7 +111,17 @@ The library is split into modules: #### Common Customization Hooks -**Scoped Collections** - Filter the collection based on user permissions or other criteria: +Most customization hooks are intended to be overridden as `private` or `protected` controller methods so they do not become public controller actions. + +For index actions, resources move through this pipeline: + +1. `collection` - base relation from the host controller +2. `scoped_resource` - visibility/tenant/security scoping +3. `prepare_resources` - search or filter transforms +4. `apply_sorting` - default sorting from `default_sorting_attribute` +5. `paginate_resources` - pagination adapter hook + +**Scoped Collections** - Restrict the base collection based on user permissions, tenancy, or other security boundaries: ```ruby def scoped_resource @@ -121,6 +133,16 @@ def scoped_resource end ``` +**Search and Filtering** - Apply request-driven filters after scoping and before sorting/pagination: + +```ruby +def prepare_resources(resources) + resources = resources.where(status: params[:status]) if params[:status].present? + resources = resources.where("title ILIKE ?", "%#{params[:q]}%") if params[:q].present? + resources +end +``` + **Custom Lookup** - Use different attributes for finding resources: ```ruby @@ -439,6 +461,21 @@ Start an interactive console to experiment with the gem: bundle exec irb -r typical_situation ``` +## Releases + +Releases are driven by git tags. The version lives in `lib/typical_situation/version.rb`, and the gemspec reads `TypicalSituation::VERSION`. + +Release locally from the branch you want to publish: + +```bash +bundle install +bin/release patch # or: minor, major +``` + +`bin/release` uses `bump`, commits the version file, creates a `vX.Y.Z` tag, pushes the branch, and pushes the tag. + +GitHub Actions publishes only when a `v*` tag is pushed. The publish workflow builds the gem and pushes it to RubyGems with `RUBYGEMS_API_KEY`. + ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/apsislabs/typical_situation. diff --git a/bin/release b/bin/release new file mode 100755 index 0000000..75c3a51 --- /dev/null +++ b/bin/release @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + echo "Usage: bin/release patch|minor|major|pre" + echo " bin/release rc X.Y.Z" +} + +level="${1:-}" +target_version="${2:-}" +case "$level" in + patch|minor|major|pre) + if [[ $# -ne 1 ]]; then + usage + exit 1 + fi + ;; + rc) + if [[ $# -ne 2 || ! "$target_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + usage + exit 1 + fi + ;; + *) + usage + exit 1 + ;; +esac + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Error: bin/release must be run inside a git repository." + exit 1 +fi + +branch="$(git symbolic-ref --quiet --short HEAD)" || { + echo "Error: releases must be run from a branch, not a detached HEAD." + exit 1 +} + +version_file="$(bundle exec bump file | awk '{print $NF}')" +current_version="$(bundle exec bump current --value-only)" +if [[ "$level" == "rc" ]]; then + next_version="${target_version}-rc" +else + next_version="$(bundle exec bump show-next "$level" --value-only)" +fi +tag="v${next_version}" + +if [[ -z "$version_file" || -z "$current_version" || -z "$next_version" ]]; then + echo "Error: could not determine gem version information." + exit 1 +fi + +if ! git diff --quiet -- "$version_file" || ! git diff --cached --quiet -- "$version_file"; then + echo "Error: $version_file has uncommitted changes." + exit 1 +fi + +if git rev-parse "$tag" >/dev/null 2>&1; then + echo "Error: tag $tag already exists." + exit 1 +fi + +echo "Releasing ${tag} (${level})" +echo "Version: ${current_version} -> ${next_version}" +echo "Branch: ${branch}" + +if [[ "$level" == "rc" ]]; then + bundle exec bump set "$next_version" --no-commit --no-bundle +else + bundle exec bump "$level" --no-commit --no-bundle +fi + +git add "$version_file" +git commit -m "$tag" +git tag "$tag" + +echo "Pushing ${branch}..." +git push origin "$branch" + +echo "Pushing ${tag}..." +git push origin "$tag" + +echo "Released ${tag}." diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.2.gemfile similarity index 55% rename from gemfiles/rails_7.0.gemfile rename to gemfiles/rails_7.2.gemfile index 8b23c5a..26c61a3 100644 --- a/gemfiles/rails_7.0.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -2,10 +2,10 @@ source "http://rubygems.org" -gem "rails", "~> 7.0" -gem "rspec", "~> 3.12" -gem "rspec-rails", "~> 6.0" +gem "rails", "~> 7.2" +gem "rspec", "~> 3.13" +gem "rspec-rails", "~> 7.0" gem "rails-controller-testing" -gem "sqlite3", "~> 1.4" +gem "sqlite3", "~> 2.1" gemspec path: "../" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_8.1.gemfile similarity index 55% rename from gemfiles/rails_7.1.gemfile rename to gemfiles/rails_8.1.gemfile index 331319d..3604b28 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -2,10 +2,10 @@ source "http://rubygems.org" -gem "rails", "~> 7.1" -gem "rspec", "~> 3.12" -gem "rspec-rails", "~> 6.1" +gem "rails", "~> 8.1" +gem "rspec", "~> 3.13" +gem "rspec-rails", "~> 8.0" gem "rails-controller-testing" -gem "sqlite3", "~> 1.4" +gem "sqlite3", "~> 2.1" gemspec path: "../" diff --git a/lib/typical_situation.rb b/lib/typical_situation.rb index 9e2ea52..d40393e 100644 --- a/lib/typical_situation.rb +++ b/lib/typical_situation.rb @@ -40,6 +40,7 @@ def typical_situation(model_type_symbol, only: nil) define_method :model_type do model_type_symbol end + protected :model_type if only only.each do |action| diff --git a/lib/typical_situation/identity.rb b/lib/typical_situation/identity.rb index 115c867..15c1219 100644 --- a/lib/typical_situation/identity.rb +++ b/lib/typical_situation/identity.rb @@ -3,6 +3,8 @@ module TypicalSituation # These Identity methods must be defined for each implementation. module Identity + protected + # Symbolized, underscored version of the model (class) to use. def model_type raise(NotImplementedError, "#model_type must be defined in the TypicalSituation implementation.") diff --git a/lib/typical_situation/operations.rb b/lib/typical_situation/operations.rb index 47e9dfa..21a21d7 100644 --- a/lib/typical_situation/operations.rb +++ b/lib/typical_situation/operations.rb @@ -4,6 +4,8 @@ module TypicalSituation # Model operations. # Assume that we're working w/ an ActiveRecord association collection. module Operations + protected + def scoped_resource collection end @@ -41,12 +43,29 @@ def has_errors? !@resource.errors.empty? end + # Collection pipeline lifecycle: + # collection - base relation (user-defined, required) + # scoped_resource - wraps/scopes the collection (visibility, tenancy, PHI) + # prepare_resources - standardized additional transforms (search, filter) + # apply_sorting - applies ORDER BY + # paginate_resources - applies pagination + # + # Override +prepare_resources+ in host controllers to add search/filter + # behavior without touching sorting or pagination hooks. def get_resources - @resources = paginate_resources(apply_sorting(scoped_resource)) + resources = scoped_resource + resources = prepare_resources(resources) + resources = apply_sorting(resources) + resources = paginate_resources(resources) + @resources = resources set_collection_instance @resources end + def prepare_resources(resources) + resources + end + def new_resource @resource = collection.build end @@ -93,20 +112,6 @@ def serializable_resource(resource) resource end - # Set the singular instance variable named after the model. Modules are delimited with "_". - # Example: a MockApplePie resource is set to ivar @mock_apple_pie. - def set_single_instance - instance_variable_set(:"@#{model_type.to_s.gsub("/", "__")}", @resource) - end - - # Set the plural instance variable named after the model. Modules are delimited with "_". - # Example: a MockApplePie resource collection is set to ivar @mock_apple_pies. - def set_collection_instance - instance_variable_set(:"@#{model_type.to_s.gsub("/", "__").pluralize}", @resources) - end - - protected - def id_param params[:id] end @@ -117,5 +122,15 @@ def apply_sorting(resources) return resources unless default_sorting_attribute resources.order(default_sorting_attribute => default_sorting_direction) end + + # Sets the singular ivar named after the model (modules delimited with "__"). + def set_single_instance + instance_variable_set(:"@#{model_type.to_s.gsub("/", "__")}", @resource) + end + + # Sets the plural ivar named after the model (modules delimited with "__"). + def set_collection_instance + instance_variable_set(:"@#{model_type.to_s.gsub("/", "__").pluralize}", @resources) + end end end diff --git a/lib/typical_situation/permissions.rb b/lib/typical_situation/permissions.rb index 367236b..04eca58 100644 --- a/lib/typical_situation/permissions.rb +++ b/lib/typical_situation/permissions.rb @@ -2,6 +2,8 @@ module TypicalSituation module Permissions + protected + def authorized?(_action, _resource = nil) true end diff --git a/lib/typical_situation/responses.rb b/lib/typical_situation/responses.rb index 788f6fe..f6806f2 100644 --- a/lib/typical_situation/responses.rb +++ b/lib/typical_situation/responses.rb @@ -3,6 +3,8 @@ module TypicalSituation # Rails MIME responses. module Responses + protected + # Return the collection as HTML or JSON # def respond_with_resources diff --git a/spec/controllers/mock_apple_pies_controller_spec.rb b/spec/controllers/mock_apple_pies_controller_spec.rb index 1044a6a..b488d36 100644 --- a/spec/controllers/mock_apple_pies_controller_spec.rb +++ b/spec/controllers/mock_apple_pies_controller_spec.rb @@ -260,50 +260,106 @@ describe "customization hooks" do describe "default behavior" do it "scoped_resource returns collection" do - expect(controller.scoped_resource).to eq(@grandma.mock_apple_pies) + expect(controller.send(:scoped_resource)).to eq(@grandma.mock_apple_pies) end it "find_resource calls find_in_collection" do - result = controller.find_resource(pie.id) + result = controller.send(:find_resource, pie.id) expect(result).to eq(pie) end it "default_sorting_attribute returns nil" do - expect(controller.default_sorting_attribute).to be_nil + expect(controller.send(:default_sorting_attribute)).to be_nil end it "default_sorting_direction returns :asc" do - expect(controller.default_sorting_direction).to eq(:asc) + expect(controller.send(:default_sorting_direction)).to eq(:asc) end it "paginate_resources returns unchanged resources" do resources = @grandma.mock_apple_pies - expect(controller.paginate_resources(resources)).to eq(resources) + expect(controller.send(:paginate_resources, resources)).to eq(resources) + end + + it "prepare_resources returns unchanged resources" do + resources = @grandma.mock_apple_pies + expect(controller.send(:prepare_resources, resources)).to eq(resources) end it "after_resource_created_path returns show path" do - path = controller.after_resource_created_path(pie) + path = controller.send(:after_resource_created_path, pie) expect(path).to eq({action: :show, id: pie.id}) end it "after_resource_updated_path returns show path" do - path = controller.after_resource_updated_path(pie) + path = controller.send(:after_resource_updated_path, pie) expect(path).to eq({action: :show, id: pie.id}) end it "after_resource_destroyed_path returns index path" do - path = controller.after_resource_destroyed_path(pie) + path = controller.send(:after_resource_destroyed_path, pie) expect(path).to eq({action: :index}) end end + describe "get_resources pipeline" do + it "runs scoped_resource, prepare_resources, apply_sorting, and paginate_resources in order" do + order = [] + scoped = @grandma.mock_apple_pies + prepared = scoped.where(id: pie.id) + sorted = prepared.reorder(ingredients: :desc) + paginated = sorted.limit(1) + + allow(controller).to receive(:scoped_resource) do + order << :scoped_resource + scoped + end + allow(controller).to receive(:prepare_resources) do |resources| + order << :prepare_resources + expect(resources).to eq(scoped) + prepared + end + allow(controller).to receive(:apply_sorting) do |resources| + order << :apply_sorting + expect(resources).to eq(prepared) + sorted + end + allow(controller).to receive(:paginate_resources) do |resources| + order << :paginate_resources + expect(resources).to eq(sorted) + paginated + end + + result = controller.send(:get_resources) + + expect(result).to eq(paginated) + expect(assigns(:mock_apple_pies)).to eq(paginated) + expect(order).to eq(%i[scoped_resource prepare_resources apply_sorting paginate_resources]) + end + + it "passes prepare_resources output into apply_sorting" do + filtered = @grandma.mock_apple_pies.where(id: pie.id) + allow(controller).to receive(:prepare_resources).and_return(filtered) + + controller.send(:get_resources) + expect(assigns(:mock_apple_pies)).to match_array([pie]) + end + + it "override of prepare_resources is respected" do + allow(controller).to receive(:prepare_resources) { |r| r.where(id: pie.id) } + + get :index + expect(assigns(:mock_apple_pies)).to eq([pie]) + end + end + describe "pagination_params" do it "permits page and per_page params" do allow(controller).to receive(:params).and_return( ActionController::Parameters.new(page: "2", per_page: "10", other: "ignored") ) - permitted = controller.pagination_params + permitted = controller.send(:pagination_params) expect(permitted[:page]).to eq("2") expect(permitted[:per_page]).to eq("10") expect(permitted[:other]).to be_nil @@ -319,20 +375,20 @@ describe "#permitted_create_params" do it "returns nil by default" do - expect(controller.permitted_create_params).to be_nil + expect(controller.send(:permitted_create_params)).to be_nil end end describe "#permitted_update_params" do it "returns nil by default" do - expect(controller.permitted_update_params).to be_nil + expect(controller.send(:permitted_update_params)).to be_nil end end describe "#create_params" do it "permits all params when permitted_create_params is nil" do allow(controller).to receive(:permitted_create_params).and_return(nil) - result = controller.create_params + result = controller.send(:create_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to eq(1) expect(result[:secret_field]).to eq("hidden") @@ -340,7 +396,7 @@ it "permits all params when permitted_create_params is empty" do allow(controller).to receive(:permitted_create_params).and_return([]) - result = controller.create_params + result = controller.send(:create_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to eq(1) expect(result[:secret_field]).to eq("hidden") @@ -348,7 +404,7 @@ it "filters params when permitted_create_params is specified" do allow(controller).to receive(:permitted_create_params).and_return([:ingredients]) - result = controller.create_params + result = controller.send(:create_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to be_nil expect(result[:secret_field]).to be_nil @@ -358,7 +414,7 @@ describe "#update_params" do it "permits all params when permitted_update_params is nil" do allow(controller).to receive(:permitted_update_params).and_return(nil) - result = controller.update_params + result = controller.send(:update_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to eq(1) expect(result[:secret_field]).to eq("hidden") @@ -366,7 +422,7 @@ it "permits all params when permitted_update_params is empty" do allow(controller).to receive(:permitted_update_params).and_return([]) - result = controller.update_params + result = controller.send(:update_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to eq(1) expect(result[:secret_field]).to eq("hidden") @@ -374,7 +430,7 @@ it "filters params when permitted_update_params is specified" do allow(controller).to receive(:permitted_update_params).and_return([:ingredients]) - result = controller.update_params + result = controller.send(:update_params) expect(result[:ingredients]).to eq("love") expect(result[:grandma_id]).to be_nil expect(result[:secret_field]).to be_nil diff --git a/spec/typical_situation_permissions_spec.rb b/spec/typical_situation_permissions_spec.rb index b7cf9e3..2f5aa1f 100644 --- a/spec/typical_situation_permissions_spec.rb +++ b/spec/typical_situation_permissions_spec.rb @@ -2,11 +2,9 @@ require "spec_helper" -PIES_COUNT = 5 - RSpec.describe MockApplePiesController, type: :controller do before(:each) do - @grandma = create(:grandma, pies_count: PIES_COUNT) + @grandma = create(:grandma, pies_count: 5) controller.current_grandma = @grandma end @@ -15,8 +13,8 @@ describe "authorization" do describe "default behavior" do it "authorized? returns true by default" do - expect(controller.authorized?(:show, pie)).to be true - expect(controller.authorized?(:destroy, pie)).to be true + expect(controller.send(:authorized?, :show, pie)).to be true + expect(controller.send(:authorized?, :destroy, pie)).to be true end end @@ -44,22 +42,22 @@ def authorized?(action, resource = nil) it "allows destroy when authorized" do pie = create(:mock_apple_pie, grandma: @grandma, ingredients: "allowed") - expect(custom_controller.authorized?(:destroy, pie)).to be true + expect(custom_controller.send(:authorized?, :destroy, pie)).to be true end it "blocks destroy when unauthorized" do pie = create(:mock_apple_pie, grandma: @grandma, ingredients: "forbidden_ingredient") - expect(custom_controller.authorized?(:destroy, pie)).to be false + expect(custom_controller.send(:authorized?, :destroy, pie)).to be false end it "allows show when authorized" do pie = create(:mock_apple_pie, grandma: @grandma, ingredients: "allowed") - expect(custom_controller.authorized?(:show, pie)).to be true + expect(custom_controller.send(:authorized?, :show, pie)).to be true end it "blocks show when unauthorized" do pie = create(:mock_apple_pie, grandma: @grandma, ingredients: "secret_ingredient") - expect(custom_controller.authorized?(:show, pie)).to be false + expect(custom_controller.send(:authorized?, :show, pie)).to be false end end @@ -83,7 +81,7 @@ def respond_as_forbidden # Test that respond_as_forbidden is called (we can't easily test the full response in this setup) expect(custom_controller).to receive(:redirect_to).with("/custom_forbidden") - custom_controller.respond_as_forbidden + custom_controller.send(:respond_as_forbidden) end end end diff --git a/spec/typical_situation_syntax_spec.rb b/spec/typical_situation_syntax_spec.rb index a5c249c..864a0f5 100644 --- a/spec/typical_situation_syntax_spec.rb +++ b/spec/typical_situation_syntax_spec.rb @@ -30,15 +30,15 @@ def find_in_collection(id) end it "defines model_type method" do - expect(controller.model_type).to eq(:test_model) + expect(controller.send(:model_type)).to eq(:test_model) end it "works with all typical situation functionality" do - expect(controller.model_class).to eq(TestModel) + expect(controller.send(:model_class)).to eq(TestModel) end it "works with plural_model_type" do - expect(controller.plural_model_type).to eq(:test_models) + expect(controller.send(:plural_model_type)).to eq(:test_models) end it "works with model_params" do @@ -49,8 +49,8 @@ def find_in_collection(id) ) end - expect(controller.model_params).to be_a(ActionController::Parameters) - expect(controller.model_params[:name]).to eq("Test") + expect(controller.send(:model_params)).to be_a(ActionController::Parameters) + expect(controller.send(:model_params)[:name]).to eq("Test") end end @@ -89,7 +89,7 @@ def find_in_collection(id) end it "still works with model_type functionality" do - expect(limited_controller.model_type).to eq(:test_model) + expect(limited_controller.send(:model_type)).to eq(:test_model) end end @@ -120,7 +120,7 @@ def find_in_collection(id) end it "defines model_type method" do - expect(rest_controller.model_type).to eq(:test_model) + expect(rest_controller.send(:model_type)).to eq(:test_model) end it "includes all REST actions" do @@ -153,7 +153,7 @@ def find_in_collection(id) equivalent_controller = equivalent_class.new equivalent_controller.current_grandma = grandma - expect(rest_controller.model_type).to eq(equivalent_controller.model_type) + expect(rest_controller.send(:model_type)).to eq(equivalent_controller.send(:model_type)) %i[index show new create edit update destroy].each do |action| expect(rest_controller.respond_to?(action)).to eq(equivalent_controller.respond_to?(action)) @@ -188,7 +188,7 @@ def find_in_collection(id) end it "defines model_type method" do - expect(crud_controller.model_type).to eq(:test_model) + expect(crud_controller.send(:model_type)).to eq(:test_model) end it "includes only CRUD actions" do @@ -224,7 +224,7 @@ def find_in_collection(id) equivalent_controller = equivalent_class.new equivalent_controller.current_grandma = grandma - expect(crud_controller.model_type).to eq(equivalent_controller.model_type) + expect(crud_controller.send(:model_type)).to eq(equivalent_controller.send(:model_type)) %i[index show new create edit update destroy].each do |action| expect(crud_controller.respond_to?(action)).to eq(equivalent_controller.respond_to?(action)) @@ -291,10 +291,10 @@ def find_in_collection(id) end it "both helpers work with model_type functionality" do - expect(rest_controller.model_type).to eq(:test_model) - expect(crud_controller.model_type).to eq(:test_model) - expect(rest_controller.model_class).to eq(TestModel) - expect(crud_controller.model_class).to eq(TestModel) + expect(rest_controller.send(:model_type)).to eq(:test_model) + expect(crud_controller.send(:model_type)).to eq(:test_model) + expect(rest_controller.send(:model_class)).to eq(TestModel) + expect(crud_controller.send(:model_class)).to eq(TestModel) end end @@ -341,8 +341,8 @@ def find_in_collection(id) old_controller = old_syntax_controller_class.new new_controller = new_syntax_controller_class.new - expect(old_controller.model_type).to eq(new_controller.model_type) - expect(old_controller.plural_model_type).to eq(new_controller.plural_model_type) + expect(old_controller.send(:model_type)).to eq(new_controller.send(:model_type)) + expect(old_controller.send(:plural_model_type)).to eq(new_controller.send(:plural_model_type)) end end diff --git a/typical_situation.gemspec b/typical_situation.gemspec index 5ca6f48..a0c45cb 100644 --- a/typical_situation.gemspec +++ b/typical_situation.gemspec @@ -17,13 +17,14 @@ Gem::Specification.new do |s| s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"] - s.required_ruby_version = ">= 3.0.0" + s.required_ruby_version = ">= 3.1.0" - s.add_runtime_dependency "rails", ">= 7.0.0" + s.add_runtime_dependency "rails", ">= 7.2.0" s.add_development_dependency "appraisal" s.add_development_dependency "bundler", ">= 2.2.0" - s.add_development_dependency "byebug" + s.add_development_dependency "bump" + s.add_development_dependency "debug" s.add_development_dependency "combustion" s.add_development_dependency "coveralls" s.add_development_dependency "factory_bot_rails"