├── .github ├── FUNDING.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── ci.yaml │ ├── github-actions-lint.yml │ ├── rubocop.yml │ └── yaml-lint.yml ├── .gitignore ├── .rubocop.yml ├── .yamllint.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app └── controllers │ └── inherited_resources │ └── base.rb ├── bin ├── bundle ├── rake └── rubocop ├── codecov.yml ├── gemfiles ├── rails_70 │ ├── Gemfile │ └── Gemfile.lock ├── rails_71 │ ├── Gemfile │ └── Gemfile.lock └── rails_72 │ ├── Gemfile │ └── Gemfile.lock ├── inherited_resources.gemspec ├── lib ├── generators │ └── rails │ │ ├── USAGE │ │ ├── inherited_resources_controller_generator.rb │ │ └── templates │ │ └── controller.rb.tt ├── inherited_resources.rb └── inherited_resources │ ├── actions.rb │ ├── base_helpers.rb │ ├── belongs_to_helpers.rb │ ├── blank_slate.rb │ ├── class_methods.rb │ ├── dsl.rb │ ├── engine.rb │ ├── polymorphic_helpers.rb │ ├── responder.rb │ ├── shallow_helpers.rb │ ├── singleton_helpers.rb │ ├── url_helpers.rb │ └── version.rb └── test ├── aliases_test.rb ├── association_chain_test.rb ├── autoload └── engine.rb ├── base_test.rb ├── belongs_to_test.rb ├── belongs_to_with_shallow_test.rb ├── changelog_test.rb ├── class_methods_test.rb ├── customized_base_test.rb ├── customized_belongs_to_test.rb ├── customized_redirect_to_test.rb ├── defaults_test.rb ├── locales └── en.yml ├── multiple_nested_optional_belongs_to_test.rb ├── nested_belongs_to_test.rb ├── nested_belongs_to_with_shallow_test.rb ├── nested_model_with_shallow_test.rb ├── nested_singleton_test.rb ├── optional_belongs_to_test.rb ├── parent_controller_test.rb ├── polymorphic_test.rb ├── redirect_to_test.rb ├── singleton_test.rb ├── strong_parameters_test.rb ├── tasks ├── gemfile_test.rb └── gemspec_test.rb ├── test_helper.rb ├── url_helpers_test.rb └── views ├── address ├── edit.html.erb ├── new.html.erb └── show.html.erb ├── cars ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── cities ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── comments ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── educations └── new.html.erb ├── employees ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── geolocation └── show.html.erb ├── groups └── edit.html.erb ├── managers ├── edit.html.erb ├── new.html.erb └── show.html.erb ├── painters ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── pets ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── photos └── index.html.erb ├── plates ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── products ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── professors ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── projects ├── edit.html.erb ├── index.html.erb ├── index.json.erb ├── new.html.erb ├── respond_to_skip_default_template.html.erb ├── respond_with_resource.html.erb └── show.html.erb ├── students ├── edit.html.erb └── new.html.erb ├── tags ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── trees ├── edit.html.erb ├── index.html.erb ├── new.html.erb └── show.html.erb ├── university └── lecturers │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb ├── users ├── create.js.erb ├── destroy.js.erb ├── edit.html.erb ├── index.html.erb ├── new.html.erb ├── show.html.erb └── update.js.erb ├── venue └── show.html.erb └── widgets └── new.html.erb /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | tidelift: rubygems/inherited_resources 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | groups: 8 | github_actions: 9 | patterns: 10 | - "*" 11 | - package-ecosystem: bundler 12 | directory: / 13 | schedule: 14 | interval: monthly 15 | versioning-strategy: lockfile-only 16 | groups: 17 | rails_default: 18 | patterns: 19 | - "*" 20 | - package-ecosystem: bundler 21 | directory: /gemfiles/rails_61 22 | schedule: 23 | interval: monthly 24 | versioning-strategy: lockfile-only 25 | groups: 26 | rails_61: 27 | patterns: 28 | - "*" 29 | - package-ecosystem: bundler 30 | directory: /gemfiles/rails_70 31 | schedule: 32 | interval: monthly 33 | versioning-strategy: lockfile-only 34 | groups: 35 | rails_70: 36 | patterns: 37 | - "*" 38 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Breaking Changes 🚨 4 | labels: 5 | - type breaking change 6 | - title: Enhancements ✨ 7 | labels: 8 | - type enhancement 9 | - title: Bug Fixes 🐛 10 | labels: 11 | - type bug fix 12 | - title: Security Fixes 🔒 13 | labels: 14 | - type security fix 15 | - title: Other Changes 🛠 16 | labels: 17 | - "*" 18 | exclude: 19 | authors: 20 | - dependabot 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | test: 11 | name: test (ruby-${{ matrix.ruby }}, ${{ matrix.rails }}) 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 15 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ruby: 18 | - "3.1" 19 | - "3.2" 20 | - "3.3" 21 | - "3.4" 22 | rails: 23 | - rails_70 24 | - rails_71 25 | - rails_72 26 | - rails_80 27 | exclude: 28 | - ruby: "3.1" 29 | rails: rails_80 30 | - ruby: "3.4" 31 | rails: rails_70 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Configure bundler (default) 35 | run: | 36 | echo "BUNDLE_GEMFILE=Gemfile" >> "$GITHUB_ENV" 37 | if: matrix.rails == 'rails_80' 38 | - name: Configure bundler (alternative) 39 | run: | 40 | echo "BUNDLE_GEMFILE=gemfiles/${{ matrix.rails }}/Gemfile" >> "$GITHUB_ENV" 41 | if: matrix.rails != 'rails_80' 42 | - uses: ruby/setup-ruby@v1 43 | with: 44 | ruby-version: ${{ matrix.ruby }} 45 | bundler-cache: true 46 | - name: Run tests 47 | env: 48 | COVERAGE: true 49 | run: | 50 | bundle exec rake test TESTOPTS="--verbose" 51 | mv coverage/coverage.xml coverage/coverage-ruby-${{ matrix.ruby }}-${{ matrix.rails }}.xml 52 | - uses: actions/upload-artifact@v4 53 | with: 54 | name: coverage-ruby-${{ matrix.ruby }}-${{ matrix.rails }} 55 | path: coverage 56 | if-no-files-found: error 57 | 58 | upload_coverage: 59 | name: Upload Coverage 60 | runs-on: ubuntu-latest 61 | needs: 62 | - test 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: actions/download-artifact@v4 66 | with: 67 | pattern: coverage-ruby-* 68 | path: coverage 69 | merge-multiple: true 70 | - uses: codecov/codecov-action@v5 71 | with: 72 | token: ${{ secrets.CODECOV_TOKEN }} 73 | directory: coverage 74 | fail_ci_if_error: true 75 | -------------------------------------------------------------------------------- /.github/workflows/github-actions-lint.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Lint 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | github_actions_lint: 8 | name: Run actionlint 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: tj-actions/changed-files@v46 13 | id: changed-files 14 | with: 15 | files: | 16 | .github/workflows/*.yaml 17 | .github/workflows/*.yml 18 | - uses: reviewdog/action-actionlint@v1 19 | if: steps.changed-files.outputs.any_changed == 'true' 20 | with: 21 | fail_level: any 22 | filter_mode: nofilter # added (default), diff_context, file, nofilter 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | reporter: github-pr-check 25 | -------------------------------------------------------------------------------- /.github/workflows/rubocop.yml: -------------------------------------------------------------------------------- 1 | name: Rubocop 2 | 3 | on: 4 | pull_request: 5 | 6 | env: 7 | RUBY_VERSION: ${{ vars.RUBOCOP_RUBY_VERSION || '3.4' }} 8 | 9 | jobs: 10 | rubocop: 11 | name: Run rubocop 12 | runs-on: ubuntu-latest 13 | env: 14 | BUNDLE_ONLY: ${{ vars.RUBOCOP_BUNDLE_ONLY || 'rubocop' }} 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: tj-actions/changed-files@v46 18 | id: changed-files 19 | with: 20 | files: | 21 | .github/workflows/rubocop.yml 22 | .rubocop.yml 23 | **.rb 24 | bin/* 25 | gemfiles/**/Gemfile 26 | Gemfile* 27 | Rakefile 28 | *.gemspec 29 | - uses: ruby/setup-ruby@v1 30 | if: steps.changed-files.outputs.any_changed == 'true' 31 | with: 32 | ruby-version: ${{ env.RUBY_VERSION }} 33 | bundler-cache: true 34 | - uses: reviewdog/action-rubocop@v2 35 | if: steps.changed-files.outputs.any_changed == 'true' 36 | with: 37 | fail_level: any 38 | filter_mode: nofilter # added (default), diff_context, file, nofilter 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | skip_install: true 41 | use_bundler: true 42 | -------------------------------------------------------------------------------- /.github/workflows/yaml-lint.yml: -------------------------------------------------------------------------------- 1 | name: YAML Lint 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | yaml_lint: 8 | name: Run yamllint 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: tj-actions/changed-files@v46 13 | id: changed-files 14 | with: 15 | files: | 16 | **.yaml 17 | **.yml 18 | - uses: reviewdog/action-yamllint@v1 19 | if: steps.changed-files.outputs.any_changed == 'true' 20 | with: 21 | fail_level: any 22 | filter_mode: nofilter # added (default), diff_context, file, nofilter 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | reporter: github-pr-check 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | pkg 3 | coverage 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | plugins: 4 | - rubocop-minitest 5 | - rubocop-packaging 6 | - rubocop-performance 7 | 8 | AllCops: 9 | DisabledByDefault: true 10 | 11 | DisplayCopNames: true 12 | DisplayStyleGuide: true 13 | 14 | TargetRubyVersion: 3.1 15 | 16 | Metrics: 17 | Enabled: false 18 | 19 | Layout/CommentIndentation: 20 | Enabled: true 21 | 22 | Layout/EndAlignment: 23 | EnforcedStyleAlignWith: variable 24 | Enabled: true 25 | 26 | Layout/EmptyLines: 27 | Enabled: true 28 | 29 | Layout/EmptyLinesAroundAccessModifier: 30 | Enabled: true 31 | 32 | Layout/EmptyLinesAroundClassBody: 33 | Enabled: true 34 | 35 | Layout/IndentationConsistency: 36 | Enabled: true 37 | EnforcedStyle: indented_internal_methods 38 | 39 | Layout/IndentationWidth: 40 | Enabled: true 41 | 42 | Layout/IndentationStyle: 43 | Enabled: true 44 | 45 | Layout/TrailingWhitespace: 46 | Enabled: true 47 | 48 | Layout/TrailingEmptyLines: 49 | Enabled: true 50 | 51 | Lint/AmbiguousOperator: 52 | Enabled: true 53 | 54 | Lint/AmbiguousRegexpLiteral: 55 | Enabled: true 56 | 57 | Lint/UselessAssignment: 58 | Enabled: true 59 | 60 | Minitest: 61 | Enabled: true 62 | 63 | Minitest/AssertEmpty: 64 | Enabled: true 65 | 66 | Minitest/AssertEmptyLiteral: 67 | Enabled: true 68 | 69 | Minitest/AssertEqual: 70 | Enabled: true 71 | 72 | Minitest/AssertInDelta: 73 | Enabled: true 74 | 75 | Minitest/AssertIncludes: 76 | Enabled: true 77 | 78 | Minitest/AssertInstanceOf: 79 | Enabled: true 80 | 81 | Minitest/AssertKindOf: 82 | Enabled: true 83 | 84 | Minitest/AssertMatch: 85 | Enabled: true 86 | 87 | Minitest/AssertNil: 88 | Enabled: true 89 | 90 | Minitest/AssertOperator: 91 | Enabled: true 92 | 93 | Minitest/AssertOutput: 94 | Enabled: true 95 | 96 | Minitest/AssertPathExists: 97 | Enabled: true 98 | 99 | Minitest/AssertPredicate: 100 | Enabled: true 101 | 102 | Minitest/AssertRaisesCompoundBody: 103 | Enabled: true 104 | 105 | Minitest/AssertRaisesWithRegexpArgument: 106 | Enabled: true 107 | 108 | Minitest/AssertRespondTo: 109 | Enabled: true 110 | 111 | Minitest/AssertSame: 112 | Enabled: true 113 | 114 | Minitest/AssertSilent: 115 | Enabled: true 116 | 117 | Minitest/AssertTruthy: 118 | Enabled: true 119 | 120 | Minitest/AssertWithExpectedArgument: 121 | Enabled: true 122 | 123 | Minitest/AssertionInLifecycleHook: 124 | Enabled: true 125 | 126 | Minitest/DuplicateTestRun: 127 | Enabled: true 128 | 129 | Minitest/EmptyLineBeforeAssertionMethods: 130 | Enabled: true 131 | 132 | Minitest/Focus: 133 | Enabled: true 134 | 135 | Minitest/GlobalExpectations: 136 | Enabled: true 137 | 138 | Minitest/LifecycleHooksOrder: 139 | Enabled: true 140 | 141 | Minitest/LiteralAsActualArgument: 142 | Enabled: true 143 | 144 | Minitest/MultipleAssertions: 145 | Enabled: false 146 | 147 | Minitest/NoAssertions: 148 | Enabled: true 149 | 150 | Minitest/NoTestCases: 151 | Enabled: true 152 | 153 | Minitest/NonExecutableTestMethod: 154 | Enabled: true 155 | 156 | Minitest/NonPublicTestMethod: 157 | Enabled: true 158 | 159 | Minitest/RedundantMessageArgument: 160 | Enabled: true 161 | 162 | Minitest/RefuteEmpty: 163 | Enabled: true 164 | 165 | Minitest/RefuteEqual: 166 | Enabled: true 167 | 168 | Minitest/RefuteFalse: 169 | Enabled: true 170 | 171 | Minitest/RefuteInDelta: 172 | Enabled: true 173 | 174 | Minitest/RefuteIncludes: 175 | Enabled: true 176 | 177 | Minitest/RefuteInstanceOf: 178 | Enabled: true 179 | 180 | Minitest/RefuteKindOf: 181 | Enabled: true 182 | 183 | Minitest/RefuteMatch: 184 | Enabled: true 185 | 186 | Minitest/RefuteNil: 187 | Enabled: true 188 | 189 | Minitest/RefuteOperator: 190 | Enabled: true 191 | 192 | Minitest/RefutePathExists: 193 | Enabled: true 194 | 195 | Minitest/RefutePredicate: 196 | Enabled: true 197 | 198 | Minitest/RefuteRespondTo: 199 | Enabled: true 200 | 201 | Minitest/RefuteSame: 202 | Enabled: true 203 | 204 | Minitest/ReturnInTestMethod: 205 | Enabled: true 206 | 207 | Minitest/SkipEnsure: 208 | Enabled: true 209 | 210 | Minitest/SkipWithoutReason: 211 | Enabled: true 212 | 213 | Minitest/TestFileName: 214 | Enabled: true 215 | 216 | Minitest/TestMethodName: 217 | Enabled: true 218 | 219 | Minitest/UnreachableAssertion: 220 | Enabled: true 221 | 222 | Minitest/UnspecifiedException: 223 | Enabled: true 224 | 225 | Minitest/UselessAssertion: 226 | Enabled: true 227 | 228 | Packaging/BundlerSetupInTests: 229 | Enabled: true 230 | 231 | Packaging/GemspecGit: 232 | Enabled: true 233 | 234 | Packaging/RequireHardcodingLib: 235 | Enabled: true 236 | 237 | Packaging/RequireRelativeHardcodingLib: 238 | Enabled: true 239 | 240 | Performance: 241 | Enabled: true 242 | 243 | Performance/AncestorsInclude: 244 | Enabled: false 245 | 246 | Performance/ArraySemiInfiniteRangeSlice: 247 | Enabled: false 248 | 249 | Performance/BigDecimalWithNumericArgument: 250 | Enabled: true 251 | 252 | Performance/BindCall: 253 | Enabled: true 254 | 255 | Performance/BlockGivenWithExplicitBlock: 256 | Enabled: true 257 | 258 | Performance/Caller: 259 | Enabled: true 260 | 261 | Performance/CaseWhenSplat: 262 | Enabled: true 263 | 264 | Performance/Casecmp: 265 | Enabled: false 266 | 267 | Performance/ChainArrayAllocation: 268 | Enabled: false 269 | 270 | Performance/CollectionLiteralInLoop: 271 | Enabled: true 272 | Exclude: 273 | - spec/**/* 274 | 275 | Performance/CompareWithBlock: 276 | Enabled: true 277 | 278 | Performance/ConcurrentMonotonicTime: 279 | Enabled: true 280 | 281 | Performance/ConstantRegexp: 282 | Enabled: true 283 | 284 | Performance/Count: 285 | Enabled: true 286 | 287 | Performance/DeletePrefix: 288 | Enabled: true 289 | 290 | Performance/DeleteSuffix: 291 | Enabled: true 292 | 293 | Performance/Detect: 294 | Enabled: true 295 | 296 | Performance/DoubleStartEndWith: 297 | Enabled: true 298 | IncludeActiveSupportAliases: true 299 | 300 | Performance/EndWith: 301 | Enabled: true 302 | 303 | Performance/FixedSize: 304 | Enabled: true 305 | 306 | Performance/FlatMap: 307 | Enabled: true 308 | EnabledForFlattenWithoutParams: false 309 | 310 | Performance/InefficientHashSearch: 311 | Enabled: true 312 | 313 | Performance/IoReadlines: 314 | Enabled: true 315 | 316 | Performance/MapCompact: 317 | Enabled: false 318 | 319 | Performance/MapMethodChain: 320 | Enabled: false 321 | 322 | Performance/MethodObjectAsBlock: 323 | Enabled: true 324 | 325 | Performance/OpenStruct: 326 | Enabled: true 327 | 328 | Performance/RangeInclude: 329 | Enabled: true 330 | 331 | Performance/RedundantBlockCall: 332 | Enabled: false 333 | 334 | Performance/RedundantEqualityComparisonBlock: 335 | Enabled: false 336 | 337 | Performance/RedundantMatch: 338 | Enabled: true 339 | 340 | Performance/RedundantMerge: 341 | Enabled: true 342 | MaxKeyValuePairs: 2 343 | 344 | Performance/RedundantSortBlock: 345 | Enabled: true 346 | 347 | Performance/RedundantSplitRegexpArgument: 348 | Enabled: true 349 | 350 | Performance/RedundantStringChars: 351 | Enabled: true 352 | 353 | Performance/RegexpMatch: 354 | Enabled: true 355 | 356 | Performance/ReverseEach: 357 | Enabled: true 358 | 359 | Performance/ReverseFirst: 360 | Enabled: true 361 | 362 | Performance/SelectMap: 363 | Enabled: false 364 | 365 | Performance/Size: 366 | Enabled: true 367 | 368 | Performance/SortReverse: 369 | Enabled: true 370 | 371 | Performance/Squeeze: 372 | Enabled: true 373 | 374 | Performance/StartWith: 375 | Enabled: true 376 | 377 | Performance/StringBytesize: 378 | Enabled: true 379 | 380 | Performance/StringIdentifierArgument: 381 | Enabled: true 382 | 383 | Performance/StringInclude: 384 | Enabled: true 385 | 386 | Performance/StringReplacement: 387 | Enabled: true 388 | 389 | Performance/Sum: 390 | Enabled: false 391 | 392 | Performance/TimesMap: 393 | Enabled: true 394 | 395 | Performance/UnfreezeString: 396 | Enabled: true 397 | 398 | Performance/UriDefaultParser: 399 | Enabled: true 400 | 401 | Performance/ZipWithoutBlock: 402 | Enabled: true 403 | 404 | Style/Encoding: 405 | Enabled: true 406 | 407 | Style/FrozenStringLiteralComment: 408 | Enabled: true 409 | Exclude: 410 | - bin/console 411 | 412 | Style/HashSyntax: 413 | Enabled: true 414 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # https://yamllint.readthedocs.io/en/stable/configuration.html 2 | extends: default 3 | ignore: | 4 | tmp/ 5 | vendor/ 6 | rules: # https://yamllint.readthedocs.io/en/stable/rules.html 7 | comments: 8 | min-spaces-from-content: 1 9 | document-start: disable 10 | line-length: disable 11 | truthy: 12 | allowed-values: 13 | - "true" 14 | - "false" 15 | - "on" 16 | - "off" 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 2.1.0 4 | 5 | * Support for setting the parent controller class [#903][] 6 | * Remove support for Ruby `< 3.1` 7 | * Remove support for Rails `< 7.0` 8 | * Test against Rails 8.0 9 | 10 | ## Version 2.0.1 11 | 12 | * Prefer `require_relative` for internal requires [#939][] 13 | 14 | ## Version 2.0.0 15 | 16 | * Test against Rails 7.2 [#921][] 17 | * Add frozen string literal to `InheritedResources` module [#933][] 18 | * Change return codes to new Responders defaults [#918][] 19 | 20 | ## Version 1.14.0 21 | 22 | * Remove upper bound dependency limits from gemspec 23 | * Allow using Rails 7.1 [#873][] 24 | * Remove support for Ruby `< 2.6` 25 | 26 | ## Version 1.13.1 27 | 28 | * Allow using Rails 7.0 29 | 30 | ## Version 1.13.0 31 | 32 | * Remove support for Ruby `< 2.5` 33 | * Coerce `:route_prefix` config option to a Symbol, ensuring compatibility with Rails versions that have resolved CVE-2021-22885 34 | 35 | ## Version 1.12.0 36 | 37 | * Remove support for Rails 5.0 and Rails 5.1 38 | * Allow using Rails 6.1 39 | 40 | ## Version 1.11.0 41 | 42 | * Add support for responders `>= 3.0` 43 | * Remove support for Ruby `< 2.4` 44 | 45 | ## Version 1.10.0 46 | 47 | _No changes_ 48 | 49 | ## Version 1.10.0.rc1 50 | 51 | * Preliminary support for Rails 6.0 52 | * Remove support for Rails 4.2 53 | 54 | ## Version 1.9.0 55 | 56 | * Support Rails 5.2.1 57 | * Remove support for Ruby `< 2.3` 58 | 59 | ## Version 1.8.0 60 | 61 | * Support Rails 5.2 62 | * Supports Ruby 2.4 63 | * Remove support for Ruby `< 2.2`, and Rails `< 4.2` 64 | * Fixed broken class name in belongs_to 65 | * Remove use of HttpCacheResponder 66 | * Correct request_name in isolated engines 67 | * Fix nested controllers and singleton option 68 | 69 | ## Version 1.7.2 70 | 71 | * Support Rails 5.1 72 | 73 | ## Version 1.7.1 74 | 75 | * Fix regression with `get_resource_ivar` that was returning `false` instead of `nil` when the value was not set 76 | * Do not load `ActionController::Base` on boot time 77 | 78 | ## Version 1.7.0 79 | 80 | * Support Rails 5 81 | * Remove support for Ruby `< 2.1` 82 | * Fix URL helpers on mountable engines 83 | * Allow support to has_scope `< 0.6` and `> 1.0`. Users are now able to choose which version they want to use in their applications 84 | 85 | ## Version 1.6.0 86 | 87 | * Support Rails 4.2 88 | 89 | ## Version 1.5.1 90 | 91 | * Lock the Rails version until only 4.2 92 | * Fix parent class lookup 93 | * Fix resource_class default value definition 94 | 95 | ## Version 1.5.0 96 | 97 | * Supports nested modules (namespaced models and controllers) 98 | * Supports Rails 4 Strong Parameters notation 99 | 100 | ## Version 1.4.1 101 | 102 | * Supports Rails 4 103 | * Improved compatibility with strong params 104 | 105 | ## Version 1.4.0 106 | 107 | * Supports Ruby 2.0.0 108 | * Added support for the strong_parameters gem. See the README for more 109 | * Added the ability to pass without_protection when creating/updating 110 | * Fixed multi-level nested singletons 111 | * Correct paths now generated for uncountable shallow resources 112 | 113 | ## Version 1.3.1 114 | 115 | * Fix polymorphic_belongs_to to get the parent 116 | * Added support for Rails 3.2 117 | * Added support to responders >= 0.6.0 118 | 119 | ## Version 1.3.0 120 | 121 | * Added support for multiple polymorphic optional nesting 122 | * Fix nested namespace in mountable apps 123 | * Added support for rails 3.1 new mass assignment conventions 124 | * Turn InheritedResources::Base into a reloadable constant to fix reloading issues 125 | 126 | ## Version 1.2.2 127 | 128 | * Fix a bug in params parsing 129 | * Call .scoped only if it is available 130 | 131 | ## Version 1.2.1 132 | 133 | * Fix a bug with namespaces 134 | * Use Post.scoped instead of Post.all in collection 135 | 136 | ## Version 1.2 137 | 138 | * Improved lookup for namespaces (by github.com/Sirupsen) 139 | * Support to custom actions (by github.com/lda) 140 | * Rails 3.1 compatibility (by github.com/etehtsea) 141 | 142 | ## Version 1.1 143 | 144 | * Rails 3 compatible 145 | 146 | ## Version 1.0 147 | 148 | * responders was removed from InheritedResources core and is a dependency. To install it, please do 149 | 150 | sudo gem install responders 151 | 152 | * has_scope was removed from InheritedResources core and is now available as a standalone gem 153 | 154 | To install it, please do 155 | 156 | sudo gem install has_scope 157 | 158 | ## Version 0.9 159 | 160 | * Allow dual blocks in destroy 161 | * Added :if and :unless to has_scope (thanks to Jack Danger) 162 | * Added create_resource, update_resource and delete_resource hooks (thanks to Carlos Antonio da Silva) 163 | * Backported ActionController::Responder from Rails 3 164 | * Added parent_url helper 165 | * Added association_chain helper (as suggested by https://github.com/emmanuel) 166 | 167 | ## Version 0.8 168 | 169 | * Fixed a small bug on optional belongs to with namespaced controllers 170 | * Allow a parameter to be given to collection_url in polymorphic cases to replace the parent 171 | * Allow InheritedResources to be called without inheritance 172 | * Ensure that controllers that inherit from a controller with InheritedResources works properly 173 | 174 | ## Version 0.7 175 | 176 | * Allow procs as default value in has scope to be able to use values from session, for example 177 | * Allow blocks with arity 0 or -1 to be given as the redirect url 178 | 179 | def destroy 180 | destroy!{ project_url(@project) } 181 | end 182 | 183 | * Allow interpolation_options to be set in the application controller 184 | * Added has_scope to controller (an interface for named_scopes) 185 | * Added polymorphic_belongs_to, optional_belongs_to and singleton_belongs_to as quick methods 186 | * Only load belongs_to, singleton and polymorphic helpers if they are actually required. base_helpers, class_methods, dumb_responder and url_helpers are loaded when you inherited from base for the first time 187 | 188 | # Version 0.6 189 | 190 | * Ensure that the default template is not rendered if the default_template_format is not accepted. This is somehow related with the security breach report 191 | 192 | https://rorsecurity.info/journal/2009/4/24/hidden-actions-render-templates.html 193 | 194 | IR forbids based on mime types. For example: respond_to :html, :except => :index ensures that the index.html.erb view is not rendered, making your IR controllers safer. 195 | 196 | * Fixed a bug that happens only when format.xml is given to blocks and then it acts as default, instead of format.html 197 | * Fixed a strange bug where when you have create.html.erb or update.html.erb, it makes IE6 and IE7 return unprocessable entity (because they send Mime::ALL) 198 | * Stop rescuing any error when constantizing the resource class and allow route_prefix to be nil 199 | * Cleaned up tests and responder structure. Whenever you pass a block to aliases and this block responds to the request, the other blocks are not parsed improving performance 200 | * [BACKWARDS INCOMPATIBLE] By default, Inherited Resources respond only :html requests 201 | * Added a quick way to overwrite the redirect to url in :create, :update and :destroy 202 | 203 | ## Version 0.5 204 | 205 | * Decoupled routes name from :instance_name and :collection_name. This way we have more flexibility. Use route_instance_name and route_collection_name to to change routes 206 | * Avoid calling human_name on nil when a resource class is not defined 207 | * Only call I18n if it's defined 208 | 209 | ## Version 0.4 210 | 211 | * Dealing with namespaced controllers out of the box 212 | * Added support to namespaced routes through :route_prefix 213 | * Added fix when resource_url is not defined 214 | * Added better handling for namespaced controllers 215 | * Added flash messages scoped by namespaced controllers 216 | * Deprecated {{resource}} in I18n, use {{resource_name}} instead 217 | * rspec bug fix is not automatically required anymore. User has to do it explicitly 218 | * Added a file which fix a rspec bug when render is called inside a method which receives a block 219 | * parent? does not take begin_of_association_chain into account anymore 220 | * Added options to url helpers 221 | * Added :optional to belongs_to associations. It allows you to deal with categories/1/products/2 and /products/2 with just one controller 222 | * Cleaned up tests 223 | 224 | ## Version 0.3 225 | 226 | * Minor bump after three bug fixes 227 | * Bug fix when showing warning of constant redefinition 228 | * Bug fix with ApplicationController not being unloaded properly on development 229 | * Bug fix when having root singleton resources. Calling `collection_url` would raise "NoMethodError \_url", not it will call root_url 230 | * More comments on UrlHelpers 231 | 232 | ## Version 0.2 233 | 234 | * Bug fix when ApplicationController is already loaded when we load respond_to 235 | * Added support success/failure blocks 236 | * Eager loading of files to work properly in multithreaded environments 237 | 238 | ## Version 0.1 239 | 240 | * Added more helper_methods 241 | * Added Rails 2.3.0 and changed tests to work with ActionController::TestCase 242 | * First release. Support to I18n, singleton controllers, polymorphic controllers, belongs_to, nested_belongs_to and url helpers 243 | 244 | [#873]: https://github.com/activeadmin/inherited_resources/pull/873 245 | [#903]: https://github.com/activeadmin/inherited_resources/pull/903 246 | [#918]: https://github.com/activeadmin/inherited_resources/pull/918 247 | [#921]: https://github.com/activeadmin/inherited_resources/pull/921 248 | [#933]: https://github.com/activeadmin/inherited_resources/pull/933 249 | [#939]: https://github.com/activeadmin/inherited_resources/pull/939 250 | [#942]: https://github.com/activeadmin/inherited_resources/pull/942 251 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gemspec path: '.' 5 | 6 | group :development do 7 | gem 'rails', '~> 8.0.0' 8 | 9 | gem 'mocha' 10 | gem 'minitest' 11 | gem 'minitest-reporters' 12 | gem 'rails-controller-testing' 13 | gem 'simplecov', require: false 14 | gem 'simplecov-cobertura' 15 | gem 'warning' 16 | end 17 | 18 | group :rubocop do 19 | gem 'rubocop' 20 | gem 'rubocop-minitest' 21 | gem 'rubocop-packaging' 22 | gem 'rubocop-performance' 23 | end 24 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | inherited_resources (2.1.0) 5 | actionpack (>= 7.0) 6 | has_scope (>= 0.6) 7 | railties (>= 7.0) 8 | responders (>= 2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actioncable (8.0.2) 14 | actionpack (= 8.0.2) 15 | activesupport (= 8.0.2) 16 | nio4r (~> 2.0) 17 | websocket-driver (>= 0.6.1) 18 | zeitwerk (~> 2.6) 19 | actionmailbox (8.0.2) 20 | actionpack (= 8.0.2) 21 | activejob (= 8.0.2) 22 | activerecord (= 8.0.2) 23 | activestorage (= 8.0.2) 24 | activesupport (= 8.0.2) 25 | mail (>= 2.8.0) 26 | actionmailer (8.0.2) 27 | actionpack (= 8.0.2) 28 | actionview (= 8.0.2) 29 | activejob (= 8.0.2) 30 | activesupport (= 8.0.2) 31 | mail (>= 2.8.0) 32 | rails-dom-testing (~> 2.2) 33 | actionpack (8.0.2) 34 | actionview (= 8.0.2) 35 | activesupport (= 8.0.2) 36 | nokogiri (>= 1.8.5) 37 | rack (>= 2.2.4) 38 | rack-session (>= 1.0.1) 39 | rack-test (>= 0.6.3) 40 | rails-dom-testing (~> 2.2) 41 | rails-html-sanitizer (~> 1.6) 42 | useragent (~> 0.16) 43 | actiontext (8.0.2) 44 | actionpack (= 8.0.2) 45 | activerecord (= 8.0.2) 46 | activestorage (= 8.0.2) 47 | activesupport (= 8.0.2) 48 | globalid (>= 0.6.0) 49 | nokogiri (>= 1.8.5) 50 | actionview (8.0.2) 51 | activesupport (= 8.0.2) 52 | builder (~> 3.1) 53 | erubi (~> 1.11) 54 | rails-dom-testing (~> 2.2) 55 | rails-html-sanitizer (~> 1.6) 56 | activejob (8.0.2) 57 | activesupport (= 8.0.2) 58 | globalid (>= 0.3.6) 59 | activemodel (8.0.2) 60 | activesupport (= 8.0.2) 61 | activerecord (8.0.2) 62 | activemodel (= 8.0.2) 63 | activesupport (= 8.0.2) 64 | timeout (>= 0.4.0) 65 | activestorage (8.0.2) 66 | actionpack (= 8.0.2) 67 | activejob (= 8.0.2) 68 | activerecord (= 8.0.2) 69 | activesupport (= 8.0.2) 70 | marcel (~> 1.0) 71 | activesupport (8.0.2) 72 | base64 73 | benchmark (>= 0.3) 74 | bigdecimal 75 | concurrent-ruby (~> 1.0, >= 1.3.1) 76 | connection_pool (>= 2.2.5) 77 | drb 78 | i18n (>= 1.6, < 2) 79 | logger (>= 1.4.2) 80 | minitest (>= 5.1) 81 | securerandom (>= 0.3) 82 | tzinfo (~> 2.0, >= 2.0.5) 83 | uri (>= 0.13.1) 84 | ansi (1.5.0) 85 | ast (2.4.3) 86 | base64 (0.2.0) 87 | benchmark (0.4.0) 88 | bigdecimal (3.1.9) 89 | builder (3.3.0) 90 | concurrent-ruby (1.3.5) 91 | connection_pool (2.5.3) 92 | crass (1.0.6) 93 | date (3.4.1) 94 | docile (1.4.1) 95 | drb (2.2.1) 96 | erubi (1.13.1) 97 | globalid (1.2.1) 98 | activesupport (>= 6.1) 99 | has_scope (0.8.2) 100 | actionpack (>= 5.2) 101 | activesupport (>= 5.2) 102 | i18n (1.14.7) 103 | concurrent-ruby (~> 1.0) 104 | io-console (0.8.0) 105 | irb (1.15.2) 106 | pp (>= 0.6.0) 107 | rdoc (>= 4.0.0) 108 | reline (>= 0.4.2) 109 | json (2.11.3) 110 | language_server-protocol (3.17.0.4) 111 | lint_roller (1.1.0) 112 | logger (1.7.0) 113 | loofah (2.24.0) 114 | crass (~> 1.0.2) 115 | nokogiri (>= 1.12.0) 116 | mail (2.8.1) 117 | mini_mime (>= 0.1.1) 118 | net-imap 119 | net-pop 120 | net-smtp 121 | marcel (1.0.4) 122 | mini_mime (1.1.5) 123 | mini_portile2 (2.8.8) 124 | minitest (5.25.5) 125 | minitest-reporters (1.7.1) 126 | ansi 127 | builder 128 | minitest (>= 5.0) 129 | ruby-progressbar 130 | mocha (2.7.1) 131 | ruby2_keywords (>= 0.0.5) 132 | net-imap (0.5.7) 133 | date 134 | net-protocol 135 | net-pop (0.1.2) 136 | net-protocol 137 | net-protocol (0.2.2) 138 | timeout 139 | net-smtp (0.5.1) 140 | net-protocol 141 | nio4r (2.7.4) 142 | nokogiri (1.18.8) 143 | mini_portile2 (~> 2.8.2) 144 | racc (~> 1.4) 145 | nokogiri (1.18.8-aarch64-linux-gnu) 146 | racc (~> 1.4) 147 | nokogiri (1.18.8-arm64-darwin) 148 | racc (~> 1.4) 149 | nokogiri (1.18.8-x86_64-darwin) 150 | racc (~> 1.4) 151 | nokogiri (1.18.8-x86_64-linux-gnu) 152 | racc (~> 1.4) 153 | parallel (1.27.0) 154 | parser (3.3.8.0) 155 | ast (~> 2.4.1) 156 | racc 157 | pp (0.6.2) 158 | prettyprint 159 | prettyprint (0.2.0) 160 | prism (1.4.0) 161 | psych (5.2.3) 162 | date 163 | stringio 164 | racc (1.8.1) 165 | rack (3.1.13) 166 | rack-session (2.1.0) 167 | base64 (>= 0.1.0) 168 | rack (>= 3.0.0) 169 | rack-test (2.2.0) 170 | rack (>= 1.3) 171 | rackup (2.2.1) 172 | rack (>= 3) 173 | rails (8.0.2) 174 | actioncable (= 8.0.2) 175 | actionmailbox (= 8.0.2) 176 | actionmailer (= 8.0.2) 177 | actionpack (= 8.0.2) 178 | actiontext (= 8.0.2) 179 | actionview (= 8.0.2) 180 | activejob (= 8.0.2) 181 | activemodel (= 8.0.2) 182 | activerecord (= 8.0.2) 183 | activestorage (= 8.0.2) 184 | activesupport (= 8.0.2) 185 | bundler (>= 1.15.0) 186 | railties (= 8.0.2) 187 | rails-controller-testing (1.0.5) 188 | actionpack (>= 5.0.1.rc1) 189 | actionview (>= 5.0.1.rc1) 190 | activesupport (>= 5.0.1.rc1) 191 | rails-dom-testing (2.2.0) 192 | activesupport (>= 5.0.0) 193 | minitest 194 | nokogiri (>= 1.6) 195 | rails-html-sanitizer (1.6.2) 196 | loofah (~> 2.21) 197 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 198 | railties (8.0.2) 199 | actionpack (= 8.0.2) 200 | activesupport (= 8.0.2) 201 | irb (~> 1.13) 202 | rackup (>= 1.0.0) 203 | rake (>= 12.2) 204 | thor (~> 1.0, >= 1.2.2) 205 | zeitwerk (~> 2.6) 206 | rainbow (3.1.1) 207 | rake (13.2.1) 208 | rdoc (6.13.1) 209 | psych (>= 4.0.0) 210 | regexp_parser (2.10.0) 211 | reline (0.6.1) 212 | io-console (~> 0.5) 213 | responders (3.1.1) 214 | actionpack (>= 5.2) 215 | railties (>= 5.2) 216 | rexml (3.4.1) 217 | rubocop (1.75.4) 218 | json (~> 2.3) 219 | language_server-protocol (~> 3.17.0.2) 220 | lint_roller (~> 1.1.0) 221 | parallel (~> 1.10) 222 | parser (>= 3.3.0.2) 223 | rainbow (>= 2.2.2, < 4.0) 224 | regexp_parser (>= 2.9.3, < 3.0) 225 | rubocop-ast (>= 1.44.0, < 2.0) 226 | ruby-progressbar (~> 1.7) 227 | unicode-display_width (>= 2.4.0, < 4.0) 228 | rubocop-ast (1.44.1) 229 | parser (>= 3.3.7.2) 230 | prism (~> 1.4) 231 | rubocop-minitest (0.38.0) 232 | lint_roller (~> 1.1) 233 | rubocop (>= 1.75.0, < 2.0) 234 | rubocop-ast (>= 1.38.0, < 2.0) 235 | rubocop-packaging (0.6.0) 236 | lint_roller (~> 1.1.0) 237 | rubocop (>= 1.72.1, < 2.0) 238 | rubocop-performance (1.25.0) 239 | lint_roller (~> 1.1) 240 | rubocop (>= 1.75.0, < 2.0) 241 | rubocop-ast (>= 1.38.0, < 2.0) 242 | ruby-progressbar (1.13.0) 243 | ruby2_keywords (0.0.5) 244 | securerandom (0.4.1) 245 | simplecov (0.22.0) 246 | docile (~> 1.1) 247 | simplecov-html (~> 0.11) 248 | simplecov_json_formatter (~> 0.1) 249 | simplecov-cobertura (2.1.0) 250 | rexml 251 | simplecov (~> 0.19) 252 | simplecov-html (0.13.1) 253 | simplecov_json_formatter (0.1.4) 254 | stringio (3.1.7) 255 | thor (1.3.2) 256 | timeout (0.4.3) 257 | tzinfo (2.0.6) 258 | concurrent-ruby (~> 1.0) 259 | unicode-display_width (3.1.4) 260 | unicode-emoji (~> 4.0, >= 4.0.4) 261 | unicode-emoji (4.0.4) 262 | uri (1.0.3) 263 | useragent (0.16.11) 264 | warning (1.5.0) 265 | websocket-driver (0.7.7) 266 | base64 267 | websocket-extensions (>= 0.1.0) 268 | websocket-extensions (0.1.5) 269 | zeitwerk (2.7.2) 270 | 271 | PLATFORMS 272 | aarch64-linux 273 | arm64-darwin 274 | ruby 275 | x86_64-darwin 276 | x86_64-linux 277 | 278 | DEPENDENCIES 279 | inherited_resources! 280 | minitest 281 | minitest-reporters 282 | mocha 283 | rails (~> 8.0.0) 284 | rails-controller-testing 285 | rubocop 286 | rubocop-minitest 287 | rubocop-packaging 288 | rubocop-performance 289 | simplecov 290 | simplecov-cobertura 291 | warning 292 | 293 | BUNDLED WITH 294 | 2.6.8 295 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2017 José Valim 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'bundler/gem_tasks' 3 | require 'rake/testtask' 4 | require 'rdoc/task' 5 | 6 | desc 'Run tests for InheritedResources.' 7 | Rake::TestTask.new(:test) do |t| 8 | t.pattern = "test/**/*_test.rb" 9 | t.libs << "test" 10 | t.libs << "lib" 11 | t.verbose = true 12 | end 13 | 14 | desc 'Generate documentation for InheritedResources.' 15 | Rake::RDocTask.new(:rdoc) do |rdoc| 16 | rdoc.rdoc_dir = 'rdoc' 17 | rdoc.title = 'InheritedResources' 18 | rdoc.options << '--line-numbers' << '--inline-source' 19 | rdoc.rdoc_files.include('README.rdoc') 20 | rdoc.rdoc_files.include('MIT-LICENSE') 21 | rdoc.rdoc_files.include('lib/**/*.rb') 22 | end 23 | 24 | task :rubocop do 25 | sh('bin/rubocop') 26 | end 27 | 28 | task default: [:test, :rubocop] 29 | -------------------------------------------------------------------------------- /app/controllers/inherited_resources/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module InheritedResources 3 | # = Base 4 | # 5 | # This is the base class that holds all actions. If you see the code for each 6 | # action, they are quite similar to Rails default scaffold. 7 | # 8 | # To change your base behavior, you can overwrite your actions and call super, 9 | # call default class method, call <actions class method 10 | # or overwrite some helpers in the base_helpers.rb file. 11 | # 12 | class Base < InheritedResources.parent_controller.constantize 13 | # Overwrite inherit_resources to add specific InheritedResources behavior. 14 | def self.inherit_resources(base) 15 | base.class_eval do 16 | include InheritedResources::Actions 17 | include InheritedResources::BaseHelpers 18 | extend InheritedResources::ClassMethods 19 | extend InheritedResources::UrlHelpers 20 | 21 | # Add at least :html mime type 22 | respond_to :html if self.mimes_for_respond_to.empty? 23 | self.responder = InheritedResources::Responder 24 | 25 | helper_method :resource, :collection, :resource_class, :association_chain, 26 | :resource_instance_name, :resource_collection_name, 27 | :resource_url, :resource_path, 28 | :collection_url, :collection_path, 29 | :new_resource_url, :new_resource_path, 30 | :edit_resource_url, :edit_resource_path, 31 | :parent_url, :parent_path, 32 | :smart_resource_url, :smart_collection_url 33 | 34 | self.class_attribute :resource_class, instance_writer: false unless self.respond_to? :resource_class 35 | self.class_attribute :parents_symbols, :resources_configuration, instance_writer: false 36 | 37 | protected :resource_class, :parents_symbols, :resources_configuration, 38 | :resource_class?, :parents_symbols?, :resources_configuration? 39 | end 40 | end 41 | 42 | inherit_resources(self) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( set -x; bundle $@ ) 4 | 5 | for gemfile in gemfiles/*/Gemfile; do 6 | ( set -x; BUNDLE_GEMFILE="$gemfile" bundle $@ ) 7 | done 8 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rake' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 300).include?('This file was generated by Bundler') 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("rake", "rake") 30 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | ENV["BUNDLE_GEMFILE"] = File.expand_path("../Gemfile", __dir__) 5 | 6 | require "bundler/setup" 7 | 8 | load Gem.bin_path("rubocop", "rubocop") 9 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 0.05% 6 | -------------------------------------------------------------------------------- /gemfiles/rails_70/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gemspec path: '../..' 5 | 6 | group :development do 7 | gem 'rails', '~> 7.0.0' 8 | 9 | gem 'mocha' 10 | gem 'minitest' 11 | gem 'minitest-reporters' 12 | gem 'rails-controller-testing' 13 | gem 'simplecov', require: false 14 | gem 'simplecov-cobertura' 15 | gem 'warning' 16 | 17 | gem "concurrent-ruby", "1.3.4" # Ref: rails/rails#54260 18 | 19 | # FIXME: relax this dependency when Ruby 3.1 support will be dropped 20 | gem "zeitwerk", "~> 2.6.18" 21 | end 22 | -------------------------------------------------------------------------------- /gemfiles/rails_70/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../.. 3 | specs: 4 | inherited_resources (2.1.0) 5 | actionpack (>= 7.0) 6 | has_scope (>= 0.6) 7 | railties (>= 7.0) 8 | responders (>= 2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actioncable (7.0.8.7) 14 | actionpack (= 7.0.8.7) 15 | activesupport (= 7.0.8.7) 16 | nio4r (~> 2.0) 17 | websocket-driver (>= 0.6.1) 18 | actionmailbox (7.0.8.7) 19 | actionpack (= 7.0.8.7) 20 | activejob (= 7.0.8.7) 21 | activerecord (= 7.0.8.7) 22 | activestorage (= 7.0.8.7) 23 | activesupport (= 7.0.8.7) 24 | mail (>= 2.7.1) 25 | net-imap 26 | net-pop 27 | net-smtp 28 | actionmailer (7.0.8.7) 29 | actionpack (= 7.0.8.7) 30 | actionview (= 7.0.8.7) 31 | activejob (= 7.0.8.7) 32 | activesupport (= 7.0.8.7) 33 | mail (~> 2.5, >= 2.5.4) 34 | net-imap 35 | net-pop 36 | net-smtp 37 | rails-dom-testing (~> 2.0) 38 | actionpack (7.0.8.7) 39 | actionview (= 7.0.8.7) 40 | activesupport (= 7.0.8.7) 41 | rack (~> 2.0, >= 2.2.4) 42 | rack-test (>= 0.6.3) 43 | rails-dom-testing (~> 2.0) 44 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 45 | actiontext (7.0.8.7) 46 | actionpack (= 7.0.8.7) 47 | activerecord (= 7.0.8.7) 48 | activestorage (= 7.0.8.7) 49 | activesupport (= 7.0.8.7) 50 | globalid (>= 0.6.0) 51 | nokogiri (>= 1.8.5) 52 | actionview (7.0.8.7) 53 | activesupport (= 7.0.8.7) 54 | builder (~> 3.1) 55 | erubi (~> 1.4) 56 | rails-dom-testing (~> 2.0) 57 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 58 | activejob (7.0.8.7) 59 | activesupport (= 7.0.8.7) 60 | globalid (>= 0.3.6) 61 | activemodel (7.0.8.7) 62 | activesupport (= 7.0.8.7) 63 | activerecord (7.0.8.7) 64 | activemodel (= 7.0.8.7) 65 | activesupport (= 7.0.8.7) 66 | activestorage (7.0.8.7) 67 | actionpack (= 7.0.8.7) 68 | activejob (= 7.0.8.7) 69 | activerecord (= 7.0.8.7) 70 | activesupport (= 7.0.8.7) 71 | marcel (~> 1.0) 72 | mini_mime (>= 1.1.0) 73 | activesupport (7.0.8.7) 74 | concurrent-ruby (~> 1.0, >= 1.0.2) 75 | i18n (>= 1.6, < 2) 76 | minitest (>= 5.1) 77 | tzinfo (~> 2.0) 78 | ansi (1.5.0) 79 | base64 (0.2.0) 80 | builder (3.3.0) 81 | concurrent-ruby (1.3.4) 82 | crass (1.0.6) 83 | date (3.4.1) 84 | docile (1.4.1) 85 | erubi (1.13.1) 86 | globalid (1.2.1) 87 | activesupport (>= 6.1) 88 | has_scope (0.8.2) 89 | actionpack (>= 5.2) 90 | activesupport (>= 5.2) 91 | i18n (1.14.7) 92 | concurrent-ruby (~> 1.0) 93 | loofah (2.24.0) 94 | crass (~> 1.0.2) 95 | nokogiri (>= 1.12.0) 96 | mail (2.8.1) 97 | mini_mime (>= 0.1.1) 98 | net-imap 99 | net-pop 100 | net-smtp 101 | marcel (1.0.4) 102 | method_source (1.1.0) 103 | mini_mime (1.1.5) 104 | mini_portile2 (2.8.8) 105 | minitest (5.25.5) 106 | minitest-reporters (1.7.1) 107 | ansi 108 | builder 109 | minitest (>= 5.0) 110 | ruby-progressbar 111 | mocha (2.7.1) 112 | ruby2_keywords (>= 0.0.5) 113 | net-imap (0.5.7) 114 | date 115 | net-protocol 116 | net-pop (0.1.2) 117 | net-protocol 118 | net-protocol (0.2.2) 119 | timeout 120 | net-smtp (0.5.1) 121 | net-protocol 122 | nio4r (2.7.4) 123 | nokogiri (1.18.8) 124 | mini_portile2 (~> 2.8.2) 125 | racc (~> 1.4) 126 | nokogiri (1.18.8-aarch64-linux-gnu) 127 | racc (~> 1.4) 128 | nokogiri (1.18.8-arm64-darwin) 129 | racc (~> 1.4) 130 | nokogiri (1.18.8-x86_64-darwin) 131 | racc (~> 1.4) 132 | nokogiri (1.18.8-x86_64-linux-gnu) 133 | racc (~> 1.4) 134 | racc (1.8.1) 135 | rack (2.2.13) 136 | rack-test (2.2.0) 137 | rack (>= 1.3) 138 | rails (7.0.8.7) 139 | actioncable (= 7.0.8.7) 140 | actionmailbox (= 7.0.8.7) 141 | actionmailer (= 7.0.8.7) 142 | actionpack (= 7.0.8.7) 143 | actiontext (= 7.0.8.7) 144 | actionview (= 7.0.8.7) 145 | activejob (= 7.0.8.7) 146 | activemodel (= 7.0.8.7) 147 | activerecord (= 7.0.8.7) 148 | activestorage (= 7.0.8.7) 149 | activesupport (= 7.0.8.7) 150 | bundler (>= 1.15.0) 151 | railties (= 7.0.8.7) 152 | rails-controller-testing (1.0.5) 153 | actionpack (>= 5.0.1.rc1) 154 | actionview (>= 5.0.1.rc1) 155 | activesupport (>= 5.0.1.rc1) 156 | rails-dom-testing (2.2.0) 157 | activesupport (>= 5.0.0) 158 | minitest 159 | nokogiri (>= 1.6) 160 | rails-html-sanitizer (1.6.2) 161 | loofah (~> 2.21) 162 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 163 | railties (7.0.8.7) 164 | actionpack (= 7.0.8.7) 165 | activesupport (= 7.0.8.7) 166 | method_source 167 | rake (>= 12.2) 168 | thor (~> 1.0) 169 | zeitwerk (~> 2.5) 170 | rake (13.2.1) 171 | responders (3.1.1) 172 | actionpack (>= 5.2) 173 | railties (>= 5.2) 174 | rexml (3.4.1) 175 | ruby-progressbar (1.13.0) 176 | ruby2_keywords (0.0.5) 177 | simplecov (0.22.0) 178 | docile (~> 1.1) 179 | simplecov-html (~> 0.11) 180 | simplecov_json_formatter (~> 0.1) 181 | simplecov-cobertura (2.1.0) 182 | rexml 183 | simplecov (~> 0.19) 184 | simplecov-html (0.13.1) 185 | simplecov_json_formatter (0.1.4) 186 | thor (1.3.2) 187 | timeout (0.4.3) 188 | tzinfo (2.0.6) 189 | concurrent-ruby (~> 1.0) 190 | warning (1.5.0) 191 | websocket-driver (0.7.7) 192 | base64 193 | websocket-extensions (>= 0.1.0) 194 | websocket-extensions (0.1.5) 195 | zeitwerk (2.6.18) 196 | 197 | PLATFORMS 198 | aarch64-linux 199 | arm64-darwin 200 | ruby 201 | x86_64-darwin 202 | x86_64-linux 203 | 204 | DEPENDENCIES 205 | concurrent-ruby (= 1.3.4) 206 | inherited_resources! 207 | minitest 208 | minitest-reporters 209 | mocha 210 | rails (~> 7.0.0) 211 | rails-controller-testing 212 | simplecov 213 | simplecov-cobertura 214 | warning 215 | zeitwerk (~> 2.6.18) 216 | 217 | BUNDLED WITH 218 | 2.6.8 219 | -------------------------------------------------------------------------------- /gemfiles/rails_71/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gemspec path: '../..' 5 | 6 | group :development do 7 | gem 'rails', '~> 7.1.0' 8 | 9 | gem 'mocha' 10 | gem 'minitest' 11 | gem 'minitest-reporters' 12 | gem 'rails-controller-testing' 13 | gem 'simplecov', require: false 14 | gem 'simplecov-cobertura' 15 | gem 'warning' 16 | 17 | # FIXME: relax this dependency when Ruby 3.1 support will be dropped 18 | gem "zeitwerk", "~> 2.6.18" 19 | end 20 | -------------------------------------------------------------------------------- /gemfiles/rails_71/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../.. 3 | specs: 4 | inherited_resources (2.1.0) 5 | actionpack (>= 7.0) 6 | has_scope (>= 0.6) 7 | railties (>= 7.0) 8 | responders (>= 2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actioncable (7.1.5.1) 14 | actionpack (= 7.1.5.1) 15 | activesupport (= 7.1.5.1) 16 | nio4r (~> 2.0) 17 | websocket-driver (>= 0.6.1) 18 | zeitwerk (~> 2.6) 19 | actionmailbox (7.1.5.1) 20 | actionpack (= 7.1.5.1) 21 | activejob (= 7.1.5.1) 22 | activerecord (= 7.1.5.1) 23 | activestorage (= 7.1.5.1) 24 | activesupport (= 7.1.5.1) 25 | mail (>= 2.7.1) 26 | net-imap 27 | net-pop 28 | net-smtp 29 | actionmailer (7.1.5.1) 30 | actionpack (= 7.1.5.1) 31 | actionview (= 7.1.5.1) 32 | activejob (= 7.1.5.1) 33 | activesupport (= 7.1.5.1) 34 | mail (~> 2.5, >= 2.5.4) 35 | net-imap 36 | net-pop 37 | net-smtp 38 | rails-dom-testing (~> 2.2) 39 | actionpack (7.1.5.1) 40 | actionview (= 7.1.5.1) 41 | activesupport (= 7.1.5.1) 42 | nokogiri (>= 1.8.5) 43 | racc 44 | rack (>= 2.2.4) 45 | rack-session (>= 1.0.1) 46 | rack-test (>= 0.6.3) 47 | rails-dom-testing (~> 2.2) 48 | rails-html-sanitizer (~> 1.6) 49 | actiontext (7.1.5.1) 50 | actionpack (= 7.1.5.1) 51 | activerecord (= 7.1.5.1) 52 | activestorage (= 7.1.5.1) 53 | activesupport (= 7.1.5.1) 54 | globalid (>= 0.6.0) 55 | nokogiri (>= 1.8.5) 56 | actionview (7.1.5.1) 57 | activesupport (= 7.1.5.1) 58 | builder (~> 3.1) 59 | erubi (~> 1.11) 60 | rails-dom-testing (~> 2.2) 61 | rails-html-sanitizer (~> 1.6) 62 | activejob (7.1.5.1) 63 | activesupport (= 7.1.5.1) 64 | globalid (>= 0.3.6) 65 | activemodel (7.1.5.1) 66 | activesupport (= 7.1.5.1) 67 | activerecord (7.1.5.1) 68 | activemodel (= 7.1.5.1) 69 | activesupport (= 7.1.5.1) 70 | timeout (>= 0.4.0) 71 | activestorage (7.1.5.1) 72 | actionpack (= 7.1.5.1) 73 | activejob (= 7.1.5.1) 74 | activerecord (= 7.1.5.1) 75 | activesupport (= 7.1.5.1) 76 | marcel (~> 1.0) 77 | activesupport (7.1.5.1) 78 | base64 79 | benchmark (>= 0.3) 80 | bigdecimal 81 | concurrent-ruby (~> 1.0, >= 1.0.2) 82 | connection_pool (>= 2.2.5) 83 | drb 84 | i18n (>= 1.6, < 2) 85 | logger (>= 1.4.2) 86 | minitest (>= 5.1) 87 | mutex_m 88 | securerandom (>= 0.3) 89 | tzinfo (~> 2.0) 90 | ansi (1.5.0) 91 | base64 (0.2.0) 92 | benchmark (0.4.0) 93 | bigdecimal (3.1.9) 94 | builder (3.3.0) 95 | concurrent-ruby (1.3.5) 96 | connection_pool (2.5.3) 97 | crass (1.0.6) 98 | date (3.4.1) 99 | docile (1.4.1) 100 | drb (2.2.1) 101 | erubi (1.13.1) 102 | globalid (1.2.1) 103 | activesupport (>= 6.1) 104 | has_scope (0.8.2) 105 | actionpack (>= 5.2) 106 | activesupport (>= 5.2) 107 | i18n (1.14.7) 108 | concurrent-ruby (~> 1.0) 109 | io-console (0.8.0) 110 | irb (1.15.2) 111 | pp (>= 0.6.0) 112 | rdoc (>= 4.0.0) 113 | reline (>= 0.4.2) 114 | logger (1.7.0) 115 | loofah (2.24.0) 116 | crass (~> 1.0.2) 117 | nokogiri (>= 1.12.0) 118 | mail (2.8.1) 119 | mini_mime (>= 0.1.1) 120 | net-imap 121 | net-pop 122 | net-smtp 123 | marcel (1.0.4) 124 | mini_mime (1.1.5) 125 | mini_portile2 (2.8.8) 126 | minitest (5.25.5) 127 | minitest-reporters (1.7.1) 128 | ansi 129 | builder 130 | minitest (>= 5.0) 131 | ruby-progressbar 132 | mocha (2.7.1) 133 | ruby2_keywords (>= 0.0.5) 134 | mutex_m (0.3.0) 135 | net-imap (0.5.7) 136 | date 137 | net-protocol 138 | net-pop (0.1.2) 139 | net-protocol 140 | net-protocol (0.2.2) 141 | timeout 142 | net-smtp (0.5.1) 143 | net-protocol 144 | nio4r (2.7.4) 145 | nokogiri (1.18.8) 146 | mini_portile2 (~> 2.8.2) 147 | racc (~> 1.4) 148 | nokogiri (1.18.8-aarch64-linux-gnu) 149 | racc (~> 1.4) 150 | nokogiri (1.18.8-arm64-darwin) 151 | racc (~> 1.4) 152 | nokogiri (1.18.8-x86_64-darwin) 153 | racc (~> 1.4) 154 | nokogiri (1.18.8-x86_64-linux-gnu) 155 | racc (~> 1.4) 156 | pp (0.6.2) 157 | prettyprint 158 | prettyprint (0.2.0) 159 | psych (5.2.3) 160 | date 161 | stringio 162 | racc (1.8.1) 163 | rack (3.1.13) 164 | rack-session (2.1.0) 165 | base64 (>= 0.1.0) 166 | rack (>= 3.0.0) 167 | rack-test (2.2.0) 168 | rack (>= 1.3) 169 | rackup (2.2.1) 170 | rack (>= 3) 171 | rails (7.1.5.1) 172 | actioncable (= 7.1.5.1) 173 | actionmailbox (= 7.1.5.1) 174 | actionmailer (= 7.1.5.1) 175 | actionpack (= 7.1.5.1) 176 | actiontext (= 7.1.5.1) 177 | actionview (= 7.1.5.1) 178 | activejob (= 7.1.5.1) 179 | activemodel (= 7.1.5.1) 180 | activerecord (= 7.1.5.1) 181 | activestorage (= 7.1.5.1) 182 | activesupport (= 7.1.5.1) 183 | bundler (>= 1.15.0) 184 | railties (= 7.1.5.1) 185 | rails-controller-testing (1.0.5) 186 | actionpack (>= 5.0.1.rc1) 187 | actionview (>= 5.0.1.rc1) 188 | activesupport (>= 5.0.1.rc1) 189 | rails-dom-testing (2.2.0) 190 | activesupport (>= 5.0.0) 191 | minitest 192 | nokogiri (>= 1.6) 193 | rails-html-sanitizer (1.6.2) 194 | loofah (~> 2.21) 195 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 196 | railties (7.1.5.1) 197 | actionpack (= 7.1.5.1) 198 | activesupport (= 7.1.5.1) 199 | irb 200 | rackup (>= 1.0.0) 201 | rake (>= 12.2) 202 | thor (~> 1.0, >= 1.2.2) 203 | zeitwerk (~> 2.6) 204 | rake (13.2.1) 205 | rdoc (6.13.1) 206 | psych (>= 4.0.0) 207 | reline (0.6.1) 208 | io-console (~> 0.5) 209 | responders (3.1.1) 210 | actionpack (>= 5.2) 211 | railties (>= 5.2) 212 | rexml (3.4.1) 213 | ruby-progressbar (1.13.0) 214 | ruby2_keywords (0.0.5) 215 | securerandom (0.4.1) 216 | simplecov (0.22.0) 217 | docile (~> 1.1) 218 | simplecov-html (~> 0.11) 219 | simplecov_json_formatter (~> 0.1) 220 | simplecov-cobertura (2.1.0) 221 | rexml 222 | simplecov (~> 0.19) 223 | simplecov-html (0.13.1) 224 | simplecov_json_formatter (0.1.4) 225 | stringio (3.1.7) 226 | thor (1.3.2) 227 | timeout (0.4.3) 228 | tzinfo (2.0.6) 229 | concurrent-ruby (~> 1.0) 230 | warning (1.5.0) 231 | websocket-driver (0.7.7) 232 | base64 233 | websocket-extensions (>= 0.1.0) 234 | websocket-extensions (0.1.5) 235 | zeitwerk (2.6.18) 236 | 237 | PLATFORMS 238 | aarch64-linux 239 | arm64-darwin 240 | ruby 241 | x86_64-darwin 242 | x86_64-linux 243 | 244 | DEPENDENCIES 245 | inherited_resources! 246 | minitest 247 | minitest-reporters 248 | mocha 249 | rails (~> 7.1.0) 250 | rails-controller-testing 251 | simplecov 252 | simplecov-cobertura 253 | warning 254 | zeitwerk (~> 2.6.18) 255 | 256 | BUNDLED WITH 257 | 2.6.8 258 | -------------------------------------------------------------------------------- /gemfiles/rails_72/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gemspec path: '../..' 5 | 6 | group :development do 7 | gem 'rails', '~> 7.2.0' 8 | 9 | gem 'mocha' 10 | gem 'minitest' 11 | gem 'minitest-reporters' 12 | gem 'rails-controller-testing' 13 | gem 'simplecov', require: false 14 | gem 'simplecov-cobertura' 15 | gem 'warning' 16 | 17 | # FIXME: relax this dependency when Ruby 3.1 support will be dropped 18 | gem "zeitwerk", "~> 2.6.18" 19 | end 20 | -------------------------------------------------------------------------------- /gemfiles/rails_72/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../.. 3 | specs: 4 | inherited_resources (2.1.0) 5 | actionpack (>= 7.0) 6 | has_scope (>= 0.6) 7 | railties (>= 7.0) 8 | responders (>= 2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actioncable (7.2.2.1) 14 | actionpack (= 7.2.2.1) 15 | activesupport (= 7.2.2.1) 16 | nio4r (~> 2.0) 17 | websocket-driver (>= 0.6.1) 18 | zeitwerk (~> 2.6) 19 | actionmailbox (7.2.2.1) 20 | actionpack (= 7.2.2.1) 21 | activejob (= 7.2.2.1) 22 | activerecord (= 7.2.2.1) 23 | activestorage (= 7.2.2.1) 24 | activesupport (= 7.2.2.1) 25 | mail (>= 2.8.0) 26 | actionmailer (7.2.2.1) 27 | actionpack (= 7.2.2.1) 28 | actionview (= 7.2.2.1) 29 | activejob (= 7.2.2.1) 30 | activesupport (= 7.2.2.1) 31 | mail (>= 2.8.0) 32 | rails-dom-testing (~> 2.2) 33 | actionpack (7.2.2.1) 34 | actionview (= 7.2.2.1) 35 | activesupport (= 7.2.2.1) 36 | nokogiri (>= 1.8.5) 37 | racc 38 | rack (>= 2.2.4, < 3.2) 39 | rack-session (>= 1.0.1) 40 | rack-test (>= 0.6.3) 41 | rails-dom-testing (~> 2.2) 42 | rails-html-sanitizer (~> 1.6) 43 | useragent (~> 0.16) 44 | actiontext (7.2.2.1) 45 | actionpack (= 7.2.2.1) 46 | activerecord (= 7.2.2.1) 47 | activestorage (= 7.2.2.1) 48 | activesupport (= 7.2.2.1) 49 | globalid (>= 0.6.0) 50 | nokogiri (>= 1.8.5) 51 | actionview (7.2.2.1) 52 | activesupport (= 7.2.2.1) 53 | builder (~> 3.1) 54 | erubi (~> 1.11) 55 | rails-dom-testing (~> 2.2) 56 | rails-html-sanitizer (~> 1.6) 57 | activejob (7.2.2.1) 58 | activesupport (= 7.2.2.1) 59 | globalid (>= 0.3.6) 60 | activemodel (7.2.2.1) 61 | activesupport (= 7.2.2.1) 62 | activerecord (7.2.2.1) 63 | activemodel (= 7.2.2.1) 64 | activesupport (= 7.2.2.1) 65 | timeout (>= 0.4.0) 66 | activestorage (7.2.2.1) 67 | actionpack (= 7.2.2.1) 68 | activejob (= 7.2.2.1) 69 | activerecord (= 7.2.2.1) 70 | activesupport (= 7.2.2.1) 71 | marcel (~> 1.0) 72 | activesupport (7.2.2.1) 73 | base64 74 | benchmark (>= 0.3) 75 | bigdecimal 76 | concurrent-ruby (~> 1.0, >= 1.3.1) 77 | connection_pool (>= 2.2.5) 78 | drb 79 | i18n (>= 1.6, < 2) 80 | logger (>= 1.4.2) 81 | minitest (>= 5.1) 82 | securerandom (>= 0.3) 83 | tzinfo (~> 2.0, >= 2.0.5) 84 | ansi (1.5.0) 85 | base64 (0.2.0) 86 | benchmark (0.4.0) 87 | bigdecimal (3.1.9) 88 | builder (3.3.0) 89 | concurrent-ruby (1.3.5) 90 | connection_pool (2.5.3) 91 | crass (1.0.6) 92 | date (3.4.1) 93 | docile (1.4.1) 94 | drb (2.2.1) 95 | erubi (1.13.1) 96 | globalid (1.2.1) 97 | activesupport (>= 6.1) 98 | has_scope (0.8.2) 99 | actionpack (>= 5.2) 100 | activesupport (>= 5.2) 101 | i18n (1.14.7) 102 | concurrent-ruby (~> 1.0) 103 | io-console (0.8.0) 104 | irb (1.15.2) 105 | pp (>= 0.6.0) 106 | rdoc (>= 4.0.0) 107 | reline (>= 0.4.2) 108 | logger (1.7.0) 109 | loofah (2.24.0) 110 | crass (~> 1.0.2) 111 | nokogiri (>= 1.12.0) 112 | mail (2.8.1) 113 | mini_mime (>= 0.1.1) 114 | net-imap 115 | net-pop 116 | net-smtp 117 | marcel (1.0.4) 118 | mini_mime (1.1.5) 119 | mini_portile2 (2.8.8) 120 | minitest (5.25.5) 121 | minitest-reporters (1.7.1) 122 | ansi 123 | builder 124 | minitest (>= 5.0) 125 | ruby-progressbar 126 | mocha (2.7.1) 127 | ruby2_keywords (>= 0.0.5) 128 | net-imap (0.5.7) 129 | date 130 | net-protocol 131 | net-pop (0.1.2) 132 | net-protocol 133 | net-protocol (0.2.2) 134 | timeout 135 | net-smtp (0.5.1) 136 | net-protocol 137 | nio4r (2.7.4) 138 | nokogiri (1.18.8) 139 | mini_portile2 (~> 2.8.2) 140 | racc (~> 1.4) 141 | nokogiri (1.18.8-aarch64-linux-gnu) 142 | racc (~> 1.4) 143 | nokogiri (1.18.8-arm64-darwin) 144 | racc (~> 1.4) 145 | nokogiri (1.18.8-x86_64-darwin) 146 | racc (~> 1.4) 147 | nokogiri (1.18.8-x86_64-linux-gnu) 148 | racc (~> 1.4) 149 | pp (0.6.2) 150 | prettyprint 151 | prettyprint (0.2.0) 152 | psych (5.2.3) 153 | date 154 | stringio 155 | racc (1.8.1) 156 | rack (3.1.13) 157 | rack-session (2.1.0) 158 | base64 (>= 0.1.0) 159 | rack (>= 3.0.0) 160 | rack-test (2.2.0) 161 | rack (>= 1.3) 162 | rackup (2.2.1) 163 | rack (>= 3) 164 | rails (7.2.2.1) 165 | actioncable (= 7.2.2.1) 166 | actionmailbox (= 7.2.2.1) 167 | actionmailer (= 7.2.2.1) 168 | actionpack (= 7.2.2.1) 169 | actiontext (= 7.2.2.1) 170 | actionview (= 7.2.2.1) 171 | activejob (= 7.2.2.1) 172 | activemodel (= 7.2.2.1) 173 | activerecord (= 7.2.2.1) 174 | activestorage (= 7.2.2.1) 175 | activesupport (= 7.2.2.1) 176 | bundler (>= 1.15.0) 177 | railties (= 7.2.2.1) 178 | rails-controller-testing (1.0.5) 179 | actionpack (>= 5.0.1.rc1) 180 | actionview (>= 5.0.1.rc1) 181 | activesupport (>= 5.0.1.rc1) 182 | rails-dom-testing (2.2.0) 183 | activesupport (>= 5.0.0) 184 | minitest 185 | nokogiri (>= 1.6) 186 | rails-html-sanitizer (1.6.2) 187 | loofah (~> 2.21) 188 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 189 | railties (7.2.2.1) 190 | actionpack (= 7.2.2.1) 191 | activesupport (= 7.2.2.1) 192 | irb (~> 1.13) 193 | rackup (>= 1.0.0) 194 | rake (>= 12.2) 195 | thor (~> 1.0, >= 1.2.2) 196 | zeitwerk (~> 2.6) 197 | rake (13.2.1) 198 | rdoc (6.13.1) 199 | psych (>= 4.0.0) 200 | reline (0.6.1) 201 | io-console (~> 0.5) 202 | responders (3.1.1) 203 | actionpack (>= 5.2) 204 | railties (>= 5.2) 205 | rexml (3.4.1) 206 | ruby-progressbar (1.13.0) 207 | ruby2_keywords (0.0.5) 208 | securerandom (0.4.1) 209 | simplecov (0.22.0) 210 | docile (~> 1.1) 211 | simplecov-html (~> 0.11) 212 | simplecov_json_formatter (~> 0.1) 213 | simplecov-cobertura (2.1.0) 214 | rexml 215 | simplecov (~> 0.19) 216 | simplecov-html (0.13.1) 217 | simplecov_json_formatter (0.1.4) 218 | stringio (3.1.7) 219 | thor (1.3.2) 220 | timeout (0.4.3) 221 | tzinfo (2.0.6) 222 | concurrent-ruby (~> 1.0) 223 | useragent (0.16.11) 224 | warning (1.5.0) 225 | websocket-driver (0.7.7) 226 | base64 227 | websocket-extensions (>= 0.1.0) 228 | websocket-extensions (0.1.5) 229 | zeitwerk (2.6.18) 230 | 231 | PLATFORMS 232 | aarch64-linux 233 | arm64-darwin 234 | ruby 235 | x86_64-darwin 236 | x86_64-linux 237 | 238 | DEPENDENCIES 239 | inherited_resources! 240 | minitest 241 | minitest-reporters 242 | mocha 243 | rails (~> 7.2.0) 244 | rails-controller-testing 245 | simplecov 246 | simplecov-cobertura 247 | warning 248 | zeitwerk (~> 2.6.18) 249 | 250 | BUNDLED WITH 251 | 2.6.8 252 | -------------------------------------------------------------------------------- /inherited_resources.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | lib = File.expand_path("lib", __dir__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "inherited_resources/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "inherited_resources" 8 | s.version = InheritedResources::VERSION.dup 9 | s.platform = Gem::Platform::RUBY 10 | s.summary = "Inherited Resources speeds up development by making your controllers inherit all restful actions so you just have to focus on what is important." 11 | s.homepage = "https://github.com/activeadmin/inherited_resources" 12 | s.description = <<~MSG 13 | Inherited Resources speeds up development by making your controllers inherit all restful actions so you just have to focus on what is important. 14 | It makes your controllers more powerful and cleaner at the same time. 15 | MSG 16 | 17 | s.authors = ['José Valim', 'Rafael Mendonça França'] 18 | s.license = "MIT" 19 | 20 | s.files = Dir["app/**/*", "lib/**/*", "README.md", "MIT-LICENSE"] 21 | s.require_paths = ["lib"] 22 | 23 | s.metadata = { "rubygems_mfa_required" => "true" } 24 | 25 | s.required_ruby_version = '>= 3.1' 26 | 27 | s.add_dependency("responders", ">= 2") 28 | s.add_dependency("actionpack", ">= 7.0") 29 | s.add_dependency("railties", ">= 7.0") 30 | s.add_dependency("has_scope", ">= 0.6") 31 | end 32 | -------------------------------------------------------------------------------- /lib/generators/rails/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Stubs out a scaffolded controller and its views using InheritedResources. 3 | Pass the model name, either CamelCased or under_scored. The controller 4 | name is retrieved as a pluralized version of the model name. 5 | 6 | To create a controller within a module, specify the model name as a 7 | path like 'parent_module/controller_name'. 8 | 9 | This generates a controller class in app/controllers and invokes helper, 10 | template engine and test framework generators. 11 | -------------------------------------------------------------------------------- /lib/generators/rails/inherited_resources_controller_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator' 4 | 5 | module Rails 6 | module Generators 7 | class InheritedResourcesControllerGenerator < ScaffoldControllerGenerator 8 | def self.source_root 9 | @source_root ||= File.expand_path("templates", File.dirname(__FILE__)) 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/rails/templates/controller.rb.tt: -------------------------------------------------------------------------------- 1 | class <%= controller_class_name %>Controller < InheritedResources::Base 2 | <% if options[:singleton] -%> 3 | defaults :singleton => true 4 | <% end -%> 5 | 6 | private 7 | 8 | def <%= singular_name %>_params 9 | params.require(:<%= singular_name %>).permit(<%= attributes_names.map{ |a_name| ":#{a_name}" }.join(", ") %>) 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /lib/inherited_resources.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This is here because responders don't require it. 4 | require 'rails/engine' 5 | require 'responders' 6 | 7 | require_relative 'inherited_resources/engine' 8 | require_relative 'inherited_resources/blank_slate' 9 | require_relative 'inherited_resources/responder' 10 | 11 | module InheritedResources 12 | ACTIONS = [ :index, :show, :new, :edit, :create, :update, :destroy ] unless self.const_defined?(:ACTIONS) 13 | 14 | autoload :Actions, 'inherited_resources/actions' 15 | autoload :BaseHelpers, 'inherited_resources/base_helpers' 16 | autoload :ShallowHelpers, 'inherited_resources/shallow_helpers' 17 | autoload :BelongsToHelpers, 'inherited_resources/belongs_to_helpers' 18 | autoload :ClassMethods, 'inherited_resources/class_methods' 19 | autoload :DSL, 'inherited_resources/dsl' 20 | autoload :PolymorphicHelpers, 'inherited_resources/polymorphic_helpers' 21 | autoload :SingletonHelpers, 'inherited_resources/singleton_helpers' 22 | autoload :UrlHelpers, 'inherited_resources/url_helpers' 23 | autoload :VERSION, 'inherited_resources/version' 24 | 25 | # Change the flash keys used by FlashResponder. 26 | def self.flash_keys=(array) 27 | Responders::FlashResponder.flash_keys = array 28 | end 29 | 30 | # Inherit from a different controller. This only has an effect if changed 31 | # before InheritedResources::Base is loaded, e.g. in a rails initializer. 32 | mattr_accessor(:parent_controller) { '::ApplicationController' } 33 | end 34 | 35 | ActiveSupport.on_load(:action_controller_base) do 36 | # If you cannot inherit from InheritedResources::Base you can call 37 | # inherit_resources in your controller to have all the required modules and 38 | # functionality included. 39 | def self.inherit_resources 40 | InheritedResources::Base.inherit_resources(self) 41 | initialize_resources_class_accessors! 42 | create_resources_url_helpers! 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/inherited_resources/actions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | # Holds all default actions for InheritedResources. 5 | module Actions 6 | 7 | # GET /resources 8 | def index(options={}, &block) 9 | respond_with(*with_chain(collection), options, &block) 10 | end 11 | alias :index! :index 12 | 13 | # GET /resources/1 14 | def show(options={}, &block) 15 | respond_with(*with_chain(resource), options, &block) 16 | end 17 | alias :show! :show 18 | 19 | # GET /resources/new 20 | def new(options={}, &block) 21 | respond_with(*with_chain(build_resource), options, &block) 22 | end 23 | alias :new! :new 24 | 25 | # GET /resources/1/edit 26 | def edit(options={}, &block) 27 | respond_with(*with_chain(resource), options, &block) 28 | end 29 | alias :edit! :edit 30 | 31 | # POST /resources 32 | def create(options={}, &block) 33 | object = build_resource 34 | 35 | if create_resource(object) 36 | options[:location] ||= smart_resource_url 37 | end 38 | 39 | respond_with_dual_blocks(object, options, &block) 40 | end 41 | alias :create! :create 42 | 43 | # PUT /resources/1 44 | def update(options={}, &block) 45 | object = resource 46 | 47 | if update_resource(object, resource_params) 48 | options[:location] ||= smart_resource_url 49 | end 50 | 51 | respond_with_dual_blocks(object, options, &block) 52 | end 53 | alias :update! :update 54 | 55 | # DELETE /resources/1 56 | def destroy(options={}, &block) 57 | object = resource 58 | options[:location] ||= smart_collection_url 59 | 60 | destroy_resource(object) 61 | respond_with_dual_blocks(object, options, &block) 62 | end 63 | alias :destroy! :destroy 64 | 65 | # Make aliases protected 66 | protected :index!, :show!, :new!, :create!, :edit!, :update!, :destroy! 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/inherited_resources/belongs_to_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | 5 | # = belongs_to 6 | # 7 | # Let's suppose that we have some tasks that belongs to projects. To specify 8 | # this association in your controllers, just do: 9 | # 10 | # class TasksController < InheritedResources::Base 11 | # belongs_to :project 12 | # end 13 | # 14 | # belongs_to accepts several options to be able to configure the association. 15 | # For example, if you want urls like /projects/:project_title/tasks, you 16 | # can customize how InheritedResources find your projects: 17 | # 18 | # class TasksController < InheritedResources::Base 19 | # belongs_to :project, :finder => :find_by_title!, :param => :project_title 20 | # end 21 | # 22 | # It also accepts :route_name, :parent_class and :instance_name as options. 23 | # Check the lib/inherited_resources/class_methods.rb for more. 24 | # 25 | # = nested_belongs_to 26 | # 27 | # Now, our Tasks get some Comments and you need to nest even deeper. Good 28 | # practices says that you should never nest more than two resources, but sometimes 29 | # you have to for security reasons. So this is an example of how you can do it: 30 | # 31 | # class CommentsController < InheritedResources::Base 32 | # nested_belongs_to :project, :task 33 | # end 34 | # 35 | # If you need to configure any of these belongs to, you can nested them using blocks: 36 | # 37 | # class CommentsController < InheritedResources::Base 38 | # belongs_to :project, :finder => :find_by_title!, :param => :project_title do 39 | # belongs_to :task 40 | # end 41 | # end 42 | # 43 | # Warning: calling several belongs_to is the same as nesting them: 44 | # 45 | # class CommentsController < InheritedResources::Base 46 | # belongs_to :project 47 | # belongs_to :task 48 | # end 49 | # 50 | # In other words, the code above is the same as calling nested_belongs_to. 51 | # 52 | module BelongsToHelpers 53 | 54 | protected 55 | 56 | # Parent is always true when belongs_to is called. 57 | # 58 | def parent? 59 | true 60 | end 61 | 62 | def parent 63 | @parent ||= association_chain[-1] 64 | end 65 | 66 | def parent_type 67 | parent.class.name.underscore.to_sym 68 | end 69 | 70 | private 71 | 72 | # Evaluate the parent given. This is used to nest parents in the 73 | # association chain. 74 | # 75 | def evaluate_parent(parent_symbol, parent_config, chain = nil) #:nodoc: 76 | get_parent_ivar(parent_config[:instance_name]) || 77 | set_parent_instance(parent_config, chain) 78 | end 79 | 80 | def get_parent_ivar(instance_name) #:nodoc: 81 | instance_variable_defined?(:"@#{instance_name}") && 82 | instance_variable_get(:"@#{instance_name}") 83 | end 84 | 85 | def set_parent_instance(parent_config, chain) #:nodoc: 86 | if parent_config[:singleton] 87 | parent = if chain 88 | chain.send(parent_config[:instance_name]) 89 | else 90 | nil 91 | end 92 | else 93 | parent = if chain 94 | chain.send(parent_config[:collection_name]) 95 | else 96 | parent_config[:parent_class] 97 | end 98 | 99 | parent = parent.send(parent_config[:finder], params[parent_config[:param]]) 100 | end 101 | 102 | instance_variable_set(:"@#{parent_config[:instance_name]}", parent) 103 | end 104 | 105 | # Maps parents_symbols to build association chain. In this case, it 106 | # simply return the parent_symbols, however on polymorphic belongs to, 107 | # it has some customization. 108 | # 109 | def symbols_for_association_chain #:nodoc: 110 | parents_symbols 111 | end 112 | 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /lib/inherited_resources/blank_slate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | # An object from BlankSlate simply discards all messages sent to it. 5 | class BlankSlate 6 | instance_methods.each do |m| 7 | undef_method m unless /^(__|object_id)/.match?(m) 8 | end 9 | 10 | def method_missing(*args) 11 | nil 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/inherited_resources/dsl.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | # Allows controllers to write actions using a class method DSL. 5 | # 6 | # class MyController < InheritedResources::Base 7 | # create! do |success, failure| 8 | # success.html { render :text => "It works!" } 9 | # end 10 | # end 11 | # 12 | module DSL 13 | def self.included(base) 14 | ACTIONS.each do |action| 15 | base.class_eval <<-WRITTER 16 | def self.#{action}!(options={}, &block) 17 | define_method :__#{action}, &block 18 | class_eval <<-ACTION 19 | def #{action} 20 | super(\#{options.inspect}, &method(:__#{action})) 21 | end 22 | ACTION 23 | end 24 | WRITTER 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/inherited_resources/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | class Railtie < ::Rails::Engine 5 | config.inherited_resources = InheritedResources 6 | 7 | if config.respond_to?(:app_generators) 8 | config.app_generators.scaffold_controller = :inherited_resources_controller 9 | else 10 | config.generators.scaffold_controller = :inherited_resources_controller 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/inherited_resources/polymorphic_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | 5 | # = polymorphic associations 6 | # 7 | # In some cases you have a resource that belongs to two different resources 8 | # but not at the same time. For example, let's suppose you have File, Message 9 | # and Task as resources and they are all commentable. 10 | # 11 | # Polymorphic associations allows you to create just one controller that will 12 | # deal with each case. 13 | # 14 | # class Comment < InheritedResources::Base 15 | # belongs_to :file, :message, :task, :polymorphic => true 16 | # end 17 | # 18 | # Your routes should be something like: 19 | # 20 | # resources :files do 21 | # resources :comments #=> /files/13/comments 22 | # end 23 | # resources :tasks do 24 | # resources :comments #=> /tasks/17/comments 25 | # end 26 | # resources :messages do 27 | # resources :comments #=> /messages/11/comments 28 | # end 29 | # 30 | # When using polymorphic associations, you get some free helpers: 31 | # 32 | # parent? #=> true 33 | # parent_type #=> :task 34 | # parent_class #=> Task 35 | # parent #=> @task 36 | # 37 | # This polymorphic controllers thing is a great idea by James Golick and he 38 | # built it in resource_controller. Here is just a re-implementation. 39 | # 40 | # = optional polymorphic associations 41 | # 42 | # Let's take another break from ProjectsController. Let's suppose we are 43 | # building a store, which sell products. 44 | # 45 | # On the website, we can show all products, but also products scoped to 46 | # categories, brands, users. In this case case, the association is optional, and 47 | # we deal with it in the following way: 48 | # 49 | # class ProductsController < InheritedResources::Base 50 | # belongs_to :category, :brand, :user, :polymorphic => true, :optional => true 51 | # end 52 | # 53 | # This will handle all those urls properly: 54 | # 55 | # /products/1 56 | # /categories/2/products/5 57 | # /brands/10/products/3 58 | # /user/13/products/11 59 | # 60 | # = nested polymorphic associations 61 | # 62 | # You can have polymorphic associations with nested resources. Let's suppose 63 | # that our File, Task and Message resources in the previous example belongs to 64 | # a project. 65 | # 66 | # This way we can have: 67 | # 68 | # class CommentsController < InheritedResources::Base 69 | # belongs_to :project { 70 | # belongs_to :file, :message, :task, :polymorphic => true 71 | # } 72 | # end 73 | # 74 | # Or: 75 | # 76 | # class CommentsController < InheritedResources::Base 77 | # nested_belongs_to :project 78 | # nested_belongs_to :file, :message, :task, :polymorphic => true 79 | # end 80 | # 81 | # Choose the syntax that makes more sense to you. :) 82 | # 83 | # Finally your routes should be something like: 84 | # 85 | # resources :projects do 86 | # resources :files do 87 | # resources :comments #=> /projects/1/files/13/comments 88 | # end 89 | # resources :tasks do 90 | # resources :comments #=> /projects/1/tasks/17/comments 91 | # end 92 | # resources :messages do 93 | # resources :comments #=> /projects/1/messages/11/comments 94 | # end 95 | # end 96 | # 97 | # The helpers work in the same way as above. 98 | # 99 | module PolymorphicHelpers 100 | 101 | protected 102 | 103 | # Returns the parent type. A Comments class can have :task, :file, :note 104 | # as parent types. 105 | # 106 | def parent_type 107 | unless instance_variable_defined?(:@parent_type) 108 | symbols_for_association_chain 109 | end 110 | 111 | if instance_variable_defined?(:@parent_type) 112 | @parent_type 113 | end 114 | end 115 | 116 | def parent_class 117 | parent.class if parent_type 118 | end 119 | 120 | # Returns the parent object. They are also available with the instance 121 | # variable name: @task, @file, @note... 122 | # 123 | def parent 124 | if parent_type 125 | p = instance_variable_defined?(:"@#{parent_type}") && instance_variable_get(:"@#{parent_type}") 126 | p || instance_variable_set(:"@#{parent_type}", association_chain[-1]) 127 | end 128 | end 129 | 130 | # If the polymorphic association is optional, we might not have a parent. 131 | # 132 | def parent? 133 | if resources_configuration[:polymorphic][:optional] 134 | parents_symbols.size > 1 || !parent_type.nil? 135 | else 136 | true 137 | end 138 | end 139 | 140 | private 141 | 142 | # Maps parents_symbols to build association chain. 143 | # 144 | # If the parents_symbols find :polymorphic, it goes through the 145 | # params keys to see which polymorphic parent matches the given params. 146 | # 147 | # When optional is given, it does not raise errors if the polymorphic 148 | # params are missing. 149 | # 150 | def symbols_for_association_chain #:nodoc: 151 | polymorphic_config = resources_configuration[:polymorphic] 152 | parents_symbols.map do |symbol| 153 | if symbol == :polymorphic 154 | params_keys = params.keys 155 | 156 | keys = polymorphic_config[:symbols].select do |poly| 157 | params_keys.include?(resources_configuration[poly][:param].to_s) 158 | end 159 | 160 | if keys.empty? 161 | raise ScriptError, "Could not find param for polymorphic association. The request " << 162 | "parameters are #{params.keys.inspect} and the polymorphic " << 163 | "associations are #{polymorphic_config[:symbols].inspect}." unless polymorphic_config[:optional] 164 | 165 | nil 166 | else 167 | @parent_type = keys[-1].to_sym 168 | @parent_types = keys.map(&:to_sym) 169 | end 170 | else 171 | symbol 172 | end 173 | end.flatten.compact 174 | end 175 | 176 | end 177 | end 178 | -------------------------------------------------------------------------------- /lib/inherited_resources/responder.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | class Responder < ActionController::Responder 5 | include Responders::FlashResponder 6 | 7 | self.error_status = :unprocessable_entity 8 | self.redirect_status = :see_other 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/inherited_resources/shallow_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | # Shallow provides a functionality that goes on pair with Rails' shallow. 5 | # It is very similar to "optional" but it actually finds all the parents 6 | # resources instead of leaving them blank. Consider the following example: 7 | # 8 | # belongs_to :post, :shallow => true do 9 | # belongs_to :comment 10 | # end 11 | # 12 | # When accessed as /comments/1, Inherited Resources will automatically get 13 | # the post resource so both objects are actually accessible through the views. 14 | # 15 | # However, when using optional, Inherited Resources wouldn't actually bother 16 | # with finding the parent object. 17 | module ShallowHelpers 18 | private 19 | 20 | def symbols_for_association_chain #:nodoc: 21 | parent_symbols = parents_symbols.dup 22 | instance = nil 23 | 24 | if id = params[:id] 25 | finder_method = resources_configuration[:self][:finder] || :find 26 | instance = self.resource_class.send(finder_method, id) 27 | elsif parents_symbols.size > 1 28 | config = resources_configuration[parent_symbols.pop] 29 | finder_method = config[:finder] || :find 30 | instance = config[:parent_class].send(finder_method, params[config[:param]]) 31 | end 32 | 33 | load_parents(instance, parent_symbols) if instance 34 | parents_symbols 35 | end 36 | 37 | def load_parents(instance, parent_symbols) 38 | parent_symbols.reverse_each do |parent| 39 | instance = instance.send(parent) 40 | config = resources_configuration[parent] 41 | params[config[:param]] = instance.to_param 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/inherited_resources/singleton_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | 5 | # = singleton 6 | # 7 | # Singletons are usually used in associations which are related through has_one 8 | # and belongs_to. You declare those associations like this: 9 | # 10 | # class ManagersController < InheritedResources::Base 11 | # belongs_to :project, :singleton => true 12 | # end 13 | # 14 | # But in some cases, like an AccountsController, you have a singleton object 15 | # that is not necessarily associated with another: 16 | # 17 | # class AccountsController < InheritedResources::Base 18 | # defaults :singleton => true 19 | # end 20 | # 21 | # Besides that, you should overwrite the methods :resource and :build_resource 22 | # to make it work properly: 23 | # 24 | # class AccountsController < InheritedResources::Base 25 | # defaults :singleton => true 26 | # 27 | # protected 28 | # def resource 29 | # @current_user.account 30 | # end 31 | # 32 | # def build_resource(attributes = {}) 33 | # Account.new(attributes) 34 | # end 35 | # end 36 | # 37 | # When you have a singleton controller, the action index is removed. 38 | # 39 | module SingletonHelpers 40 | 41 | protected 42 | 43 | # Singleton methods does not deal with collections. 44 | # 45 | def collection; end 46 | 47 | # Overwrites how singleton deals with resource. 48 | # 49 | # If you are going to overwrite it, you should notice that the 50 | # end_of_association_chain here is not the same as in default belongs_to. 51 | # 52 | # class TasksController < InheritedResources::Base 53 | # belongs_to :project 54 | # end 55 | # 56 | # In this case, the association chain would be: 57 | # 58 | # Project.find(params[:project_id]).tasks 59 | # 60 | # So you would just have to call find(:all) at the end of association 61 | # chain. And this is what happened. 62 | # 63 | # In singleton controllers: 64 | # 65 | # class ManagersController < InheritedResources::Base 66 | # belongs_to :project, :singleton => true 67 | # end 68 | # 69 | # The association chain will be: 70 | # 71 | # Project.find(params[:project_id]) 72 | # 73 | # So we have to call manager on it, not find. 74 | # 75 | def resource 76 | get_resource_ivar || set_resource_ivar(end_of_association_chain.send(resource_instance_name)) 77 | end 78 | 79 | private 80 | 81 | # Returns the appropriated method to build the resource. 82 | # 83 | def method_for_association_build #:nodoc: 84 | :"build_#{resource_instance_name}" 85 | end 86 | 87 | # Sets the method_for_association_chain to nil. See resource 88 | # above for more information. 89 | # 90 | def method_for_association_chain #:nodoc: 91 | nil 92 | end 93 | 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/inherited_resources/url_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InheritedResources 4 | # = URLHelpers 5 | # 6 | # When you use InheritedResources it creates some UrlHelpers for you. 7 | # And they handle everything for you. 8 | # 9 | # # /posts/1/comments 10 | # resource_url # => /posts/1/comments/#{@comment.to_param} 11 | # resource_url(comment) # => /posts/1/comments/#{comment.to_param} 12 | # new_resource_url # => /posts/1/comments/new 13 | # edit_resource_url # => /posts/1/comments/#{@comment.to_param}/edit 14 | # collection_url # => /posts/1/comments 15 | # parent_url # => /posts/1 16 | # 17 | # # /projects/1/tasks 18 | # resource_url # => /projects/1/tasks/#{@task.to_param} 19 | # resource_url(task) # => /projects/1/tasks/#{task.to_param} 20 | # new_resource_url # => /projects/1/tasks/new 21 | # edit_resource_url # => /projects/1/tasks/#{@task.to_param}/edit 22 | # collection_url # => /projects/1/tasks 23 | # parent_url # => /projects/1 24 | # 25 | # # /users 26 | # resource_url # => /users/#{@user.to_param} 27 | # resource_url(user) # => /users/#{user.to_param} 28 | # new_resource_url # => /users/new 29 | # edit_resource_url # => /users/#{@user.to_param}/edit 30 | # collection_url # => /users 31 | # parent_url # => / 32 | # 33 | # The nice thing is that those urls are not guessed during runtime. They are 34 | # all created when you inherit. 35 | # 36 | module UrlHelpers 37 | protected 38 | 39 | # This method hard code url helpers in the class. 40 | # 41 | # We are doing this because is cheaper than guessing them when our action 42 | # is being processed (and even more cheaper when we are using nested 43 | # resources). 44 | # 45 | # When we are using polymorphic associations, those helpers rely on 46 | # polymorphic_url Rails helper. 47 | # 48 | def create_resources_url_helpers! 49 | resource_segments, resource_ivars = [], [] 50 | resource_config = self.resources_configuration[:self] 51 | 52 | singleton = resource_config[:singleton] 53 | uncountable = !singleton && resource_config[:route_collection_name] == resource_config[:route_instance_name] 54 | polymorphic = self.parents_symbols.include?(:polymorphic) 55 | 56 | # Add route_prefix if any. 57 | unless resource_config[:route_prefix].blank? 58 | if polymorphic 59 | resource_ivars << resource_config[:route_prefix] 60 | else 61 | resource_segments << resource_config[:route_prefix] 62 | end 63 | end 64 | 65 | # Deal with belongs_to associations and polymorphic associations. 66 | # Remember that we don't have to build the segments in polymorphic cases, 67 | # because the url will be polymorphic_url. 68 | # 69 | self.parents_symbols.each do |symbol| 70 | if symbol == :polymorphic 71 | resource_ivars << :parent 72 | else 73 | config = self.resources_configuration[symbol] 74 | if config[:singleton] && polymorphic 75 | resource_ivars << config[:instance_name] 76 | else 77 | resource_segments << config[:route_name] 78 | end 79 | if !config[:singleton] 80 | resource_ivars << :"@#{config[:instance_name]}" 81 | end 82 | end 83 | end 84 | 85 | collection_ivars = resource_ivars.dup 86 | collection_segments = resource_segments.dup 87 | 88 | # Generate parent url before we add resource instances. 89 | unless parents_symbols.empty? 90 | generate_url_and_path_helpers nil, :parent, resource_segments, resource_ivars 91 | generate_url_and_path_helpers :edit, :parent, resource_segments, resource_ivars 92 | end 93 | 94 | # In singleton cases, we do not send the current element instance variable 95 | # because the id is not in the URL. For example, we should call: 96 | # 97 | # project_manager_url(@project) 98 | # 99 | # Instead of: 100 | # 101 | # project_manager_url(@project, @manager) 102 | # 103 | # Another exception in singleton cases is that collection url does not 104 | # exist. In such cases, we create the parent collection url. So in the 105 | # manager case above, the collection url will be: 106 | # 107 | # project_url(@project) 108 | # 109 | # If the singleton does not have a parent, it will default to root_url. 110 | # 111 | collection_segments << resource_config[:route_collection_name] unless singleton 112 | resource_segments << resource_config[:route_instance_name] 113 | resource_ivars << :"@#{resource_config[:instance_name]}" unless singleton 114 | 115 | # Finally, polymorphic cases we have to give hints to the polymorphic url 116 | # builder. This works by attaching new ivars as symbols or records. 117 | # 118 | if polymorphic && singleton 119 | resource_ivars << resource_config[:instance_name] 120 | new_ivars = resource_ivars 121 | end 122 | 123 | # If route is uncountable then add "_index" suffix to collection index route name 124 | if uncountable 125 | collection_segments << :"#{collection_segments.pop}_index" 126 | end 127 | 128 | generate_url_and_path_helpers nil, :collection, collection_segments, collection_ivars 129 | generate_url_and_path_helpers :new, :resource, resource_segments, new_ivars || collection_ivars 130 | generate_url_and_path_helpers nil, :resource, resource_segments, resource_ivars 131 | generate_url_and_path_helpers :edit, :resource, resource_segments, resource_ivars 132 | 133 | if resource_config[:custom_actions] 134 | [*resource_config[:custom_actions][:resource]].each do | method | 135 | generate_url_and_path_helpers method, :resource, resource_segments, resource_ivars 136 | end 137 | [*resource_config[:custom_actions][:collection]].each do | method | 138 | generate_url_and_path_helpers method, :resources, collection_segments, collection_ivars 139 | end 140 | end 141 | end 142 | 143 | def handle_shallow_resource(prefix, name, segments, ivars) #:nodoc: 144 | return segments, ivars unless self.resources_configuration[:self][:shallow] 145 | case name 146 | when :collection, :resources 147 | segments = segments[-2..-1] 148 | ivars = [ivars.last] 149 | when :resource 150 | if prefix == :new 151 | segments = segments[-2..-1] 152 | ivars = [ivars.last] 153 | else 154 | segments = [segments.last] 155 | ivars = [ivars.last] 156 | end 157 | when :parent 158 | segments = [segments.last] 159 | ivars = [ivars.last] 160 | end 161 | 162 | segments ||= [] 163 | 164 | unless self.resources_configuration[:self][:route_prefix].blank? 165 | segments.unshift self.resources_configuration[:self][:route_prefix] 166 | end 167 | 168 | return segments, ivars 169 | end 170 | 171 | def generate_url_and_path_helpers(prefix, name, resource_segments, resource_ivars) #:nodoc: 172 | resource_segments, resource_ivars = handle_shallow_resource(prefix, name, resource_segments, resource_ivars) 173 | 174 | ivars = resource_ivars.dup 175 | singleton = self.resources_configuration[:self][:singleton] 176 | polymorphic = self.parents_symbols.include?(:polymorphic) 177 | 178 | # In collection in polymorphic cases, allow an argument to be given as a 179 | # replacemente for the parent. 180 | # 181 | parent_index = ivars.index(:parent) if polymorphic 182 | 183 | segments = if polymorphic 184 | :polymorphic 185 | elsif resource_segments.empty? 186 | 'root' 187 | else 188 | resource_segments.join('_') 189 | end 190 | 191 | define_params_helper(prefix, name, singleton, polymorphic, parent_index, ivars) 192 | define_helper_method(prefix, name, :path, segments) 193 | define_helper_method(prefix, name, :url, segments) 194 | end 195 | 196 | def define_params_helper(prefix, name, singleton, polymorphic, parent_index, ivars) 197 | params_method_name = ['', prefix, name, :params].compact.join('_') 198 | 199 | undef_method params_method_name if method_defined? params_method_name 200 | 201 | define_method params_method_name do |*given_args| 202 | given_args = given_args.collect { |arg| arg.respond_to?(:permitted?) ? arg.to_h : arg } 203 | given_options = given_args.extract_options! 204 | 205 | args = ivars.map do |ivar| 206 | ivar.is_a?(Symbol) && ivar.to_s.start_with?('@') ? instance_variable_get(ivar) : ivar 207 | end 208 | args[parent_index] = parent if parent_index 209 | 210 | if !(singleton && name != :parent) && args.present? && name != :collection && prefix != :new 211 | resource = args.pop 212 | args.push(given_args.first || resource) 213 | end 214 | 215 | if polymorphic 216 | if name == :collection 217 | args[parent_index] = given_args.present? ? given_args.first : parent 218 | end 219 | if (name == :collection || name == :resource && prefix == :new) && !singleton 220 | args << (@_resource_class_new ||= resource_class.new) 221 | end 222 | args.compact! if self.resources_configuration[:polymorphic][:optional] 223 | args = [args] 224 | end 225 | args << given_options 226 | end 227 | protected params_method_name 228 | end 229 | 230 | def define_helper_method(prefix, name, suffix, segments) 231 | method_name = [prefix, name, suffix].compact.join('_') 232 | params_method_name = ['', prefix, name, :params].compact.join('_') 233 | segments_method = [prefix, segments, suffix].compact.join('_') 234 | 235 | undef_method method_name if method_defined? method_name 236 | 237 | define_method method_name do |*given_args| 238 | given_args = send params_method_name, *given_args 239 | send segments_method, *given_args 240 | end 241 | protected method_name 242 | end 243 | 244 | end 245 | end 246 | -------------------------------------------------------------------------------- /lib/inherited_resources/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module InheritedResources 3 | VERSION = '2.1.0'.freeze 4 | end 5 | -------------------------------------------------------------------------------- /test/aliases_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Student 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class ApplicationController < ActionController::Base 9 | include InheritedResources::DSL 10 | end 11 | 12 | class StudentsController < ApplicationController 13 | inherit_resources 14 | respond_to :html, :xml 15 | 16 | def edit 17 | edit! do |format| 18 | format.xml { render plain: 'Render XML' } 19 | end 20 | end 21 | 22 | def new 23 | @something = 'magical' 24 | new! 25 | end 26 | 27 | create!(location: "http://test.host/") do |success, failure| 28 | success.html { render plain: "I won't redirect!" } 29 | failure.xml { render plain: "I shouldn't be rendered" } 30 | end 31 | 32 | update! do |success, failure| 33 | success.html { redirect_to(resource_url) } 34 | failure.html { render plain: "I won't render!" } 35 | end 36 | 37 | destroy! do |format| 38 | format.html { render plain: "Destroyed!" } 39 | end 40 | end 41 | 42 | class AliasesTest < ActionController::TestCase 43 | tests StudentsController 44 | 45 | def setup 46 | draw_routes do 47 | resources :students 48 | end 49 | end 50 | 51 | def teardown 52 | clear_routes 53 | end 54 | 55 | def test_assignments_before_calling_alias 56 | Student.stubs(:new).returns(mock_student) 57 | get :new 58 | 59 | assert_response :success 60 | assert_equal 'magical', assigns(:something) 61 | end 62 | 63 | def test_controller_should_render_new 64 | Student.stubs(:new).returns(mock_student) 65 | get :new 66 | 67 | assert_response :success 68 | assert_equal 'New HTML', @response.body.strip 69 | end 70 | 71 | def test_expose_the_requested_user_on_edit 72 | Student.expects(:find).with('42').returns(mock_student) 73 | get :edit, params: { id: '42' } 74 | 75 | assert_equal mock_student, assigns(:student) 76 | assert_response :success 77 | end 78 | 79 | def test_controller_should_render_edit 80 | Student.stubs(:find).returns(mock_student) 81 | get :edit, params: { id: '42' } 82 | 83 | assert_response :success 84 | assert_equal 'Edit HTML', @response.body.strip 85 | end 86 | 87 | def test_render_xml_when_it_is_given_as_a_block 88 | @request.accept = 'application/xml' 89 | Student.stubs(:find).returns(mock_student) 90 | get :edit, params: { id: '42' } 91 | 92 | assert_response :success 93 | assert_equal 'Render XML', @response.body 94 | end 95 | 96 | def test_is_not_redirected_on_create_with_success_if_success_block_is_given 97 | Student.stubs(:new).returns(mock_student(save: true)) 98 | @controller.stubs(:resource_url).returns('http://test.host/') 99 | post :create 100 | 101 | assert_response :success 102 | assert_equal "I won't redirect!", @response.body 103 | end 104 | 105 | def test_dumb_responder_quietly_receives_everything_on_failure 106 | @request.accept = 'text/html' 107 | Student.stubs(:new).returns(mock_student(save: false, errors: {some: :error})) 108 | @controller.stubs(:resource_url).returns('http://test.host/') 109 | post :create 110 | 111 | assert_response :unprocessable_entity 112 | assert_equal "New HTML", @response.body.strip 113 | end 114 | 115 | def test_html_is_the_default_when_only_xml_is_overwritten 116 | @request.accept = '*/*' 117 | Student.stubs(:new).returns(mock_student(save: false, errors: {some: :error})) 118 | @controller.stubs(:resource_url).returns('http://test.host/') 119 | post :create 120 | 121 | assert_response :unprocessable_entity 122 | assert_equal "New HTML", @response.body.strip 123 | end 124 | 125 | def test_wont_render_edit_template_on_update_with_failure_if_failure_block_is_given 126 | Student.stubs(:find).returns(mock_student(update: false, errors: { fail: true })) 127 | put :update, params: { id: '42' } 128 | 129 | assert_response :success 130 | assert_equal "I won't render!", @response.body 131 | end 132 | 133 | def test_dumb_responder_quietly_receives_everything_on_success 134 | Student.stubs(:find).returns(mock_student(update: true)) 135 | @controller.stubs(:resource_url).returns('http://test.host/') 136 | put :update, params: { id: '42', student: {these: 'params'} } 137 | 138 | assert_equal mock_student, assigns(:student) 139 | end 140 | 141 | def test_block_is_called_when_student_is_destroyed 142 | Student.stubs(:find).returns(mock_student(destroy: true)) 143 | delete :destroy, params: { id: '42' } 144 | 145 | assert_response :success 146 | assert_equal "Destroyed!", @response.body 147 | end 148 | 149 | def test_options_are_used_in_respond_with 150 | @request.accept = "application/xml" 151 | mock_student = mock_student(save: true, to_xml: "XML") 152 | Student.stubs(:new).returns(mock_student) 153 | 154 | post :create 155 | 156 | assert_equal "http://test.host/", @response.location 157 | end 158 | 159 | protected 160 | 161 | def mock_student(expectations={}) 162 | @mock_student ||= begin 163 | student = mock(expectations.except(:errors)) 164 | student.stubs(:class).returns(Student) 165 | student.stubs(:errors).returns(expectations.fetch(:errors, {})) 166 | student 167 | end 168 | end 169 | end 170 | -------------------------------------------------------------------------------- /test/association_chain_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Pet 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class Puppet 9 | extend ActiveModel::Naming 10 | end 11 | 12 | class PetsController < InheritedResources::Base 13 | attr_accessor :current_user 14 | 15 | def edit 16 | @pet = 'new pet' 17 | edit! 18 | end 19 | 20 | protected 21 | 22 | def collection 23 | @pets ||= end_of_association_chain.all 24 | end 25 | 26 | def begin_of_association_chain 27 | @current_user 28 | end 29 | end 30 | 31 | class BeginOfAssociationChainTest < ActionController::TestCase 32 | tests PetsController 33 | 34 | def setup 35 | draw_routes do 36 | resources :pets 37 | end 38 | 39 | @controller.current_user = mock() 40 | end 41 | 42 | def teardown 43 | clear_routes 44 | end 45 | 46 | def test_begin_of_association_chain_is_called_on_index 47 | @controller.current_user.expects(:pets).returns(Pet) 48 | Pet.expects(:all).returns(mock_pet) 49 | get :index 50 | 51 | assert_response :success 52 | assert_equal 'Index HTML', @response.body.strip 53 | end 54 | 55 | def test_begin_of_association_chain_is_called_on_new 56 | @controller.current_user.expects(:pets).returns(Pet) 57 | Pet.expects(:build).returns(mock_pet) 58 | get :new 59 | 60 | assert_response :success 61 | assert_equal 'New HTML', @response.body.strip 62 | end 63 | 64 | def test_begin_of_association_chain_is_called_on_show 65 | @controller.current_user.expects(:pets).returns(Pet) 66 | Pet.expects(:find).with('47').returns(mock_pet) 67 | get :show, params: { id: '47' } 68 | 69 | assert_response :success 70 | assert_equal 'Show HTML', @response.body.strip 71 | end 72 | 73 | def test_instance_variable_should_not_be_set_if_already_defined 74 | @controller.current_user.expects(:pets).never 75 | Pet.expects(:find).never 76 | get :edit, params: { id: '47' } 77 | 78 | assert_response :success 79 | assert_equal 'new pet', assigns(:pet) 80 | end 81 | 82 | def test_model_is_not_initialized_with_nil 83 | @controller.current_user.expects(:pets).returns(Pet) 84 | Pet.expects(:build).with({}).returns(mock_pet) 85 | get :new 86 | 87 | assert_equal mock_pet, assigns(:pet) 88 | end 89 | 90 | def test_begin_of_association_chain_is_included_in_chain 91 | @controller.current_user.expects(:pets).returns(Pet) 92 | Pet.expects(:build).with({}).returns(mock_pet) 93 | get :new 94 | 95 | assert_equal [@controller.current_user], @controller.send(:association_chain) 96 | end 97 | 98 | protected 99 | 100 | def mock_pet(stubs={}) 101 | @mock_pet ||= mock(stubs) 102 | end 103 | end 104 | 105 | class PuppetsController < InheritedResources::Base 106 | optional_belongs_to :pet 107 | end 108 | 109 | class AssociationChainTest < ActionController::TestCase 110 | tests PuppetsController 111 | 112 | def setup 113 | draw_routes do 114 | resources :puppets 115 | end 116 | 117 | @controller.stubs(:collection_url).returns('/') 118 | end 119 | 120 | def teardown 121 | clear_routes 122 | end 123 | 124 | def test_parent_is_added_to_association_chain 125 | Pet.expects(:find).with('37').returns(mock_pet) 126 | mock_pet.expects(:puppets).returns(Puppet) 127 | Puppet.expects(:find).with('42').returns(mock_puppet) 128 | mock_puppet.expects(:destroy) 129 | delete :destroy, params: { id: '42', pet_id: '37' } 130 | 131 | assert_equal [mock_pet], @controller.send(:association_chain) 132 | end 133 | 134 | def test_parent_is_added_to_association_chain_if_not_available 135 | Puppet.expects(:find).with('42').returns(mock_puppet) 136 | mock_puppet.expects(:destroy) 137 | delete :destroy, params: { id: '42' } 138 | 139 | assert_empty @controller.send(:association_chain) 140 | end 141 | 142 | protected 143 | 144 | def mock_pet(stubs={}) 145 | @mock_pet ||= mock(stubs) 146 | end 147 | 148 | def mock_puppet(stubs={}) 149 | @mock_puppet ||= mock(stubs) 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /test/autoload/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module MyTestNamespace 3 | class Engine; end 4 | end 5 | -------------------------------------------------------------------------------- /test/base_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class User 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class AccountsController < InheritedResources::Base 9 | end 10 | 11 | class UsersController < AccountsController 12 | respond_to :html, :xml 13 | respond_to :js, only: [:create, :update, :destroy] 14 | attr_reader :scopes_applied 15 | 16 | def self.name 17 | "UsersController" 18 | end 19 | 20 | protected 21 | 22 | def apply_scopes(object) 23 | @scopes_applied = true 24 | object 25 | end 26 | end 27 | 28 | module UserTestHelper 29 | def setup 30 | draw_routes do 31 | resources :users 32 | end 33 | 34 | @controller_class = Class.new(UsersController) 35 | @controller = @controller_class.new 36 | @controller.request = @request = new_request 37 | @controller.response = @response = new_response 38 | @controller.stubs(:user_url).returns("/") 39 | end 40 | 41 | def teardown 42 | clear_routes 43 | end 44 | 45 | protected 46 | 47 | def new_request 48 | ActionController::TestRequest.create(UsersController) 49 | end 50 | 51 | def new_response 52 | ActionDispatch::TestResponse.create 53 | end 54 | 55 | def mock_user(expectations={}) 56 | @mock_user ||= begin 57 | user = mock(expectations.except(:errors)) 58 | user.stubs(:class).returns(User) 59 | user.stubs(:errors).returns(expectations.fetch(:errors, {})) 60 | user.singleton_class.class_eval do 61 | def method_missing(symbol, *arguments, &block) 62 | raise NoMethodError.new('this is expected by Array#flatten') if symbol == :to_ary 63 | super 64 | end 65 | end 66 | user 67 | end 68 | end 69 | 70 | def build_parameters(hash) 71 | ActionController::Parameters.new(hash) 72 | end 73 | end 74 | 75 | class IndexActionBaseTest < ActionController::TestCase 76 | include UserTestHelper 77 | 78 | def test_expose_all_users_as_instance_variable 79 | User.expects(:scoped).returns([mock_user]) 80 | get :index 81 | 82 | assert_equal [mock_user], assigns(:users) 83 | end 84 | 85 | def test_apply_scopes_if_method_is_available 86 | User.expects(:scoped).returns([mock_user]) 87 | get :index 88 | 89 | assert @controller.scopes_applied 90 | end 91 | 92 | def test_controller_should_render_index 93 | User.stubs(:scoped).returns([mock_user]) 94 | get :index 95 | 96 | assert_response :success 97 | assert_equal 'Index HTML', @response.body.strip 98 | end 99 | 100 | def test_render_all_users_as_xml_when_mime_type_is_xml 101 | @request.accept = 'application/xml' 102 | User.expects(:scoped).returns(collection = [mock_user]) 103 | collection.expects(:to_xml).returns('Generated XML') 104 | get :index 105 | 106 | assert_response :success 107 | assert_equal 'Generated XML', @response.body 108 | end 109 | 110 | def test_scoped_is_called_only_when_available 111 | User.stubs(:all).returns([mock_user]) 112 | get :index 113 | 114 | assert_instance_of Array, assigns(:users) 115 | end 116 | end 117 | 118 | class ShowActionBaseTest < ActionController::TestCase 119 | include UserTestHelper 120 | 121 | def test_expose_the_requested_user 122 | User.expects(:find).with('42').returns(mock_user) 123 | get :show, params: { id: '42' } 124 | 125 | assert_equal mock_user, assigns(:user) 126 | end 127 | 128 | def test_controller_should_render_show 129 | User.stubs(:find).returns(mock_user) 130 | get :show, params: { id: '42' } 131 | 132 | assert_response :success 133 | assert_equal 'Show HTML', @response.body.strip 134 | end 135 | 136 | def test_render_exposed_user_as_xml_when_mime_type_is_xml 137 | @request.accept = 'application/xml' 138 | User.expects(:find).with('42').returns(mock_user) 139 | mock_user.expects(:to_xml).returns("Generated XML") 140 | 141 | get :show, params: { id: '42' } 142 | 143 | assert_response :success 144 | assert_equal 'Generated XML', @response.body 145 | end 146 | end 147 | 148 | class NewActionBaseTest < ActionController::TestCase 149 | include UserTestHelper 150 | 151 | def test_expose_a_new_user 152 | User.expects(:new).returns(mock_user) 153 | get :new 154 | 155 | assert_equal mock_user, assigns(:user) 156 | end 157 | 158 | def test_controller_should_render_new 159 | User.stubs(:new).returns(mock_user) 160 | get :new 161 | 162 | assert_response :success 163 | assert_equal 'New HTML', @response.body.strip 164 | end 165 | 166 | def test_render_exposed_a_new_user_as_xml_when_mime_type_is_xml 167 | @request.accept = 'application/xml' 168 | User.expects(:new).returns(mock_user) 169 | mock_user.expects(:to_xml).returns("Generated XML") 170 | 171 | get :new 172 | 173 | assert_response :success 174 | assert_equal 'Generated XML', @response.body 175 | end 176 | end 177 | 178 | class EditActionBaseTest < ActionController::TestCase 179 | include UserTestHelper 180 | 181 | def test_expose_the_requested_user 182 | User.expects(:find).with('42').returns(mock_user) 183 | get :edit, params: { id: '42' } 184 | 185 | assert_response :success 186 | assert_equal mock_user, assigns(:user) 187 | end 188 | 189 | def test_controller_should_render_edit 190 | User.stubs(:find).returns(mock_user) 191 | get :edit, params: { id: '42' } 192 | 193 | assert_response :success 194 | assert_equal 'Edit HTML', @response.body.strip 195 | end 196 | end 197 | 198 | class CreateActionBaseTest < ActionController::TestCase 199 | include UserTestHelper 200 | 201 | def test_expose_a_newly_create_user_when_saved_with_success 202 | User.expects(:new).with(build_parameters({'these' => 'params'})).returns(mock_user(save: true)) 203 | post :create, params: { user: {these: 'params'} } 204 | 205 | assert_equal mock_user, assigns(:user) 206 | end 207 | 208 | def test_expose_a_newly_create_user_when_saved_with_success_and_role_setted 209 | @controller.class.send(:with_role, :admin) 210 | User.expects(:new).with(build_parameters({'these' => 'params'}), {as: :admin}).returns(mock_user(save: true)) 211 | post :create, params: { user: {these: 'params'} } 212 | 213 | assert_equal mock_user, assigns(:user) 214 | end 215 | 216 | def test_expose_a_newly_create_user_when_saved_with_success_and_without_protection_setted 217 | @controller.class.send(:without_protection, true) 218 | User.expects(:new).with(build_parameters({'these' => 'params'}), {without_protection: true}).returns(mock_user(save: true)) 219 | post :create, params: { user: {these: 'params'} } 220 | 221 | assert_equal mock_user, assigns(:user) 222 | end 223 | 224 | def test_redirect_to_the_created_user 225 | User.stubs(:new).returns(mock_user(save: true)) 226 | @controller.expects(:resource_url).returns('http://test.host/') 227 | post :create, format: :html 228 | 229 | assert_redirected_to 'http://test.host/' 230 | end 231 | 232 | def test_show_flash_message_when_success 233 | User.stubs(:new).returns(mock_user(save: true)) 234 | post :create 235 | 236 | assert_equal 'User was successfully created.', flash[:notice] 237 | end 238 | 239 | def test_show_flash_message_with_javascript_request_when_success 240 | User.stubs(:new).returns(mock_user(save: true)) 241 | post :create, format: :js 242 | 243 | assert_equal 'User was successfully created.', flash[:notice] 244 | end 245 | 246 | def test_render_new_template_when_user_cannot_be_saved 247 | User.stubs(:new).returns(mock_user(save: false, errors: {some: :error})) 248 | post :create 249 | 250 | assert_response :unprocessable_entity 251 | assert_equal "New HTML", @response.body.strip 252 | end 253 | 254 | def test_dont_show_flash_message_when_user_cannot_be_saved 255 | User.stubs(:new).returns(mock_user(save: false, errors: {some: :error})) 256 | post :create 257 | 258 | assert_empty flash 259 | end 260 | end 261 | 262 | class UpdateActionBaseTest < ActionController::TestCase 263 | include UserTestHelper 264 | 265 | def test_update_the_requested_object 266 | User.expects(:find).with('42').returns(mock_user) 267 | mock_user.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 268 | put :update, params: { id: '42', user: {these: 'params'} } 269 | 270 | assert_equal mock_user, assigns(:user) 271 | end 272 | 273 | def test_update_the_requested_object_when_setted_role 274 | @controller.class.send(:with_role, :admin) 275 | User.expects(:find).with('42').returns(mock_user) 276 | mock_user.expects(:update).with(build_parameters({'these' => 'params'}), {as: :admin}).returns(true) 277 | put :update, params: { id: '42', user: {these: 'params'} } 278 | 279 | assert_equal mock_user, assigns(:user) 280 | end 281 | 282 | def test_update_the_requested_object_when_setted_without_protection 283 | @controller.class.send(:without_protection, true) 284 | User.expects(:find).with('42').returns(mock_user) 285 | mock_user.expects(:update).with(build_parameters({'these' => 'params'}), {without_protection: true}).returns(true) 286 | put :update, params: { id: '42', user: {these: 'params'} } 287 | 288 | assert_equal mock_user, assigns(:user) 289 | end 290 | 291 | def test_redirect_to_the_updated_user 292 | User.stubs(:find).returns(mock_user(update: true)) 293 | @controller.expects(:resource_url).returns('http://test.host/') 294 | put :update, params: { id: '42' } 295 | 296 | assert_redirected_to 'http://test.host/' 297 | end 298 | 299 | def test_redirect_to_the_users_list_if_show_undefined 300 | @controller.class.send(:actions, :all, except: :show) 301 | User.stubs(:find).returns(mock_user(update: true)) 302 | @controller.expects(:collection_url).returns('http://test.host/') 303 | put :update, params: { id: '42' } 304 | 305 | assert_redirected_to 'http://test.host/' 306 | end 307 | 308 | def test_show_flash_message_when_success 309 | User.stubs(:find).returns(mock_user(update: true)) 310 | put :update, params: { id: '42' } 311 | 312 | assert_equal 'User was successfully updated.', flash[:notice] 313 | end 314 | 315 | def test_show_flash_message_with_javascript_request_when_success 316 | User.stubs(:find).returns(mock_user(update: true)) 317 | post :update, params: { id: '42' }, format: :js 318 | 319 | assert_equal 'User was successfully updated.', flash[:notice] 320 | end 321 | 322 | def test_render_edit_template_when_user_cannot_be_saved 323 | User.stubs(:find).returns(mock_user(update: false, errors: {some: :error})) 324 | put :update, params: { id: '42' } 325 | 326 | assert_response :unprocessable_entity 327 | assert_equal "Edit HTML", @response.body.strip 328 | end 329 | 330 | def test_dont_show_flash_message_when_user_cannot_be_saved 331 | User.stubs(:find).returns(mock_user(update: false, errors: {some: :error})) 332 | put :update, params: { id: '42' } 333 | 334 | assert_empty flash 335 | end 336 | end 337 | 338 | class DestroyActionBaseTest < ActionController::TestCase 339 | include UserTestHelper 340 | 341 | def test_the_requested_user_is_destroyed 342 | User.expects(:find).with('42').returns(mock_user) 343 | mock_user.expects(:destroy).returns(true) 344 | delete :destroy, params: { id: '42' } 345 | 346 | assert_equal mock_user, assigns(:user) 347 | end 348 | 349 | def test_show_flash_message_when_user_can_be_deleted 350 | User.stubs(:find).returns(mock_user(destroy: true)) 351 | delete :destroy, params: { id: '42' } 352 | 353 | assert_equal 'User was successfully destroyed.', flash[:notice] 354 | end 355 | 356 | def test_show_flash_message_with_javascript_request_when_user_can_be_deleted 357 | User.stubs(:find).returns(mock_user(destroy: true)) 358 | delete :destroy, params: { id: '42' }, format: :js 359 | 360 | assert_equal 'User was successfully destroyed.', flash[:notice] 361 | end 362 | 363 | def test_show_flash_message_when_user_cannot_be_deleted 364 | User.stubs(:find).returns(mock_user(destroy: false, errors: { fail: true })) 365 | delete :destroy, params: { id: '42' } 366 | 367 | assert_equal 'User could not be destroyed.', flash[:alert] 368 | end 369 | 370 | def test_show_flash_message_with_javascript_request_when_user_cannot_be_deleted 371 | User.stubs(:find).returns(mock_user(destroy: false, errors: { fail: true })) 372 | delete :destroy, params: { id: '42' }, format: :js 373 | 374 | assert_response :unprocessable_entity 375 | assert_equal 'User could not be destroyed.', flash[:alert] 376 | end 377 | 378 | def test_redirects_to_users_list 379 | User.stubs(:find).returns(mock_user(destroy: true)) 380 | @controller.expects(:collection_url).returns('http://test.host/') 381 | delete :destroy, params: { id: '42' } 382 | 383 | assert_response :see_other 384 | assert_redirected_to 'http://test.host/' 385 | end 386 | 387 | def test_redirects_to_the_resource_if_cannot_be_destroyed 388 | User.stubs(:find).returns(mock_user(destroy: false)) 389 | @controller.expects(:collection_url).returns('http://test.host/') 390 | delete :destroy, params: { id: '42' } 391 | 392 | assert_response :see_other 393 | assert_redirected_to 'http://test.host/' 394 | end 395 | end 396 | -------------------------------------------------------------------------------- /test/belongs_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Post 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class Comment 9 | extend ActiveModel::Naming 10 | end 11 | 12 | class CommentsController < InheritedResources::Base 13 | belongs_to :post 14 | end 15 | 16 | class BelongsToTest < ActionController::TestCase 17 | tests CommentsController 18 | 19 | def setup 20 | draw_routes do 21 | resources :comments, :posts 22 | end 23 | 24 | Post.expects(:find).with('37').returns(mock_post) 25 | mock_post.expects(:comments).returns(Comment) 26 | end 27 | 28 | def teardown 29 | clear_routes 30 | end 31 | 32 | def test_expose_all_comments_as_instance_variable_on_index 33 | Comment.expects(:scoped).returns([mock_comment]) 34 | get :index, params: { post_id: '37' } 35 | 36 | assert_equal mock_post, assigns(:post) 37 | assert_equal [mock_comment], assigns(:comments) 38 | end 39 | 40 | def test_expose_the_requested_comment_on_show 41 | Comment.expects(:find).with('42').returns(mock_comment) 42 | get :show, params: { id: '42', post_id: '37' } 43 | 44 | assert_equal mock_post, assigns(:post) 45 | assert_equal mock_comment, assigns(:comment) 46 | end 47 | 48 | def test_expose_a_new_comment_on_new 49 | Comment.expects(:build).returns(mock_comment) 50 | get :new, params: { post_id: '37' } 51 | 52 | assert_equal mock_post, assigns(:post) 53 | assert_equal mock_comment, assigns(:comment) 54 | end 55 | 56 | def test_expose_the_requested_comment_on_edit 57 | Comment.expects(:find).with('42').returns(mock_comment) 58 | get :edit, params: { id: '42', post_id: '37' } 59 | 60 | assert_equal mock_post, assigns(:post) 61 | assert_equal mock_comment, assigns(:comment) 62 | end 63 | 64 | def test_expose_a_newly_create_comment_on_create 65 | Comment.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_comment(save: true)) 66 | post :create, params: { post_id: '37', comment: {these: 'params'} } 67 | 68 | assert_equal mock_post, assigns(:post) 69 | assert_equal mock_comment, assigns(:comment) 70 | end 71 | 72 | def test_update_the_requested_object_on_update 73 | Comment.expects(:find).with('42').returns(mock_comment) 74 | mock_comment.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 75 | put :update, params: { id: '42', post_id: '37', comment: {these: 'params'} } 76 | 77 | assert_equal mock_post, assigns(:post) 78 | assert_equal mock_comment, assigns(:comment) 79 | end 80 | 81 | def test_the_requested_comment_is_destroyed_on_destroy 82 | Comment.expects(:find).with('42').returns(mock_comment) 83 | mock_comment.expects(:destroy) 84 | delete :destroy, params: { id: '42', post_id: '37' } 85 | 86 | assert_equal mock_post, assigns(:post) 87 | assert_equal mock_comment, assigns(:comment) 88 | end 89 | 90 | def helper_methods 91 | @controller.class._helpers.instance_methods.map {|m| m.to_s } 92 | end 93 | 94 | def test_helpers 95 | Comment.expects(:scoped).returns([mock_comment]) 96 | get :index, params: { post_id: '37' } 97 | 98 | assert_includes helper_methods, 'parent?' 99 | assert @controller.send(:parent?) 100 | assert_equal mock_post, assigns(:post) 101 | assert_includes helper_methods, 'parent' 102 | assert_equal mock_post, @controller.send(:parent) 103 | end 104 | 105 | protected 106 | 107 | def mock_post(stubs={}) 108 | @mock_post ||= mock(stubs) 109 | end 110 | 111 | def mock_comment(stubs={}) 112 | @mock_comment ||= mock(stubs) 113 | end 114 | 115 | def build_parameters(hash) 116 | ActionController::Parameters.new(hash) 117 | end 118 | end 119 | 120 | class Reply 121 | extend ActiveModel::Naming 122 | end 123 | 124 | class RepliesController < InheritedResources::Base 125 | belongs_to :post 126 | actions :all, except: [:show, :index] 127 | end 128 | 129 | class BelongsToWithRedirectsTest < ActionController::TestCase 130 | tests RepliesController 131 | 132 | def setup 133 | draw_routes do 134 | resources :replies, :posts 135 | end 136 | 137 | Post.expects(:find).with('37').returns(mock_post) 138 | mock_post.expects(:replies).returns(Reply) 139 | end 140 | 141 | def teardown 142 | clear_routes 143 | end 144 | 145 | def test_redirect_to_the_post_on_create_if_show_and_index_undefined 146 | @controller.expects(:parent_url).returns('http://test.host/') 147 | Reply.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_reply(save: true)) 148 | post :create, params: { post_id: '37', reply: { these: 'params' } } 149 | 150 | assert_redirected_to 'http://test.host/' 151 | end 152 | 153 | def test_redirect_to_the_post_on_update_if_show_and_index_undefined 154 | Reply.stubs(:find).returns(mock_reply(update: true)) 155 | @controller.expects(:parent_url).returns('http://test.host/') 156 | put :update, params: { id: '42', post_id: '37', reply: { these: 'params' } } 157 | 158 | assert_redirected_to 'http://test.host/' 159 | end 160 | 161 | def test_redirect_to_the_post_on_destroy_if_show_and_index_undefined 162 | Reply.expects(:find).with('42').returns(mock_reply) 163 | mock_reply.expects(:destroy) 164 | @controller.expects(:parent_url).returns('http://test.host/') 165 | delete :destroy, params: { id: '42', post_id: '37' } 166 | 167 | assert_redirected_to 'http://test.host/' 168 | end 169 | 170 | protected 171 | 172 | def mock_post(stubs={}) 173 | @mock_post ||= mock(stubs) 174 | end 175 | 176 | def mock_reply(stubs={}) 177 | @mock_reply ||= mock(stubs) 178 | end 179 | 180 | def build_parameters(hash) 181 | ActionController::Parameters.new(hash) 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /test/belongs_to_with_shallow_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Post 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class Tag 9 | extend ActiveModel::Naming 10 | end 11 | 12 | class TagsController < InheritedResources::Base 13 | belongs_to :post, shallow: true, finder: :find_by_slug 14 | end 15 | 16 | class BelongsToWithShallowTest < ActionController::TestCase 17 | tests TagsController 18 | 19 | def setup 20 | draw_routes do 21 | resources :tags 22 | end 23 | 24 | Post.expects(:find_by_slug).with('thirty_seven').returns(mock_post) 25 | mock_post.expects(:tags).returns(Tag) 26 | 27 | @controller.stubs(:collection_url).returns('/') 28 | end 29 | 30 | def teardown 31 | clear_routes 32 | end 33 | 34 | def test_expose_all_tags_as_instance_variable_on_index 35 | Tag.expects(:scoped).returns([mock_tag]) 36 | get :index, params: { post_id: 'thirty_seven' } 37 | 38 | assert_equal mock_post, assigns(:post) 39 | assert_equal [mock_tag], assigns(:tags) 40 | end 41 | 42 | def test_expose_a_new_tag_on_new 43 | Tag.expects(:build).returns(mock_tag) 44 | get :new, params: { post_id: 'thirty_seven' } 45 | 46 | assert_equal mock_post, assigns(:post) 47 | assert_equal mock_tag, assigns(:tag) 48 | end 49 | 50 | def test_expose_a_newly_create_tag_on_create 51 | Tag.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_tag(save: true)) 52 | post :create, params: { post_id: 'thirty_seven', tag: {these: 'params'} } 53 | 54 | assert_equal mock_post, assigns(:post) 55 | assert_equal mock_tag, assigns(:tag) 56 | end 57 | 58 | def test_expose_the_requested_tag_on_show 59 | should_find_parents 60 | get :show, params: { id: '42' } 61 | 62 | assert_equal mock_post, assigns(:post) 63 | assert_equal mock_tag, assigns(:tag) 64 | end 65 | 66 | def test_expose_the_requested_tag_on_edit 67 | should_find_parents 68 | get :edit, params: { id: '42' } 69 | 70 | assert_equal mock_post, assigns(:post) 71 | assert_equal mock_tag, assigns(:tag) 72 | end 73 | 74 | def test_update_the_requested_object_on_update 75 | should_find_parents 76 | mock_tag.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 77 | put :update, params: { id: '42', tag: {these: 'params'} } 78 | 79 | assert_equal mock_post, assigns(:post) 80 | assert_equal mock_tag, assigns(:tag) 81 | end 82 | 83 | def test_the_requested_tag_is_destroyed_on_destroy 84 | should_find_parents 85 | mock_tag.expects(:destroy) 86 | delete :destroy, params: { id: '42', post_id: '37' } 87 | 88 | assert_equal mock_post, assigns(:post) 89 | assert_equal mock_tag, assigns(:tag) 90 | end 91 | 92 | protected 93 | 94 | def should_find_parents 95 | mock_tag.expects(:post).returns(mock_post) 96 | mock_post.expects(:to_param).returns('thirty_seven') 97 | Tag.expects(:find).with('42').twice.returns(mock_tag) 98 | end 99 | 100 | def mock_post(stubs={}) 101 | @mock_post ||= mock(stubs) 102 | end 103 | 104 | def mock_tag(stubs={}) 105 | @mock_tag ||= mock(stubs) 106 | end 107 | 108 | def build_parameters(hash) 109 | ActionController::Parameters.new(hash) 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /test/changelog_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class ChangelogTest < ActiveSupport::TestCase 5 | def setup 6 | path = File.join(File.dirname(__dir__), "CHANGELOG.md") 7 | @changelog = File.read(path) 8 | end 9 | 10 | def test_has_definitions_for_all_implicit_links 11 | implicit_link_names = @changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq 12 | 13 | implicit_link_names.each do |name| 14 | assert_includes @changelog, "[#{name}]: https" 15 | end 16 | end 17 | 18 | def test_entry_does_end_with_a_punctuation 19 | lines = @changelog.each_line 20 | entries = lines.grep(/^\*/) 21 | 22 | entries.each do |entry| 23 | assert_no_match(/(\.|\:)$/, entry) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/class_methods_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Book; end 5 | class Folder; end 6 | 7 | class BooksController < InheritedResources::Base 8 | custom_actions collection: :search, resource: [:delete] 9 | actions :index, :show 10 | end 11 | 12 | class ReadersController < InheritedResources::Base 13 | actions :all, except: [ :edit, :update ] 14 | end 15 | 16 | class FoldersController < InheritedResources::Base 17 | end 18 | 19 | class Dean 20 | def self.human_name; 'Dean'; end 21 | end 22 | 23 | class DeansController < InheritedResources::Base 24 | belongs_to :school 25 | end 26 | 27 | module Controller 28 | class User; end 29 | 30 | class UsersController < InheritedResources::Base; end 31 | 32 | module Admin 33 | class UsersController < InheritedResources::Base; end 34 | end 35 | end 36 | 37 | class ControllerGroup; end 38 | 39 | module Controller 40 | class GroupsController < InheritedResources::Base; end 41 | end 42 | 43 | module Library 44 | class Base 45 | end 46 | 47 | class Category 48 | end 49 | 50 | class Subcategory 51 | end 52 | 53 | class SubcategoriesController < InheritedResources::Base 54 | end 55 | end 56 | 57 | module MyEngine 58 | class Engine < Rails::Engine 59 | isolate_namespace MyEngine 60 | end 61 | 62 | class PeopleController < InheritedResources::Base; end 63 | end 64 | 65 | module MyNamespace 66 | class PeopleController < InheritedResources::Base; end 67 | end 68 | 69 | module EmptyNamespace; end 70 | 71 | class ActionsClassMethodTest < ActionController::TestCase 72 | tests BooksController 73 | 74 | def setup 75 | draw_routes do 76 | resources :books 77 | end 78 | end 79 | 80 | def teardown 81 | clear_routes 82 | end 83 | 84 | def test_cannot_render_actions 85 | assert_raise AbstractController::ActionNotFound do 86 | get :new 87 | end 88 | end 89 | 90 | def test_actions_are_undefined 91 | action_methods = BooksController.send(:action_methods).map(&:to_sym) 92 | 93 | assert_equal 4, action_methods.size 94 | 95 | [:index, :show, :delete, :search].each do |action| 96 | assert_includes action_methods, action 97 | end 98 | 99 | instance_methods = BooksController.send(:instance_methods).map(&:to_sym) 100 | 101 | [:new, :edit, :create, :update, :destroy].each do |action| 102 | refute_includes instance_methods, action 103 | end 104 | end 105 | 106 | def test_actions_are_undefined_when_except_option_is_given 107 | action_methods = ReadersController.send(:action_methods) 108 | 109 | assert_equal 5, action_methods.size 110 | 111 | ['index', 'new', 'show', 'create', 'destroy'].each do |action| 112 | assert_includes action_methods, action 113 | end 114 | end 115 | end 116 | 117 | class DefaultsClassMethodTest < ActiveSupport::TestCase 118 | def test_resource_class_is_set_to_nil_when_resource_model_cannot_be_found 119 | assert_nil ReadersController.send(:resource_class) 120 | end 121 | 122 | def test_defaults_are_set 123 | assert_equal Folder, FoldersController.send(:resource_class) 124 | assert_equal :folder, FoldersController.send(:resources_configuration)[:self][:instance_name] 125 | assert_equal :folders, FoldersController.send(:resources_configuration)[:self][:collection_name] 126 | end 127 | 128 | def test_defaults_can_be_overwriten 129 | BooksController.send(:defaults, resource_class: String, instance_name: 'string', collection_name: 'strings') 130 | 131 | assert_equal String, BooksController.send(:resource_class) 132 | assert_equal :string, BooksController.send(:resources_configuration)[:self][:instance_name] 133 | assert_equal :strings, BooksController.send(:resources_configuration)[:self][:collection_name] 134 | 135 | BooksController.send(:defaults, class_name: 'Integer', instance_name: :integer, collection_name: :integers) 136 | 137 | assert_equal Integer, BooksController.send(:resource_class) 138 | assert_equal :integer, BooksController.send(:resources_configuration)[:self][:instance_name] 139 | assert_equal :integers, BooksController.send(:resources_configuration)[:self][:collection_name] 140 | end 141 | 142 | def test_defaults_raises_invalid_key 143 | assert_raise ArgumentError do 144 | BooksController.send(:defaults, boom: String) 145 | end 146 | end 147 | 148 | def test_url_helpers_are_recreated_when_defaults_change # rubocop:disable Minitest/NoAssertions 149 | BooksController.expects(:create_resources_url_helpers!).returns(true).once 150 | BooksController.send(:defaults, instance_name: 'string', collection_name: 'strings') 151 | end 152 | end 153 | 154 | class BelongsToErrorsTest < ActiveSupport::TestCase 155 | def test_belongs_to_raise_errors_with_invalid_arguments 156 | assert_raise ArgumentError do 157 | DeansController.send(:belongs_to) 158 | end 159 | 160 | assert_raise ArgumentError do 161 | DeansController.send(:belongs_to, :nice, invalid_key: '') 162 | end 163 | end 164 | 165 | def test_belongs_to_raises_an_error_when_multiple_associations_are_given_with_options 166 | assert_raise ArgumentError do 167 | DeansController.send(:belongs_to, :arguments, :with_options, parent_class: Book) 168 | end 169 | end 170 | 171 | def test_url_helpers_are_recreated_just_once_when_belongs_to_is_called_with_block # rubocop:disable Minitest/NoAssertions 172 | DeansController.expects(:create_resources_url_helpers!).returns(true).once 173 | DeansController.send(:belongs_to, :school) do 174 | belongs_to :association 175 | end 176 | ensure 177 | DeansController.send(:parents_symbols=, [:school]) 178 | end 179 | 180 | def test_url_helpers_are_recreated_just_once_when_belongs_to_is_called_with_multiple_blocks # rubocop:disable Minitest/NoAssertions 181 | DeansController.expects(:create_resources_url_helpers!).returns(true).once 182 | DeansController.send(:belongs_to, :school) do 183 | belongs_to :association do 184 | belongs_to :nested 185 | end 186 | end 187 | ensure 188 | DeansController.send(:parents_symbols=, [:school]) 189 | end 190 | 191 | def test_belongs_to_for_namespaced_controller_and_namespaced_model_fetches_model_in_the_namespace_firstly 192 | Library::SubcategoriesController.send(:belongs_to, :category) 193 | 194 | assert_equal Library::Category, Library::SubcategoriesController.resources_configuration[:category][:parent_class] 195 | end 196 | 197 | def test_belongs_to_for_namespaced_controller_and_non_namespaced_model_sets_parent_class_properly 198 | Library::SubcategoriesController.send(:belongs_to, :book) 199 | 200 | assert_equal Book, Library::SubcategoriesController.resources_configuration[:book][:parent_class] 201 | end 202 | 203 | def test_belongs_to_for_namespaced_model_sets_parent_class_properly 204 | Library::SubcategoriesController.send(:belongs_to, :library, class_name: 'Library::Base') 205 | 206 | assert_equal Library::Base, Library::SubcategoriesController.resources_configuration[:library][:parent_class] 207 | end 208 | 209 | def test_belongs_to_without_namespace_sets_parent_class_properly 210 | FoldersController.send(:belongs_to, :book) 211 | 212 | assert_equal Book, FoldersController.resources_configuration[:book][:parent_class] 213 | end 214 | end 215 | 216 | class SpecialCasesClassMethodTest < ActionController::TestCase 217 | def test_resource_class_to_corresponding_model_class 218 | assert_equal Controller::User, Controller::UsersController.send(:resource_class) 219 | assert_equal Controller::User, Controller::Admin::UsersController.send(:resource_class) 220 | assert_equal ControllerGroup, Controller::GroupsController.send(:resource_class) 221 | end 222 | end 223 | 224 | class MountableEngineTest < ActiveSupport::TestCase 225 | def test_route_prefix_do_not_include_engine_name 226 | puts MyEngine::PeopleController.send(:resources_configuration)[:self][:route_prefix] 227 | 228 | assert_nil MyEngine::PeopleController.send(:resources_configuration)[:self][:route_prefix] 229 | end 230 | 231 | def test_route_prefix_present_when_parent_module_is_not_a_engine 232 | assert_equal :my_namespace, MyNamespace::PeopleController.send(:resources_configuration)[:self][:route_prefix] 233 | end 234 | end 235 | 236 | class EngineLoadErrorTest < ActiveSupport::TestCase 237 | def test_does_not_crash_on_engine_load_error # rubocop:disable Minitest/NoAssertions 238 | ActiveSupport::Dependencies.autoload_paths << 'test/autoload' 239 | 240 | assert_nil EmptyNamespace.class_eval <<~RUBY 241 | class PeopleController < InheritedResources::Base; end 242 | RUBY 243 | end 244 | end 245 | -------------------------------------------------------------------------------- /test/customized_base_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Car 5 | extend ActiveModel::Naming 6 | end 7 | 8 | class CarsController < InheritedResources::Base 9 | respond_to :html 10 | 11 | protected 12 | 13 | def collection 14 | @cars ||= Car.get_all 15 | end 16 | 17 | def build_resource 18 | @car ||= Car.create_new(params[:car]) 19 | end 20 | 21 | def resource 22 | @car ||= Car.get(params[:id]) 23 | end 24 | 25 | def create_resource(resource) 26 | resource.save_successfully 27 | end 28 | 29 | def update_resource(resource, attributes) 30 | resource.update_successfully(*attributes) 31 | end 32 | 33 | def destroy_resource(resource) 34 | resource.destroy_successfully 35 | end 36 | end 37 | 38 | module CarTestHelper 39 | def setup 40 | draw_routes do 41 | resources :cars 42 | end 43 | 44 | @controller = CarsController.new 45 | @controller.request = @request = new_request 46 | @controller.response = @response = new_response 47 | @controller.stubs(:car_url).returns("/") 48 | end 49 | 50 | def teardown 51 | clear_routes 52 | end 53 | 54 | protected 55 | 56 | def new_request 57 | ActionController::TestRequest.create(CarsController) 58 | end 59 | 60 | def new_response 61 | ActionDispatch::TestResponse.create 62 | end 63 | 64 | def mock_car(expectations={}) 65 | @mock_car ||= begin 66 | car = mock(expectations.except(:errors)) 67 | car.stubs(:class).returns(Car) 68 | car.stubs(:errors).returns(expectations.fetch(:errors, {})) 69 | car 70 | end 71 | end 72 | 73 | def build_parameters(hash) 74 | ActionController::Parameters.new(hash) 75 | end 76 | end 77 | 78 | class IndexActionCustomizedBaseTest < ActionController::TestCase 79 | include CarTestHelper 80 | 81 | def test_expose_all_users_as_instance_variable 82 | Car.expects(:get_all).returns([mock_car]) 83 | get :index 84 | 85 | assert_equal [mock_car], assigns(:cars) 86 | end 87 | end 88 | 89 | class ShowActionCustomizedBaseTest < ActionController::TestCase 90 | include CarTestHelper 91 | 92 | def test_expose_the_requested_user 93 | Car.expects(:get).with('42').returns(mock_car) 94 | get :show, params: { id: '42' } 95 | 96 | assert_equal mock_car, assigns(:car) 97 | end 98 | end 99 | 100 | class NewActionCustomizedBaseTest < ActionController::TestCase 101 | include CarTestHelper 102 | 103 | def test_expose_a_new_user 104 | Car.expects(:create_new).returns(mock_car) 105 | get :new 106 | 107 | assert_equal mock_car, assigns(:car) 108 | end 109 | end 110 | 111 | class EditActionCustomizedBaseTest < ActionController::TestCase 112 | include CarTestHelper 113 | 114 | def test_expose_the_requested_user 115 | Car.expects(:get).with('42').returns(mock_car) 116 | get :edit, params: { id: '42' } 117 | 118 | assert_response :success 119 | assert_equal mock_car, assigns(:car) 120 | end 121 | end 122 | 123 | class CreateActionCustomizedBaseTest < ActionController::TestCase 124 | include CarTestHelper 125 | 126 | def test_expose_a_newly_create_user_when_saved_with_success 127 | Car.expects(:create_new).with(build_parameters({'these' => 'params'})).returns(mock_car(save_successfully: true)) 128 | post :create, params: { car: {these: 'params'} } 129 | 130 | assert_equal mock_car, assigns(:car) 131 | end 132 | 133 | def test_redirect_to_the_created_user 134 | Car.stubs(:create_new).returns(mock_car(save_successfully: true)) 135 | @controller.expects(:resource_url).returns('http://test.host/') 136 | post :create 137 | 138 | assert_redirected_to 'http://test.host/' 139 | end 140 | 141 | def test_render_new_template_when_user_cannot_be_saved 142 | Car.stubs(:create_new).returns(mock_car(save_successfully: false, errors: {some: :error})) 143 | post :create 144 | 145 | assert_response :unprocessable_entity 146 | assert_equal "New HTML", @response.body.strip 147 | end 148 | end 149 | 150 | class UpdateActionCustomizedBaseTest < ActionController::TestCase 151 | include CarTestHelper 152 | 153 | def test_update_the_requested_object 154 | Car.expects(:get).with('42').returns(mock_car) 155 | mock_car.expects(:update_successfully).with(build_parameters({'these' => 'params'})).returns(true) 156 | put :update, params: { id: '42', car: {these: 'params'} } 157 | 158 | assert_equal mock_car, assigns(:car) 159 | end 160 | 161 | def test_redirect_to_the_created_user 162 | Car.stubs(:get).returns(mock_car(update_successfully: true)) 163 | @controller.expects(:resource_url).returns('http://test.host/') 164 | put :update, params: { id: '42' } 165 | 166 | assert_redirected_to 'http://test.host/' 167 | end 168 | 169 | def test_render_edit_template_when_user_cannot_be_saved 170 | Car.stubs(:get).returns(mock_car(update_successfully: false, errors: {some: :error})) 171 | put :update, params: { id: '42' } 172 | 173 | assert_response :unprocessable_entity 174 | assert_equal "Edit HTML", @response.body.strip 175 | end 176 | end 177 | 178 | class DestroyActionCustomizedBaseTest < ActionController::TestCase 179 | include CarTestHelper 180 | 181 | def test_the_requested_user_is_destroyed 182 | Car.expects(:get).with('42').returns(mock_car) 183 | mock_car.expects(:destroy_successfully) 184 | delete :destroy, params: { id: '42' } 185 | 186 | assert_equal mock_car, assigns(:car) 187 | end 188 | 189 | def test_show_flash_message_when_user_can_be_deleted 190 | Car.stubs(:get).returns(mock_car(destroy_successfully: true)) 191 | delete :destroy, params: { id: '42' } 192 | 193 | assert_equal 'Car was successfully destroyed.', flash[:notice] 194 | end 195 | 196 | def test_show_flash_message_when_cannot_be_deleted 197 | Car.stubs(:get).returns(mock_car(destroy_successfully: false, errors: { fail: true })) 198 | delete :destroy, params: { id: '42' } 199 | 200 | assert_equal 'Car could not be destroyed.', flash[:alert] 201 | end 202 | end 203 | -------------------------------------------------------------------------------- /test/customized_belongs_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class GreatSchool 5 | end 6 | 7 | class Professor 8 | def self.human_name; 'Professor'; end 9 | end 10 | 11 | class ProfessorsController < InheritedResources::Base 12 | belongs_to :school, parent_class: GreatSchool, instance_name: :great_school, 13 | finder: :find_by_title!, param: :school_title 14 | end 15 | 16 | class CustomizedBelongsToTest < ActionController::TestCase 17 | tests ProfessorsController 18 | 19 | def setup 20 | draw_routes do 21 | resources :professors 22 | end 23 | 24 | GreatSchool.expects(:find_by_title!).with('nice').returns(mock_school(professors: Professor)) 25 | @controller.stubs(:collection_url).returns('/') 26 | end 27 | 28 | def teardown 29 | clear_routes 30 | end 31 | 32 | def test_expose_the_requested_school_with_chosen_instance_variable_on_index 33 | Professor.stubs(:scoped).returns([mock_professor]) 34 | get :index, params: { school_title: 'nice' } 35 | 36 | assert_equal mock_school, assigns(:great_school) 37 | end 38 | 39 | def test_expose_the_requested_school_with_chosen_instance_variable_on_show 40 | Professor.stubs(:find).returns(mock_professor) 41 | get :show, params: { id: 42, school_title: 'nice' } 42 | 43 | assert_equal mock_school, assigns(:great_school) 44 | end 45 | 46 | def test_expose_the_requested_school_with_chosen_instance_variable_on_new 47 | Professor.stubs(:build).returns(mock_professor) 48 | get :new, params: { school_title: 'nice' } 49 | 50 | assert_equal mock_school, assigns(:great_school) 51 | end 52 | 53 | def test_expose_the_requested_school_with_chosen_instance_variable_on_edit 54 | Professor.stubs(:find).returns(mock_professor) 55 | get :edit, params: { id: 42, school_title: 'nice' } 56 | 57 | assert_equal mock_school, assigns(:great_school) 58 | end 59 | 60 | def test_expose_the_requested_school_with_chosen_instance_variable_on_create 61 | Professor.stubs(:build).returns(mock_professor(save: true)) 62 | post :create, params: { school_title: 'nice' } 63 | 64 | assert_equal mock_school, assigns(:great_school) 65 | end 66 | 67 | def test_expose_the_requested_school_with_chosen_instance_variable_on_update 68 | Professor.stubs(:find).returns(mock_professor(update: true)) 69 | put :update, params: { id: 42, school_title: 'nice' } 70 | 71 | assert_equal mock_school, assigns(:great_school) 72 | end 73 | 74 | def test_expose_the_requested_school_with_chosen_instance_variable_on_destroy 75 | Professor.stubs(:find).returns(mock_professor(destroy: true)) 76 | delete :destroy, params: { id: 42, school_title: 'nice' } 77 | 78 | assert_equal mock_school, assigns(:great_school) 79 | end 80 | 81 | protected 82 | 83 | def mock_school(stubs={}) 84 | @mock_school ||= mock(stubs) 85 | end 86 | 87 | def mock_professor(stubs={}) 88 | @mock_professor ||= mock(stubs) 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /test/customized_redirect_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Post; 5 | def self.human_name; 'Post'; end 6 | end 7 | 8 | class PostsController < InheritedResources::Base 9 | actions :all, except: [:show] 10 | end 11 | 12 | class RedirectToIndexWithoutShowTest < ActionController::TestCase 13 | tests PostsController 14 | 15 | def setup 16 | draw_routes do 17 | resources :posts 18 | end 19 | end 20 | 21 | def teardown 22 | clear_routes 23 | end 24 | 25 | def test_redirect_index_url_after_create 26 | Post.stubs(:new).returns(mock_machine(save: true)) 27 | 28 | refute_respond_to PostsController, :show 29 | post :create 30 | 31 | assert_redirected_to 'http://test.host/posts' 32 | end 33 | 34 | def test_redirect_to_index_url_after_update 35 | Post.stubs(:find).returns(mock_machine(update: true)) 36 | 37 | refute_respond_to PostsController, :show 38 | put :update, params: { id: '42' } 39 | 40 | assert_redirected_to 'http://test.host/posts' 41 | end 42 | 43 | protected 44 | 45 | def mock_machine(stubs={}) 46 | @mock_machine ||= mock(stubs) 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /test/defaults_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Malarz 5 | def self.human_name; 'Painter'; end 6 | 7 | def to_param 8 | self.slug 9 | end 10 | end 11 | 12 | class PaintersController < InheritedResources::Base 13 | defaults instance_name: 'malarz', collection_name: 'malarze', 14 | resource_class: Malarz, route_prefix: nil, 15 | finder: :find_by_slug 16 | end 17 | 18 | class DefaultsTest < ActionController::TestCase 19 | tests PaintersController 20 | 21 | def setup 22 | draw_routes do 23 | resources :painters 24 | end 25 | end 26 | 27 | def teardown 28 | clear_routes 29 | end 30 | 31 | def test_expose_all_painters_as_instance_variable 32 | Malarz.expects(:scoped).returns([mock_painter]) 33 | get :index 34 | 35 | assert_equal [mock_painter], assigns(:malarze) 36 | end 37 | 38 | def test_collection_instance_variable_should_not_be_set_if_already_defined 39 | @controller.instance_variable_set(:@malarze, [mock_painter]) 40 | Malarz.expects(:scoped).never 41 | get :index 42 | 43 | assert_equal [mock_painter], assigns(:malarze) 44 | end 45 | 46 | def test_expose_the_requested_painter_on_show 47 | Malarz.expects(:find_by_slug).with('forty_two').returns(mock_painter) 48 | get :show, params: { id: 'forty_two' } 49 | 50 | assert_equal mock_painter, assigns(:malarz) 51 | end 52 | 53 | def test_expose_a_new_painter 54 | Malarz.expects(:new).returns(mock_painter) 55 | get :new 56 | 57 | assert_equal mock_painter, assigns(:malarz) 58 | end 59 | 60 | def test_expose_the_requested_painter_on_edit 61 | Malarz.expects(:find_by_slug).with('forty_two').returns(mock_painter) 62 | get :edit, params: { id: 'forty_two' } 63 | 64 | assert_response :success 65 | assert_equal mock_painter, assigns(:malarz) 66 | end 67 | 68 | def test_expose_a_newly_create_painter_when_saved_with_success 69 | Malarz.expects(:new).with(build_parameters({'these' => 'params'})).returns(mock_painter(save: true)) 70 | post :create, params: { malarz: {these: 'params'} } 71 | 72 | assert_equal mock_painter, assigns(:malarz) 73 | end 74 | 75 | def test_update_the_requested_object 76 | Malarz.expects(:find_by_slug).with('forty_two').returns(mock_painter) 77 | mock_painter.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 78 | put :update, params: { id: 'forty_two', malarz: {these: 'params'} } 79 | 80 | assert_equal mock_painter, assigns(:malarz) 81 | end 82 | 83 | def test_the_requested_painter_is_destroyed 84 | Malarz.expects(:find_by_slug).with('forty_two').returns(mock_painter) 85 | mock_painter.expects(:destroy) 86 | delete :destroy, params: { id: 'forty_two' } 87 | 88 | assert_equal mock_painter, assigns(:malarz) 89 | end 90 | 91 | protected 92 | 93 | def mock_painter(stubs={}) 94 | @mock_painter ||= mock(stubs) 95 | end 96 | 97 | def build_parameters(hash) 98 | ActionController::Parameters.new(hash) 99 | end 100 | end 101 | 102 | class Lecturer 103 | def self.human_name; 'Einstein'; end 104 | end 105 | module University; end 106 | class University::LecturersController < InheritedResources::Base 107 | defaults finder: :find_by_slug 108 | end 109 | 110 | class DefaultsNamespaceTest < ActionController::TestCase 111 | tests University::LecturersController 112 | 113 | def setup 114 | draw_routes do 115 | namespace :university do 116 | resources :lecturers 117 | end 118 | end 119 | end 120 | 121 | def teardown 122 | clear_routes 123 | end 124 | 125 | def test_expose_all_lecturers_as_instance_variable 126 | Lecturer.expects(:scoped).returns([mock_lecturer]) 127 | get :index 128 | 129 | assert_equal [mock_lecturer], assigns(:lecturers) 130 | end 131 | 132 | def test_expose_the_requested_lecturer_on_show 133 | Lecturer.expects(:find_by_slug).with('forty_two').returns(mock_lecturer) 134 | get :show, params: { id: 'forty_two' } 135 | 136 | assert_equal mock_lecturer, assigns(:lecturer) 137 | end 138 | 139 | def test_expose_a_new_lecturer 140 | Lecturer.expects(:new).returns(mock_lecturer) 141 | get :new 142 | 143 | assert_equal mock_lecturer, assigns(:lecturer) 144 | end 145 | 146 | def test_expose_the_requested_lecturer_on_edit 147 | Lecturer.expects(:find_by_slug).with('forty_two').returns(mock_lecturer) 148 | get :edit, params: { id: 'forty_two' } 149 | 150 | assert_response :success 151 | assert_equal mock_lecturer, assigns(:lecturer) 152 | end 153 | 154 | def test_expose_a_newly_create_lecturer_when_saved_with_success 155 | Lecturer.expects(:new).with(build_parameters({'these' => 'params'})).returns(mock_lecturer(save: true)) 156 | post :create, params: { lecturer: {these: 'params'} } 157 | 158 | assert_equal mock_lecturer, assigns(:lecturer) 159 | end 160 | 161 | def test_update_the_lecturer 162 | Lecturer.expects(:find_by_slug).with('forty_two').returns(mock_lecturer) 163 | mock_lecturer.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 164 | put :update, params: { id: 'forty_two', lecturer: {these: 'params'} } 165 | 166 | assert_equal mock_lecturer, assigns(:lecturer) 167 | end 168 | 169 | def test_the_requested_lecturer_is_destroyed 170 | Lecturer.expects(:find_by_slug).with('forty_two').returns(mock_lecturer) 171 | mock_lecturer.expects(:destroy) 172 | delete :destroy, params: { id: 'forty_two' } 173 | 174 | assert_equal mock_lecturer, assigns(:lecturer) 175 | end 176 | 177 | protected 178 | 179 | def mock_lecturer(stubs={}) 180 | @mock_lecturer ||= mock(stubs) 181 | end 182 | 183 | def build_parameters(hash) 184 | ActionController::Parameters.new(hash) 185 | end 186 | end 187 | 188 | class Group 189 | end 190 | class AdminGroup 191 | end 192 | module Admin; end 193 | class Admin::Group 194 | end 195 | class Admin::GroupsController < InheritedResources::Base 196 | end 197 | class NamespacedModelForNamespacedController < ActionController::TestCase 198 | tests Admin::GroupsController 199 | 200 | def test_that_it_picked_the_namespaced_model 201 | # make public so we can test it 202 | Admin::GroupsController.send(:public, :resource_class) 203 | 204 | assert_equal Admin::Group, @controller.resource_class 205 | end 206 | end 207 | 208 | class Role 209 | end 210 | class AdminRole 211 | end 212 | class Admin::RolesController < InheritedResources::Base 213 | end 214 | class TwoPartNameModelForNamespacedController < ActionController::TestCase 215 | tests Admin::RolesController 216 | 217 | def test_that_it_picked_the_camelcased_model 218 | # make public so we can test it 219 | Admin::RolesController.send(:public, :resource_class) 220 | 221 | assert_equal AdminRole, @controller.resource_class 222 | end 223 | end 224 | 225 | class User 226 | end 227 | class Admin::UsersController < InheritedResources::Base 228 | end 229 | class AnotherTwoPartNameModelForNamespacedController < ActionController::TestCase 230 | tests Admin::UsersController 231 | 232 | def test_that_it_picked_the_camelcased_model 233 | # make public so we can test it 234 | Admin::UsersController.send(:public, :resource_class) 235 | 236 | assert_equal User, @controller.resource_class 237 | end 238 | 239 | def test_that_it_got_the_request_params_right 240 | # make public so we can test it 241 | Admin::UsersController.send(:public, :resources_configuration) 242 | 243 | assert_equal 'user', @controller.resources_configuration[:self][:request_name] 244 | end 245 | end 246 | 247 | module MyEngine 248 | class Engine < Rails::Engine 249 | isolate_namespace MyEngine 250 | end 251 | 252 | class Person 253 | extend ActiveModel::Naming 254 | end 255 | 256 | class PeopleController < InheritedResources::Base 257 | defaults resource_class: Person 258 | end 259 | end 260 | 261 | class IsolatedEngineModelController < ActionController::TestCase 262 | tests MyEngine::PeopleController 263 | 264 | def setup 265 | # make public so we can test it 266 | MyEngine::PeopleController.send(:public, *MyEngine::PeopleController.protected_instance_methods) 267 | end 268 | 269 | def test_isolated_model_name 270 | assert_equal 'person', @controller.resources_configuration[:self][:request_name] 271 | end 272 | end 273 | -------------------------------------------------------------------------------- /test/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | flash: 3 | addresses: 4 | create: 5 | notice: "You created a new address close to {{reference}}." 6 | update: 7 | notice: "Nice! {{resource_name}} was updated with success!" 8 | error: "Oh no! We could not update your address!" 9 | admin: 10 | actions: 11 | create: 12 | notice: "Admin notice message." 13 | error: "Admin error message." 14 | addresses: 15 | create: 16 | notice: "Admin, you created a new address close to {{reference}}." 17 | -------------------------------------------------------------------------------- /test/nested_belongs_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Country 5 | end 6 | 7 | class State 8 | end 9 | 10 | class City 11 | def self.human_name; 'City'; end 12 | end 13 | 14 | class CitiesController < InheritedResources::Base 15 | belongs_to :country, :state 16 | end 17 | 18 | class NestedBelongsToTest < ActionController::TestCase 19 | tests CitiesController 20 | 21 | def setup 22 | draw_routes do 23 | resources :cities 24 | end 25 | 26 | Country.expects(:find).with('13').returns(mock_country) 27 | mock_country.expects(:states).returns(State) 28 | State.expects(:find).with('37').returns(mock_state) 29 | mock_state.expects(:cities).returns(City) 30 | 31 | @controller.stubs(:collection_url).returns('/') 32 | end 33 | 34 | def teardown 35 | clear_routes 36 | end 37 | 38 | def test_assigns_country_and_state_and_city_on_index 39 | City.expects(:scoped).returns([mock_city]) 40 | get :index, params: { state_id: '37', country_id: '13' } 41 | 42 | assert_equal mock_country, assigns(:country) 43 | assert_equal mock_state, assigns(:state) 44 | assert_equal [mock_city], assigns(:cities) 45 | end 46 | 47 | def test_assigns_country_and_state_and_city_on_show 48 | City.expects(:find).with('42').returns(mock_city) 49 | get :show, params: { id: '42', state_id: '37', country_id: '13' } 50 | 51 | assert_equal mock_country, assigns(:country) 52 | assert_equal mock_state, assigns(:state) 53 | assert_equal mock_city, assigns(:city) 54 | end 55 | 56 | def test_assigns_country_and_state_and_city_on_new 57 | City.expects(:build).returns(mock_city) 58 | get :new, params: { state_id: '37', country_id: '13' } 59 | 60 | assert_equal mock_country, assigns(:country) 61 | assert_equal mock_state, assigns(:state) 62 | assert_equal mock_city, assigns(:city) 63 | end 64 | 65 | def test_assigns_country_and_state_and_city_on_edit 66 | City.expects(:find).with('42').returns(mock_city) 67 | get :edit, params: { id: '42', state_id: '37', country_id: '13' } 68 | 69 | assert_equal mock_country, assigns(:country) 70 | assert_equal mock_state, assigns(:state) 71 | assert_equal mock_city, assigns(:city) 72 | end 73 | 74 | def test_assigns_country_and_state_and_city_on_create 75 | City.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_city) 76 | mock_city.expects(:save).returns(true) 77 | post :create, params: { state_id: '37', country_id: '13', city: {these: 'params'} } 78 | 79 | assert_equal mock_country, assigns(:country) 80 | assert_equal mock_state, assigns(:state) 81 | assert_equal mock_city, assigns(:city) 82 | end 83 | 84 | def test_assigns_country_and_state_and_city_on_update 85 | City.expects(:find).with('42').returns(mock_city) 86 | mock_city.expects(:update).returns(true) 87 | put :update, params: { id: '42', state_id: '37', country_id: '13', city: {these: 'params'} } 88 | 89 | assert_equal mock_country, assigns(:country) 90 | assert_equal mock_state, assigns(:state) 91 | assert_equal mock_city, assigns(:city) 92 | end 93 | 94 | def test_assigns_country_and_state_and_city_on_destroy 95 | City.expects(:find).with('42').returns(mock_city) 96 | mock_city.expects(:destroy) 97 | delete :destroy, params: { id: '42', state_id: '37', country_id: '13' } 98 | 99 | assert_equal mock_country, assigns(:country) 100 | assert_equal mock_state, assigns(:state) 101 | assert_equal mock_city, assigns(:city) 102 | end 103 | 104 | protected 105 | 106 | def mock_country(stubs={}) 107 | @mock_country ||= mock(stubs) 108 | end 109 | 110 | def mock_state(stubs={}) 111 | @mock_state ||= mock(stubs) 112 | end 113 | 114 | def mock_city(stubs={}) 115 | @mock_city ||= mock(stubs) 116 | end 117 | 118 | def build_parameters(hash) 119 | ActionController::Parameters.new(hash) 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /test/nested_belongs_to_with_shallow_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Dresser 5 | end 6 | 7 | class Shelf 8 | end 9 | 10 | class Plate 11 | end 12 | 13 | class PlatesController < InheritedResources::Base 14 | belongs_to :dresser, :shelf, shallow: true 15 | end 16 | 17 | class NestedBelongsToWithShallowTest < ActionController::TestCase 18 | tests PlatesController 19 | 20 | def setup 21 | draw_routes do 22 | resources :plates 23 | end 24 | 25 | mock_shelf.expects(:dresser).returns(mock_dresser) 26 | mock_dresser.expects(:to_param).returns('13') 27 | 28 | Dresser.expects(:find).with('13').returns(mock_dresser) 29 | mock_dresser.expects(:shelves).returns(Shelf) 30 | mock_shelf.expects(:plates).returns(Plate) 31 | 32 | @controller.stubs(:collection_url).returns('/') 33 | end 34 | 35 | def teardown 36 | clear_routes 37 | end 38 | 39 | def test_assigns_dresser_and_shelf_and_plate_on_index 40 | Shelf.expects(:find).with('37').twice.returns(mock_shelf) 41 | Plate.expects(:scoped).returns([mock_plate]) 42 | get :index, params: { shelf_id: '37' } 43 | 44 | assert_equal mock_dresser, assigns(:dresser) 45 | assert_equal mock_shelf, assigns(:shelf) 46 | assert_equal [mock_plate], assigns(:plates) 47 | end 48 | 49 | def test_assigns_dresser_and_shelf_and_plate_on_show 50 | should_find_parents 51 | get :show, params: { id: '42' } 52 | 53 | assert_equal mock_dresser, assigns(:dresser) 54 | assert_equal mock_shelf, assigns(:shelf) 55 | assert_equal mock_plate, assigns(:plate) 56 | end 57 | 58 | def test_assigns_dresser_and_shelf_and_plate_on_new 59 | Plate.expects(:build).returns(mock_plate) 60 | Shelf.expects(:find).with('37').twice.returns(mock_shelf) 61 | get :new, params: { shelf_id: '37' } 62 | 63 | assert_equal mock_dresser, assigns(:dresser) 64 | assert_equal mock_shelf, assigns(:shelf) 65 | assert_equal mock_plate, assigns(:plate) 66 | end 67 | 68 | def test_assigns_dresser_and_shelf_and_plate_on_edit 69 | should_find_parents 70 | get :edit, params: { id: '42' } 71 | 72 | assert_equal mock_dresser, assigns(:dresser) 73 | assert_equal mock_shelf, assigns(:shelf) 74 | assert_equal mock_plate, assigns(:plate) 75 | end 76 | 77 | def test_assigns_dresser_and_shelf_and_plate_on_create 78 | Shelf.expects(:find).with('37').twice.returns(mock_shelf) 79 | 80 | Plate.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_plate) 81 | mock_plate.expects(:save).returns(true) 82 | post :create, params: { shelf_id: '37', plate: {these: 'params'} } 83 | 84 | assert_equal mock_dresser, assigns(:dresser) 85 | assert_equal mock_shelf, assigns(:shelf) 86 | assert_equal mock_plate, assigns(:plate) 87 | end 88 | 89 | def test_assigns_dresser_and_shelf_and_plate_on_update 90 | should_find_parents 91 | mock_plate.expects(:update).returns(true) 92 | put :update, params: { id: '42', plate: {these: 'params'} } 93 | 94 | assert_equal mock_dresser, assigns(:dresser) 95 | assert_equal mock_shelf, assigns(:shelf) 96 | assert_equal mock_plate, assigns(:plate) 97 | end 98 | 99 | def test_assigns_dresser_and_shelf_and_plate_on_destroy 100 | should_find_parents 101 | mock_plate.expects(:destroy) 102 | delete :destroy, params: { id: '42' } 103 | 104 | assert_equal mock_dresser, assigns(:dresser) 105 | assert_equal mock_shelf, assigns(:shelf) 106 | assert_equal mock_plate, assigns(:plate) 107 | end 108 | 109 | protected 110 | 111 | def should_find_parents 112 | Plate.expects(:find).with('42').returns(mock_plate) 113 | mock_plate.expects(:shelf).returns(mock_shelf) 114 | mock_shelf.expects(:to_param).returns('37') 115 | Plate.expects(:find).with('42').returns(mock_plate) 116 | Shelf.expects(:find).with('37').returns(mock_shelf) 117 | end 118 | 119 | def mock_dresser(stubs={}) 120 | @mock_dresser ||= mock(stubs) 121 | end 122 | 123 | def mock_shelf(stubs={}) 124 | @mock_shelf ||= mock(stubs) 125 | end 126 | 127 | def mock_plate(stubs={}) 128 | @mock_plate ||= mock(stubs) 129 | end 130 | 131 | def build_parameters(hash) 132 | ActionController::Parameters.new(hash) 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /test/nested_model_with_shallow_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Subfaculty 5 | end 6 | 7 | class Speciality 8 | end 9 | 10 | module Plan 11 | class Group 12 | end 13 | 14 | class Education 15 | end 16 | end 17 | 18 | class GroupsController < InheritedResources::Base 19 | defaults resource_class: Plan::Group, finder: :find_by_slug 20 | belongs_to :subfaculty, shallow: true do 21 | belongs_to :speciality 22 | end 23 | end 24 | 25 | class EducationsController < InheritedResources::Base 26 | defaults resource_class: Plan::Education 27 | belongs_to :subfaculty, shallow: true do 28 | belongs_to :speciality do 29 | belongs_to :group, parent_class: Plan::Group, 30 | instance_name: :plan_group, 31 | param: :group_id, 32 | finder: :find_by_slug 33 | end 34 | end 35 | end 36 | 37 | class NestedModelWithShallowTest < ActionController::TestCase 38 | tests GroupsController 39 | 40 | def setup 41 | draw_routes do 42 | resources :groups 43 | end 44 | 45 | mock_speciality.expects(:subfaculty).returns(mock_subfaculty) 46 | mock_subfaculty.expects(:to_param).returns('13') 47 | 48 | Subfaculty.expects(:find).with('13').returns(mock_subfaculty) 49 | mock_subfaculty.expects(:specialities).returns(Speciality) 50 | mock_speciality.expects(:groups).returns(Plan::Group) 51 | end 52 | 53 | def teardown 54 | clear_routes 55 | end 56 | 57 | def test_assigns_subfaculty_and_speciality_and_group_on_edit 58 | should_find_parents 59 | get :edit, params: { id: 'forty_two' } 60 | 61 | assert_equal mock_subfaculty, assigns(:subfaculty) 62 | assert_equal mock_speciality, assigns(:speciality) 63 | assert_equal mock_group, assigns(:group) 64 | end 65 | 66 | def test_expose_a_newly_create_group_with_speciality 67 | Speciality.expects(:find).with('37').twice.returns(mock_speciality) 68 | Plan::Group.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_group(save: true)) 69 | post :create, params: { speciality_id: '37', group: {'these' => 'params'} } 70 | 71 | assert_equal mock_group, assigns(:group) 72 | end 73 | 74 | def test_expose_a_update_group_with_speciality 75 | should_find_parents 76 | mock_group.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 77 | post :update, params: { id: 'forty_two', group: {'these' => 'params'} } 78 | 79 | assert_equal mock_group, assigns(:group) 80 | end 81 | 82 | protected 83 | 84 | def should_find_parents 85 | Plan::Group.expects(:find_by_slug).with('forty_two').returns(mock_group) 86 | mock_group.expects(:speciality).returns(mock_speciality) 87 | mock_speciality.expects(:to_param).returns('37') 88 | Plan::Group.expects(:find_by_slug).with('forty_two').returns(mock_group) 89 | Speciality.expects(:find).with('37').returns(mock_speciality) 90 | end 91 | 92 | def mock_group(stubs={}) 93 | @mock_group ||= mock(stubs) 94 | end 95 | 96 | def mock_speciality(stubs={}) 97 | @mock_speciality ||= mock(stubs) 98 | end 99 | 100 | def mock_subfaculty(stubs={}) 101 | @mock_subfaculty ||= mock(stubs) 102 | end 103 | 104 | def build_parameters(hash) 105 | ActionController::Parameters.new(hash) 106 | end 107 | end 108 | 109 | class TwoNestedModelWithShallowTest < ActionController::TestCase 110 | tests EducationsController 111 | 112 | def setup 113 | draw_routes do 114 | resources :educations 115 | end 116 | 117 | mock_speciality.expects(:subfaculty).returns(mock_subfaculty) 118 | mock_subfaculty.expects(:to_param).returns('13') 119 | Subfaculty.expects(:find).with('13').returns(mock_subfaculty) 120 | mock_subfaculty.expects(:specialities).returns(Speciality) 121 | mock_speciality.expects(:groups).returns(Plan::Group) 122 | end 123 | 124 | def teardown 125 | clear_routes 126 | end 127 | 128 | def test_assigns_subfaculty_and_speciality_and_group_on_new 129 | should_find_parents 130 | get :new, params: { group_id: 'forty_two' } 131 | 132 | assert_equal mock_subfaculty, assigns(:subfaculty) 133 | assert_equal mock_speciality, assigns(:speciality) 134 | assert_equal mock_group, assigns(:plan_group) 135 | assert_equal mock_education, assigns(:education) 136 | end 137 | 138 | protected 139 | 140 | def should_find_parents 141 | Plan::Group.expects(:find_by_slug).with('forty_two').returns(mock_group) 142 | mock_group.expects(:speciality).returns(mock_speciality) 143 | mock_group.expects(:educations).returns(mock_education) 144 | mock_education.expects(:build).returns(mock_education) 145 | mock_speciality.expects(:to_param).returns('37') 146 | Plan::Group.expects(:find_by_slug).with('forty_two').returns(mock_group) 147 | Speciality.expects(:find).with('37').returns(mock_speciality) 148 | end 149 | 150 | def mock_group(stubs={}) 151 | @mock_group ||= mock(stubs) 152 | end 153 | 154 | def mock_education(stubs={}) 155 | @mock_education ||= mock(stubs) 156 | end 157 | 158 | def mock_speciality(stubs={}) 159 | @mock_speciality ||= mock(stubs) 160 | end 161 | 162 | def mock_subfaculty(stubs={}) 163 | @mock_subfaculty ||= mock(stubs) 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /test/nested_singleton_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | # This test file is instead to test the how controller flow and actions 5 | # using a belongs_to association. This is done using mocks a la rspec. 6 | # 7 | class Party 8 | extend ActiveModel::Naming 9 | end 10 | 11 | class Venue 12 | extend ActiveModel::Naming 13 | end 14 | 15 | class Address 16 | extend ActiveModel::Naming 17 | end 18 | 19 | ActiveSupport::Inflector.inflections do |inflect| 20 | inflect.singular "address", "address" 21 | inflect.plural "address", "addresses" 22 | end 23 | 24 | class VenueController < InheritedResources::Base 25 | defaults singleton: true 26 | belongs_to :party 27 | end 28 | 29 | # for the slightly pathological 30 | # /party/37/venue/address case 31 | class AddressController < InheritedResources::Base 32 | defaults singleton: true 33 | belongs_to :party do 34 | belongs_to :venue, singleton: true 35 | end 36 | end 37 | 38 | #and the more pathological case 39 | class GeolocationController < InheritedResources::Base 40 | defaults singleton: true 41 | belongs_to :party do 42 | belongs_to :venue, singleton: true do 43 | belongs_to :address, singleton: true 44 | end 45 | end 46 | end 47 | 48 | class NestedSingletonTest < ActionController::TestCase 49 | tests AddressController 50 | 51 | def setup 52 | draw_routes do 53 | resources :party do 54 | resource :venue, controller: :venue do 55 | resource :address, controller: :address do 56 | resource :geolocation, controller: :geolocation 57 | end 58 | end 59 | end 60 | end 61 | end 62 | 63 | def teardown 64 | clear_routes 65 | end 66 | 67 | def test_does_not_break_parent_controller 68 | #this is kind of tacky, but seems to work 69 | old_controller = @controller 70 | @controller = VenueController.new 71 | Party.expects(:find).with('37').returns(mock_party) 72 | mock_party.expects(:venue).returns(mock_venue) 73 | get :show, params: { party_id: '37' } 74 | 75 | assert_equal mock_party, assigns(:party) 76 | assert_equal mock_venue, assigns(:venue) 77 | ensure 78 | @controller = old_controller 79 | end 80 | 81 | def test_does_not_break_child_controller 82 | #this is kind of tacky, but seems to work 83 | old_controller = @controller 84 | @controller = GeolocationController.new 85 | Party.expects(:find).with('37').returns(mock_party) 86 | mock_party.expects(:venue).returns(mock_venue) 87 | mock_venue.expects(:address).returns(mock_address) 88 | mock_address.expects(:geolocation).returns(mock_geolocation) 89 | get :show, params: { party_id: '37' } 90 | 91 | assert_equal mock_party, assigns(:party) 92 | assert_equal mock_venue, assigns(:venue) 93 | assert_equal mock_address, assigns(:address) 94 | assert_equal mock_geolocation, assigns(:geolocation) 95 | ensure 96 | @controller = old_controller 97 | end 98 | 99 | def test_expose_a_new_address_on_new 100 | Party.expects(:find).with('37').returns(mock_party) 101 | mock_party.expects(:venue).returns(mock_venue) 102 | mock_venue.expects(:build_address).returns(mock_address) 103 | get :new, params: { party_id: '37' } 104 | 105 | assert_equal mock_party, assigns(:party) 106 | assert_equal mock_venue, assigns(:venue) 107 | assert_equal mock_address, assigns(:address) 108 | end 109 | 110 | def test_expose_the_address_on_edit 111 | Party.expects(:find).with('37').returns(mock_party) 112 | mock_party.expects(:venue).returns(mock_venue) 113 | mock_venue.expects(:address).returns(mock_address) 114 | get :edit, params: { party_id: '37' } 115 | 116 | assert_equal mock_party, assigns(:party) 117 | assert_equal mock_venue, assigns(:venue) 118 | assert_equal mock_address, assigns(:address) 119 | assert_response :success 120 | end 121 | 122 | def test_expose_the_address_on_show 123 | Party.expects(:find).with('37').returns(mock_party) 124 | mock_party.expects(:venue).returns(mock_venue) 125 | mock_venue.expects(:address).returns(mock_address) 126 | get :show, params: { party_id: '37' } 127 | 128 | assert_equal mock_party, assigns(:party) 129 | assert_equal mock_venue, assigns(:venue) 130 | assert_equal mock_address, assigns(:address) 131 | assert_response :success 132 | end 133 | 134 | def test_expose_a_newly_create_address_on_create 135 | Party.expects(:find).with('37').returns(mock_party) 136 | mock_party.expects(:venue).returns(mock_venue) 137 | mock_venue.expects(:build_address).with(build_parameters({'these' => 'params'})).returns(mock_address(save: true)) 138 | post :create, params: { party_id: '37', address: {these: 'params'} } 139 | 140 | assert_equal mock_party, assigns(:party) 141 | assert_equal mock_venue, assigns(:venue) 142 | assert_equal mock_address, assigns(:address) 143 | end 144 | 145 | def test_update_the_requested_object_on_update 146 | Party.expects(:find).with('37').returns(mock_party) 147 | mock_party.expects(:venue).returns(mock_venue(address: mock_address)) 148 | mock_address.expects(:update).with(build_parameters({'these' => 'params'})).returns(mock_address(save: true)) 149 | post :update, params: { party_id: '37', address: {these: 'params'} } 150 | 151 | assert_equal mock_party, assigns(:party) 152 | assert_equal mock_venue, assigns(:venue) 153 | assert_equal mock_address, assigns(:address) 154 | end 155 | 156 | def test_the_requested_manager_is_destroyed_on_destroy 157 | Party.expects(:find).with('37').returns(mock_party) 158 | mock_party.expects(:venue).returns(mock_venue) 159 | mock_venue.expects(:address).returns(mock_address) 160 | @controller.expects(:parent_url).returns('http://test.host/') 161 | mock_address.expects(:destroy) 162 | delete :destroy, params: { party_id: '37' } 163 | 164 | assert_equal mock_party, assigns(:party) 165 | assert_equal mock_venue, assigns(:venue) 166 | assert_equal mock_address, assigns(:address) 167 | end 168 | 169 | protected 170 | 171 | def mock_party(stubs={}) 172 | @mock_party ||= mock('party',stubs) 173 | end 174 | 175 | def mock_venue(stubs={}) 176 | @mock_venue ||= mock('venue',stubs) 177 | end 178 | 179 | def mock_address(stubs={}) 180 | @mock_address ||= mock('address',stubs) 181 | end 182 | 183 | def mock_geolocation(stubs={}) 184 | @mock_geolocation ||= mock('geolocation', stubs) 185 | end 186 | 187 | def build_parameters(hash) 188 | ActionController::Parameters.new(hash) 189 | end 190 | end 191 | -------------------------------------------------------------------------------- /test/optional_belongs_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Brands; end 5 | class Category; end 6 | 7 | class Product 8 | def self.human_name; 'Product'; end 9 | end 10 | 11 | class ProductsController < InheritedResources::Base 12 | belongs_to :brand, :category, polymorphic: true, optional: true 13 | end 14 | 15 | class OptionalTest < ActionController::TestCase 16 | tests ProductsController 17 | 18 | def setup 19 | draw_routes do 20 | resources :products 21 | end 22 | 23 | @controller.stubs(:resource_url).returns('/') 24 | end 25 | 26 | def teardown 27 | clear_routes 28 | end 29 | 30 | def test_expose_all_products_as_instance_variable_with_category 31 | Category.expects(:find).with('37').returns(mock_category) 32 | mock_category.expects(:products).returns(Product) 33 | Product.expects(:scoped).returns([mock_product]) 34 | get :index, params: { category_id: '37' } 35 | 36 | assert_equal mock_category, assigns(:category) 37 | assert_equal [mock_product], assigns(:products) 38 | end 39 | 40 | def test_expose_all_products_as_instance_variable_without_category 41 | Product.expects(:scoped).returns([mock_product]) 42 | get :index 43 | 44 | assert_nil assigns(:category) 45 | assert_equal [mock_product], assigns(:products) 46 | end 47 | 48 | def test_expose_the_requested_product_with_category 49 | Category.expects(:find).with('37').returns(mock_category) 50 | mock_category.expects(:products).returns(Product) 51 | Product.expects(:find).with('42').returns(mock_product) 52 | get :show, params: { id: '42', category_id: '37' } 53 | 54 | assert_equal mock_category, assigns(:category) 55 | assert_equal mock_product, assigns(:product) 56 | end 57 | 58 | def test_expose_the_requested_product_without_category 59 | Product.expects(:find).with('42').returns(mock_product) 60 | get :show, params: { id: '42' } 61 | 62 | assert_nil assigns(:category) 63 | assert_equal mock_product, assigns(:product) 64 | end 65 | 66 | def test_expose_a_new_product_with_category 67 | Category.expects(:find).with('37').returns(mock_category) 68 | mock_category.expects(:products).returns(Product) 69 | Product.expects(:build).returns(mock_product) 70 | get :new, params: { category_id: '37' } 71 | 72 | assert_equal mock_category, assigns(:category) 73 | assert_equal mock_product, assigns(:product) 74 | end 75 | 76 | def test_expose_a_new_product_without_category 77 | Product.expects(:new).returns(mock_product) 78 | get :new 79 | 80 | assert_nil assigns(:category) 81 | assert_equal mock_product, assigns(:product) 82 | end 83 | 84 | def test_expose_the_requested_product_for_edition_with_category 85 | Category.expects(:find).with('37').returns(mock_category) 86 | mock_category.expects(:products).returns(Product) 87 | Product.expects(:find).with('42').returns(mock_product) 88 | get :edit, params: { id: '42', category_id: '37' } 89 | 90 | assert_equal mock_category, assigns(:category) 91 | assert_equal mock_product, assigns(:product) 92 | end 93 | 94 | def test_expose_the_requested_product_for_edition_without_category 95 | Product.expects(:find).with('42').returns(mock_product) 96 | get :edit, params: { id: '42' } 97 | 98 | assert_nil assigns(:category) 99 | assert_equal mock_product, assigns(:product) 100 | end 101 | 102 | def test_expose_a_newly_create_product_with_category 103 | Category.expects(:find).with('37').returns(mock_category) 104 | mock_category.expects(:products).returns(Product) 105 | Product.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_product(save: true)) 106 | post :create, params: { category_id: '37', product: {these: 'params'} } 107 | 108 | assert_equal mock_category, assigns(:category) 109 | assert_equal mock_product, assigns(:product) 110 | end 111 | 112 | def test_expose_a_newly_create_product_without_category 113 | Product.expects(:new).with(build_parameters({'these' => 'params'})).returns(mock_product(save: true)) 114 | post :create, params: { product: {these: 'params'} } 115 | 116 | assert_nil assigns(:category) 117 | assert_equal mock_product, assigns(:product) 118 | end 119 | 120 | def test_update_the_requested_object_with_category 121 | Category.expects(:find).with('37').returns(mock_category) 122 | mock_category.expects(:products).returns(Product) 123 | Product.expects(:find).with('42').returns(mock_product) 124 | mock_product.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 125 | 126 | put :update, params: { id: '42', category_id: '37', product: {these: 'params'} } 127 | 128 | assert_equal mock_category, assigns(:category) 129 | assert_equal mock_product, assigns(:product) 130 | end 131 | 132 | def test_update_the_requested_object_without_category 133 | Product.expects(:find).with('42').returns(mock_product) 134 | mock_product.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 135 | 136 | put :update, params: { id: '42', product: {these: 'params'} } 137 | 138 | assert_nil assigns(:category) 139 | assert_equal mock_product, assigns(:product) 140 | end 141 | 142 | def test_the_requested_product_is_destroyed_with_category 143 | Category.expects(:find).with('37').returns(mock_category) 144 | mock_category.expects(:products).returns(Product) 145 | Product.expects(:find).with('42').returns(mock_product) 146 | mock_product.expects(:destroy).returns(true) 147 | @controller.expects(:collection_url).returns('/') 148 | 149 | delete :destroy, params: { id: '42', category_id: '37' } 150 | 151 | assert_equal mock_category, assigns(:category) 152 | assert_equal mock_product, assigns(:product) 153 | end 154 | 155 | def test_the_requested_product_is_destroyed_without_category 156 | Product.expects(:find).with('42').returns(mock_product) 157 | mock_product.expects(:destroy).returns(true) 158 | @controller.expects(:collection_url).returns('/') 159 | 160 | delete :destroy, params: { id: '42' } 161 | 162 | assert_nil assigns(:category) 163 | assert_equal mock_product, assigns(:product) 164 | end 165 | 166 | def test_polymorphic_helpers 167 | Product.expects(:scoped).returns([mock_product]) 168 | get :index 169 | 170 | refute @controller.send(:parent?) 171 | assert_nil assigns(:parent_type) 172 | assert_nil @controller.send(:parent_type) 173 | assert_nil @controller.send(:parent_class) 174 | assert_nil assigns(:category) 175 | assert_nil @controller.send(:parent) 176 | end 177 | 178 | protected 179 | 180 | def mock_category(stubs={}) 181 | @mock_category ||= mock(stubs) 182 | end 183 | 184 | def mock_product(stubs={}) 185 | @mock_product ||= mock(stubs) 186 | end 187 | 188 | def build_parameters(hash) 189 | ActionController::Parameters.new(hash) 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /test/parent_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | def force_parent_controller(value) 5 | InheritedResources.send(:remove_const, :Base) 6 | InheritedResources.parent_controller = value 7 | load File.join(__dir__, '..', 'app', 'controllers', 'inherited_resources', 'base.rb') 8 | end 9 | 10 | class ParentControllerTest < ActionController::TestCase 11 | def test_setting_parent_controller 12 | original_parent = InheritedResources::Base.superclass 13 | 14 | assert_equal ApplicationController, original_parent 15 | 16 | force_parent_controller('ActionController::Base') 17 | 18 | assert_equal ActionController::Base, InheritedResources::Base.superclass 19 | ensure 20 | force_parent_controller(original_parent.to_s) # restore original parent 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/polymorphic_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Factory; end 5 | class Company; end 6 | class User; end 7 | class Photo; end 8 | 9 | class Employee 10 | def self.human_name; 'Employee'; end 11 | end 12 | 13 | class EmployeesController < InheritedResources::Base 14 | belongs_to :factory, :company, polymorphic: true 15 | end 16 | 17 | class PhotosController < InheritedResources::Base 18 | belongs_to :user, :task, polymorphic: true 19 | 20 | def index 21 | parent 22 | # Overwrite index 23 | end 24 | end 25 | 26 | class PolymorphicFactoriesTest < ActionController::TestCase 27 | tests EmployeesController 28 | 29 | def setup 30 | draw_routes do 31 | resources :employees 32 | end 33 | 34 | Factory.expects(:find).with('37').returns(mock_factory) 35 | mock_factory.expects(:employees).returns(Employee) 36 | 37 | @controller.stubs(:resource_url).returns('/') 38 | @controller.stubs(:collection_url).returns('/') 39 | end 40 | 41 | def teardown 42 | clear_routes 43 | end 44 | 45 | def test_expose_all_employees_as_instance_variable_on_index 46 | Employee.expects(:scoped).returns([mock_employee]) 47 | get :index, params: { factory_id: '37' } 48 | 49 | assert_equal mock_factory, assigns(:factory) 50 | assert_equal [mock_employee], assigns(:employees) 51 | end 52 | 53 | def test_expose_the_requested_employee_on_show 54 | Employee.expects(:find).with('42').returns(mock_employee) 55 | get :show, params: { id: '42', factory_id: '37' } 56 | 57 | assert_equal mock_factory, assigns(:factory) 58 | assert_equal mock_employee, assigns(:employee) 59 | end 60 | 61 | def test_expose_a_new_employee_on_new 62 | Employee.expects(:build).returns(mock_employee) 63 | get :new, params: { factory_id: '37' } 64 | 65 | assert_equal mock_factory, assigns(:factory) 66 | assert_equal mock_employee, assigns(:employee) 67 | end 68 | 69 | def test_expose_the_requested_employee_on_edit 70 | Employee.expects(:find).with('42').returns(mock_employee) 71 | get :edit, params: { id: '42', factory_id: '37' } 72 | 73 | assert_equal mock_factory, assigns(:factory) 74 | assert_equal mock_employee, assigns(:employee) 75 | assert_response :success 76 | end 77 | 78 | def test_expose_a_newly_create_employee_on_create 79 | Employee.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_employee(save: true)) 80 | post :create, params: { factory_id: '37', employee: {these: 'params'} } 81 | 82 | assert_equal mock_factory, assigns(:factory) 83 | assert_equal mock_employee, assigns(:employee) 84 | end 85 | 86 | def test_update_the_requested_object_on_update 87 | Employee.expects(:find).with('42').returns(mock_employee) 88 | mock_employee.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 89 | put :update, params: { id: '42', factory_id: '37', employee: {these: 'params'} } 90 | 91 | assert_equal mock_factory, assigns(:factory) 92 | assert_equal mock_employee, assigns(:employee) 93 | end 94 | 95 | def test_the_requested_employee_is_destroyed_on_destroy 96 | Employee.expects(:find).with('42').returns(mock_employee) 97 | mock_employee.expects(:destroy) 98 | delete :destroy, params: { id: '42', factory_id: '37' } 99 | 100 | assert_equal mock_factory, assigns(:factory) 101 | assert_equal mock_employee, assigns(:employee) 102 | end 103 | 104 | def test_polymorphic_helpers 105 | mock_factory.stubs(:class).returns(Factory) 106 | 107 | Employee.expects(:scoped).returns([mock_employee]) 108 | get :index, params: { factory_id: '37' } 109 | 110 | assert @controller.send(:parent?) 111 | assert_equal :factory, assigns(:parent_type) 112 | assert_equal :factory, @controller.send(:parent_type) 113 | assert_equal Factory, @controller.send(:parent_class) 114 | assert_equal mock_factory, assigns(:factory) 115 | assert_equal mock_factory, @controller.send(:parent) 116 | end 117 | 118 | protected 119 | 120 | def mock_factory(stubs={}) 121 | @mock_factory ||= mock(stubs) 122 | end 123 | 124 | def mock_employee(stubs={}) 125 | @mock_employee ||= mock(stubs) 126 | end 127 | 128 | def build_parameters(hash) 129 | ActionController::Parameters.new(hash) 130 | end 131 | end 132 | 133 | class PolymorphicCompanyTest < ActionController::TestCase 134 | tests EmployeesController 135 | 136 | def setup 137 | draw_routes do 138 | resources :employees 139 | end 140 | 141 | Company.expects(:find).with('37').returns(mock_company) 142 | mock_company.expects(:employees).returns(Employee) 143 | 144 | @controller.stubs(:resource_url).returns('/') 145 | @controller.stubs(:collection_url).returns('/') 146 | end 147 | 148 | def teardown 149 | clear_routes 150 | end 151 | 152 | def test_expose_all_employees_as_instance_variable_on_index 153 | Employee.expects(:scoped).returns([mock_employee]) 154 | get :index, params: { company_id: '37' } 155 | 156 | assert_equal mock_company, assigns(:company) 157 | assert_equal [mock_employee], assigns(:employees) 158 | end 159 | 160 | def test_expose_the_requested_employee_on_show 161 | Employee.expects(:find).with('42').returns(mock_employee) 162 | get :show, params: { id: '42', company_id: '37' } 163 | 164 | assert_equal mock_company, assigns(:company) 165 | assert_equal mock_employee, assigns(:employee) 166 | end 167 | 168 | def test_expose_a_new_employee_on_new 169 | Employee.expects(:build).returns(mock_employee) 170 | get :new, params: { company_id: '37' } 171 | 172 | assert_equal mock_company, assigns(:company) 173 | assert_equal mock_employee, assigns(:employee) 174 | end 175 | 176 | def test_expose_the_requested_employee_on_edit 177 | Employee.expects(:find).with('42').returns(mock_employee) 178 | get :edit, params: { id: '42', company_id: '37' } 179 | 180 | assert_equal mock_company, assigns(:company) 181 | assert_equal mock_employee, assigns(:employee) 182 | assert_response :success 183 | end 184 | 185 | def test_expose_a_newly_create_employee_on_create 186 | Employee.expects(:build).with(build_parameters({'these' => 'params'})).returns(mock_employee(save: true)) 187 | post :create, params: { company_id: '37', employee: {these: 'params'} } 188 | 189 | assert_equal mock_company, assigns(:company) 190 | assert_equal mock_employee, assigns(:employee) 191 | end 192 | 193 | def test_update_the_requested_object_on_update 194 | Employee.expects(:find).with('42').returns(mock_employee) 195 | mock_employee.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 196 | put :update, params: { id: '42', company_id: '37', employee: {these: 'params'} } 197 | 198 | assert_equal mock_company, assigns(:company) 199 | assert_equal mock_employee, assigns(:employee) 200 | end 201 | 202 | def test_the_requested_employee_is_destroyed_on_destroy 203 | Employee.expects(:find).with('42').returns(mock_employee) 204 | mock_employee.expects(:destroy) 205 | delete :destroy, params: { id: '42', company_id: '37' } 206 | 207 | assert_equal mock_company, assigns(:company) 208 | assert_equal mock_employee, assigns(:employee) 209 | end 210 | 211 | def test_polymorphic_helpers 212 | mock_company.stubs(:class).returns(Company) 213 | 214 | Employee.expects(:scoped).returns([mock_employee]) 215 | get :index, params: { company_id: '37' } 216 | 217 | assert @controller.send(:parent?) 218 | assert_equal :company, assigns(:parent_type) 219 | assert_equal :company, @controller.send(:parent_type) 220 | assert_equal Company, @controller.send(:parent_class) 221 | assert_equal mock_company, assigns(:company) 222 | assert_equal mock_company, @controller.send(:parent) 223 | end 224 | 225 | protected 226 | 227 | def mock_company(stubs={}) 228 | @mock_company ||= mock(stubs) 229 | end 230 | 231 | def mock_employee(stubs={}) 232 | @mock_employee ||= mock(stubs) 233 | end 234 | 235 | def build_parameters(hash) 236 | ActionController::Parameters.new(hash) 237 | end 238 | end 239 | 240 | class PolymorphicPhotosTest < ActionController::TestCase 241 | tests PhotosController 242 | 243 | def setup 244 | draw_routes do 245 | resources :photos 246 | end 247 | 248 | User.expects(:find).with('37').returns(mock_user) 249 | end 250 | 251 | def teardown 252 | clear_routes 253 | end 254 | 255 | def test_parent_as_instance_variable_on_index_when_method_overwritten 256 | get :index, params: { user_id: '37' } 257 | 258 | assert_equal mock_user, assigns(:user) 259 | end 260 | 261 | protected 262 | 263 | def mock_user(stubs={}) 264 | @mock_user ||= mock(stubs) 265 | end 266 | end 267 | -------------------------------------------------------------------------------- /test/redirect_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Machine; 5 | def self.human_name; 'Machine'; end 6 | end 7 | 8 | class MachinesController < InheritedResources::Base 9 | def create 10 | create!{ complex_url(:create, true, true) } 11 | end 12 | 13 | def update 14 | update!{ complex_url(:update, false, false) } 15 | end 16 | 17 | def destroy 18 | destroy!{ complex_url(:destroy, true, false) } 19 | end 20 | 21 | protected 22 | 23 | def complex_url(name, arg2, arg3) 24 | 'http://test.host/' + name.to_s 25 | end 26 | end 27 | 28 | class RedirectToWithBlockTest < ActionController::TestCase 29 | tests MachinesController 30 | 31 | def setup 32 | draw_routes do 33 | resources :machines 34 | end 35 | end 36 | 37 | def teardown 38 | clear_routes 39 | end 40 | 41 | def test_redirect_to_the_given_url_on_create 42 | Machine.stubs(:new).returns(mock_machine(save: true)) 43 | post :create 44 | 45 | assert_redirected_to 'http://test.host/create' 46 | end 47 | 48 | def test_redirect_to_the_given_url_on_update 49 | Machine.stubs(:find).returns(mock_machine(update: true)) 50 | put :update, params: { id: '42' } 51 | 52 | assert_redirected_to 'http://test.host/update' 53 | end 54 | 55 | def test_redirect_to_the_given_url_on_destroy 56 | Machine.stubs(:find).returns(mock_machine(destroy: true)) 57 | delete :destroy, params: { id: '42' } 58 | 59 | assert_redirected_to 'http://test.host/destroy' 60 | end 61 | 62 | protected 63 | 64 | def mock_machine(stubs={}) 65 | @mock_machine ||= mock(stubs) 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /test/singleton_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | # This test file is instead to test the how controller flow and actions 5 | # using a belongs_to association. This is done using mocks a la rspec. 6 | # 7 | class Store 8 | extend ActiveModel::Naming 9 | end 10 | 11 | class Manager 12 | extend ActiveModel::Naming 13 | end 14 | 15 | class ManagersController < InheritedResources::Base 16 | defaults singleton: true 17 | belongs_to :store 18 | end 19 | 20 | class SingletonTest < ActionController::TestCase 21 | tests ManagersController 22 | 23 | def setup 24 | draw_routes do 25 | resources :store do 26 | resource :manager 27 | end 28 | end 29 | end 30 | 31 | def teardown 32 | clear_routes 33 | end 34 | 35 | def test_expose_the_requested_manager_on_show 36 | Store.expects(:find).with('37').returns(mock_store) 37 | mock_store.expects(:manager).returns(mock_manager) 38 | get :show, params: { store_id: '37' } 39 | 40 | assert_equal mock_store, assigns(:store) 41 | assert_equal mock_manager, assigns(:manager) 42 | end 43 | 44 | def test_expose_a_new_manager_on_new 45 | Store.expects(:find).with('37').returns(mock_store) 46 | mock_store.expects(:build_manager).returns(mock_manager) 47 | get :new, params: { store_id: '37' } 48 | 49 | assert_equal mock_store, assigns(:store) 50 | assert_equal mock_manager, assigns(:manager) 51 | end 52 | 53 | def test_expose_the_requested_manager_on_edit 54 | Store.expects(:find).with('37').returns(mock_store) 55 | mock_store.expects(:manager).returns(mock_manager) 56 | get :edit, params: { store_id: '37' } 57 | 58 | assert_equal mock_store, assigns(:store) 59 | assert_equal mock_manager, assigns(:manager) 60 | assert_response :success 61 | end 62 | 63 | def test_expose_a_newly_create_manager_on_create 64 | Store.expects(:find).with('37').returns(mock_store) 65 | mock_store.expects(:build_manager).with(build_parameters({'these' => 'params'})).returns(mock_manager(save: true)) 66 | post :create, params: { store_id: '37', manager: {these: 'params'} } 67 | 68 | assert_equal mock_store, assigns(:store) 69 | assert_equal mock_manager, assigns(:manager) 70 | end 71 | 72 | def test_update_the_requested_object_on_update 73 | Store.expects(:find).with('37').returns(mock_store(manager: mock_manager)) 74 | mock_manager.expects(:update).with(build_parameters({'these' => 'params'})).returns(true) 75 | put :update, params: { store_id: '37', manager: {these: 'params'} } 76 | 77 | assert_equal mock_store, assigns(:store) 78 | assert_equal mock_manager, assigns(:manager) 79 | end 80 | 81 | def test_the_requested_manager_is_destroyed_on_destroy 82 | Store.expects(:find).with('37').returns(mock_store) 83 | mock_store.expects(:manager).returns(mock_manager) 84 | @controller.expects(:parent_url).returns('http://test.host/') 85 | mock_manager.expects(:destroy) 86 | delete :destroy, params: { store_id: '37' } 87 | 88 | assert_equal mock_store, assigns(:store) 89 | assert_equal mock_manager, assigns(:manager) 90 | end 91 | 92 | protected 93 | 94 | def mock_store(stubs={}) 95 | @mock_store ||= mock(stubs) 96 | end 97 | 98 | def mock_manager(stubs={}) 99 | @mock_manager ||= mock(stubs) 100 | end 101 | 102 | def build_parameters(hash) 103 | ActionController::Parameters.new(hash) 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /test/strong_parameters_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test_helper' 3 | 4 | class Widget 5 | extend ActiveModel::Naming 6 | include ActiveModel::Conversion 7 | end 8 | 9 | class WidgetsController < InheritedResources::Base 10 | end 11 | 12 | # test usage of `permitted_params` 13 | class StrongParametersTest < ActionController::TestCase 14 | def setup 15 | draw_routes do 16 | resources :widgets 17 | end 18 | 19 | @controller = WidgetsController.new 20 | @controller.stubs(:widget_url).returns("/") 21 | @controller.stubs(:permitted_params).returns(widget: {permitted: 'param'}) 22 | class << @controller 23 | private :permitted_params 24 | end 25 | end 26 | 27 | def teardown 28 | clear_routes 29 | end 30 | 31 | def test_permitted_params_from_new # rubocop:disable Minitest/NoAssertions 32 | Widget.expects(:new).with(permitted: 'param') 33 | get :new, params: { widget: { permitted: 'param', prohibited: 'param' } } 34 | end 35 | 36 | def test_permitted_params_from_create # rubocop:disable Minitest/NoAssertions 37 | Widget.expects(:new).with(permitted: 'param').returns(mock(save: true)) 38 | post :create, params: { widget: { permitted: 'param', prohibited: 'param' } } 39 | end 40 | 41 | def test_permitted_params_from_update # rubocop:disable Minitest/NoAssertions 42 | mock_widget = mock 43 | mock_widget.stubs(:class).returns(Widget) 44 | mock_widget.expects(:update).with(permitted: 'param') 45 | mock_widget.stubs(:persisted?).returns(true) 46 | mock_widget.stubs(:to_model).returns(mock_widget) 47 | mock_widget.stubs(:model_name).returns(Widget.model_name) 48 | Widget.expects(:find).with('42').returns(mock_widget) 49 | put :update, params: { id: '42', widget: {permitted: 'param', prohibited: 'param'} } 50 | end 51 | 52 | # `permitted_params` has greater priority than `widget_params` 53 | def test_with_permitted_and_resource_methods # rubocop:disable Minitest/NoAssertions 54 | @controller.stubs(:widget_params).returns(permitted: 'another_param') 55 | class << @controller 56 | private :widget_params 57 | end 58 | Widget.expects(:new).with(permitted: 'param') 59 | get :new, params: { widget: { permitted: 'param', prohibited: 'param' } } 60 | end 61 | end 62 | 63 | # test usage of `widget_params` 64 | class StrongParametersWithoutPermittedParamsTest < ActionController::TestCase 65 | def setup 66 | draw_routes do 67 | resources :widgets 68 | end 69 | 70 | @controller = WidgetsController.new 71 | @controller.stubs(:widget_url).returns("/") 72 | @controller.stubs(:widget_params).returns(permitted: 'param') 73 | class << @controller 74 | private :widget_params 75 | end 76 | end 77 | 78 | def teardown 79 | clear_routes 80 | end 81 | 82 | def test_permitted_params_from_new # rubocop:disable Minitest/NoAssertions 83 | Widget.expects(:new).with(permitted: 'param') 84 | get :new, params: { widget: { permitted: 'param', prohibited: 'param' } } 85 | end 86 | 87 | def test_permitted_params_from_create # rubocop:disable Minitest/NoAssertions 88 | Widget.expects(:new).with(permitted: 'param').returns(mock(save: true)) 89 | post :create, params: { widget: { permitted: 'param', prohibited: 'param' } } 90 | end 91 | 92 | def test_permitted_params_from_update # rubocop:disable Minitest/NoAssertions 93 | mock_widget = mock 94 | mock_widget.stubs(:class).returns(Widget) 95 | mock_widget.expects(:update).with(permitted: 'param') 96 | mock_widget.stubs(:persisted?).returns(true) 97 | mock_widget.stubs(:to_model).returns(mock_widget) 98 | mock_widget.stubs(:model_name).returns(Widget.model_name) 99 | Widget.expects(:find).with('42').returns(mock_widget) 100 | put :update, params: { id: '42', widget: {permitted: 'param', prohibited: 'param'} } 101 | end 102 | end 103 | 104 | # test usage of `widget_params` integrated with strong parameters (not using stubs) 105 | class StrongParametersIntegrationTest < ActionController::TestCase 106 | def setup 107 | draw_routes do 108 | resources :widgets 109 | end 110 | 111 | @controller = WidgetsController.new 112 | @controller.stubs(:widget_url).returns("/") 113 | 114 | class << @controller 115 | define_method :widget_params do 116 | params.require(:widget).permit(:permitted) 117 | end 118 | private :widget_params 119 | end 120 | end 121 | 122 | def teardown 123 | clear_routes 124 | end 125 | 126 | def test_permitted_empty_params_from_new # rubocop:disable Minitest/NoAssertions 127 | Widget.expects(:new).with({}) 128 | get :new, params: {} 129 | end 130 | 131 | def test_permitted_params_from_new # rubocop:disable Minitest/NoAssertions 132 | Widget.expects(:new).with(build_parameters({'permitted' => 'param'}).permit!) 133 | get :new, params: { widget: { permitted: 'param', prohibited: 'param' } } 134 | end 135 | 136 | def test_permitted_params_from_create # rubocop:disable Minitest/NoAssertions 137 | Widget.expects(:new).with(build_parameters({'permitted' => 'param'}).permit!).returns(mock(save: true)) 138 | post :create, params: { widget: { permitted: 'param', prohibited: 'param' } } 139 | end 140 | 141 | def test_permitted_params_from_update # rubocop:disable Minitest/NoAssertions 142 | mock_widget = mock 143 | mock_widget.stubs(:class).returns(Widget) 144 | mock_widget.expects(:update).with(build_parameters({'permitted' => 'param'}).permit!) 145 | mock_widget.stubs(:persisted?).returns(true) 146 | mock_widget.stubs(:to_model).returns(mock_widget) 147 | mock_widget.stubs(:model_name).returns(Widget.model_name) 148 | Widget.expects(:find).with('42').returns(mock_widget) 149 | put :update, params: { id: '42', widget: {permitted: 'param', prohibited: 'param'} } 150 | end 151 | 152 | protected 153 | 154 | def build_parameters(hash) 155 | ActionController::Parameters.new(hash) 156 | end 157 | end 158 | -------------------------------------------------------------------------------- /test/tasks/gemfile_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "minitest" 3 | 4 | class GemfilesTest < Minitest::Test 5 | def test_gemfile_is_up_to_date 6 | gemfile = ENV["BUNDLE_GEMFILE"] || "Gemfile" 7 | current_lockfile = File.read("#{gemfile}.lock") 8 | 9 | new_lockfile = Bundler.with_original_env do 10 | `bundle lock --print` 11 | end 12 | 13 | msg = "Please update #{gemfile}'s lock file with `BUNDLE_GEMFILE=#{gemfile} bundle install` and commit the result" 14 | 15 | assert_equal current_lockfile, new_lockfile, msg 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/tasks/gemspec_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "minitest" 3 | require "open3" 4 | require "inherited_resources/version" 5 | 6 | class GemspecTest < Minitest::Test 7 | def setup 8 | @build = Open3.capture3("gem build inherited_resources.gemspec") 9 | end 10 | 11 | def teardown 12 | File.delete("inherited_resources-#{InheritedResources::VERSION}.gem") 13 | end 14 | 15 | def test_succeeds 16 | assert_predicate @build[2], :success? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | if ENV.fetch('COVERAGE', false) 3 | require 'simplecov' 4 | require 'simplecov-cobertura' 5 | SimpleCov.start do 6 | add_filter %r{^/test/} 7 | minimum_coverage 98 8 | maximum_coverage_drop 0.2 9 | formatter SimpleCov::Formatter::CoberturaFormatter 10 | end 11 | end 12 | 13 | require 'rubygems' 14 | require 'bundler' 15 | 16 | Bundler.setup 17 | 18 | require 'minitest/autorun' 19 | require 'mocha/minitest' 20 | require 'minitest/autorun' 21 | require 'minitest/reporters' 22 | 23 | ENV["RAILS_ENV"] = "test" 24 | RAILS_ROOT = "anywhere" 25 | 26 | require "active_support" 27 | require "active_model" 28 | require "action_controller" 29 | 30 | # TODO: Remove warning gem and the following lines when freerange/mocha#593 will be fixed 31 | require "warning" 32 | Warning.ignore(/Mocha deprecation warning .+ expected keyword arguments .+ but received positional hash/) 33 | 34 | require 'rails-controller-testing' 35 | Rails::Controller::Testing.install 36 | 37 | I18n.load_path << File.join(File.dirname(__FILE__), 'locales', 'en.yml') 38 | I18n.reload! 39 | 40 | class ApplicationController < ActionController::Base; end 41 | 42 | # Add IR to load path and load the main file 43 | $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') 44 | require 'inherited_resources' 45 | 46 | ActionController::Base.view_paths = File.join(File.dirname(__FILE__), 'views') 47 | 48 | InheritedResources::Routes = ActionDispatch::Routing::RouteSet.new 49 | 50 | def draw_routes(&block) 51 | InheritedResources::Routes.draw(&block) 52 | end 53 | 54 | def clear_routes 55 | InheritedResources::Routes.draw { } 56 | end 57 | 58 | ActionController::Base.send :include, InheritedResources::Routes.url_helpers 59 | 60 | # Add app base to load path 61 | $:.unshift File.expand_path(File.dirname(__FILE__) + '/../app/controllers') 62 | require 'inherited_resources/base' 63 | 64 | class ActiveSupport::TestCase 65 | setup do 66 | @routes = InheritedResources::Routes 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /test/views/address/edit.html.erb: -------------------------------------------------------------------------------- 1 | edit html 2 | -------------------------------------------------------------------------------- /test/views/address/new.html.erb: -------------------------------------------------------------------------------- 1 | new html 2 | -------------------------------------------------------------------------------- /test/views/address/show.html.erb: -------------------------------------------------------------------------------- 1 | show html 2 | -------------------------------------------------------------------------------- /test/views/cars/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/cars/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/cars/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/cars/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/cities/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/cities/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/cities/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/cities/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/comments/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/comments/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/comments/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/comments/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/educations/new.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeadmin/inherited_resources/866d9fb29ef22826f608b8226302744ee5a6db3a/test/views/educations/new.html.erb -------------------------------------------------------------------------------- /test/views/employees/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/employees/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/employees/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/employees/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/geolocation/show.html.erb: -------------------------------------------------------------------------------- 1 | show html 2 | -------------------------------------------------------------------------------- /test/views/groups/edit.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeadmin/inherited_resources/866d9fb29ef22826f608b8226302744ee5a6db3a/test/views/groups/edit.html.erb -------------------------------------------------------------------------------- /test/views/managers/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/managers/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/managers/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/painters/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/painters/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/painters/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/painters/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/pets/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/pets/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/pets/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/pets/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/photos/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/plates/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/plates/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/plates/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/plates/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/products/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/products/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/products/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/products/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/professors/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/professors/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/professors/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/professors/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/projects/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML -------------------------------------------------------------------------------- /test/views/projects/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/projects/index.json.erb: -------------------------------------------------------------------------------- 1 | Index JSON 2 | -------------------------------------------------------------------------------- /test/views/projects/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML -------------------------------------------------------------------------------- /test/views/projects/respond_to_skip_default_template.html.erb: -------------------------------------------------------------------------------- 1 | DefaultTemplate HTML 2 | -------------------------------------------------------------------------------- /test/views/projects/respond_with_resource.html.erb: -------------------------------------------------------------------------------- 1 | RespondTo HTML 2 | -------------------------------------------------------------------------------- /test/views/projects/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML -------------------------------------------------------------------------------- /test/views/students/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/students/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/tags/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/tags/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/tags/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/tags/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/trees/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/trees/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/trees/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/trees/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/university/lecturers/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/university/lecturers/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/university/lecturers/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/university/lecturers/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/users/create.js.erb: -------------------------------------------------------------------------------- 1 | Create JS -------------------------------------------------------------------------------- /test/views/users/destroy.js.erb: -------------------------------------------------------------------------------- 1 | Destroy JS -------------------------------------------------------------------------------- /test/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | Edit HTML 2 | -------------------------------------------------------------------------------- /test/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 | Index HTML 2 | -------------------------------------------------------------------------------- /test/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | New HTML 2 | -------------------------------------------------------------------------------- /test/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 | Show HTML 2 | -------------------------------------------------------------------------------- /test/views/users/update.js.erb: -------------------------------------------------------------------------------- 1 | Update JS -------------------------------------------------------------------------------- /test/views/venue/show.html.erb: -------------------------------------------------------------------------------- 1 | show html 2 | -------------------------------------------------------------------------------- /test/views/widgets/new.html.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeadmin/inherited_resources/866d9fb29ef22826f608b8226302744ee5a6db3a/test/views/widgets/new.html.erb --------------------------------------------------------------------------------