Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow resource_name to be inherited #872

Merged
merged 3 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 38 additions & 37 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Rails:

AllCops:
NewCops: enable
TargetRubyVersion: 2.6

Layout/LineLength:
Max: 140
Expand Down Expand Up @@ -69,43 +70,43 @@ Style/HashSyntax:

Metrics/ParameterLists:
Exclude:
- 'lib/apipie/generator/swagger/context.rb'
- "lib/apipie/generator/swagger/context.rb"

Style/Documentation:
Exclude:
- 'app/controllers/apipie/apipies_controller.rb'
- 'app/helpers/apipie_helper.rb'
- 'lib/apipie/apipie_module.rb'
- 'lib/apipie/application.rb'
- 'lib/apipie/configuration.rb'
- 'lib/apipie/core_ext/route.rb'
- 'lib/apipie/dsl_definition.rb'
- 'lib/apipie/error_description.rb'
- 'lib/apipie/errors.rb'
- 'lib/apipie/extractor.rb'
- 'lib/apipie/extractor/collector.rb'
- 'lib/apipie/extractor/recorder.rb'
- 'lib/apipie/extractor/writer.rb'
- 'lib/apipie/generator/generator.rb'
- 'lib/apipie/generator/swagger/**/*'
- 'lib/apipie/helpers.rb'
- 'lib/apipie/markup.rb'
- 'lib/apipie/method_description.rb'
- 'lib/apipie/method_description/api.rb'
- 'lib/apipie/middleware/checksum_in_headers.rb'
- 'lib/apipie/railtie.rb'
- 'lib/apipie/response_description.rb'
- 'lib/apipie/response_description_adapter.rb'
- 'lib/apipie/routes_formatter.rb'
- 'lib/apipie/routing.rb'
- 'lib/apipie/rspec/response_validation_helper.rb'
- 'lib/apipie/swagger_generator.rb'
- 'lib/apipie/see_description.rb'
- 'lib/apipie/static_dispatcher.rb'
- 'lib/apipie/tag_list_description.rb'
- 'lib/apipie/validator.rb'
- 'lib/generators/apipie/install/install_generator.rb'
- 'lib/generators/apipie/views_generator.rb'
- "app/controllers/apipie/apipies_controller.rb"
- "app/helpers/apipie_helper.rb"
- "lib/apipie/apipie_module.rb"
- "lib/apipie/application.rb"
- "lib/apipie/configuration.rb"
- "lib/apipie/core_ext/route.rb"
- "lib/apipie/dsl_definition.rb"
- "lib/apipie/error_description.rb"
- "lib/apipie/errors.rb"
- "lib/apipie/extractor.rb"
- "lib/apipie/extractor/collector.rb"
- "lib/apipie/extractor/recorder.rb"
- "lib/apipie/extractor/writer.rb"
- "lib/apipie/generator/generator.rb"
- "lib/apipie/generator/swagger/**/*"
- "lib/apipie/helpers.rb"
- "lib/apipie/markup.rb"
- "lib/apipie/method_description.rb"
- "lib/apipie/method_description/api.rb"
- "lib/apipie/middleware/checksum_in_headers.rb"
- "lib/apipie/railtie.rb"
- "lib/apipie/response_description.rb"
- "lib/apipie/response_description_adapter.rb"
- "lib/apipie/routes_formatter.rb"
- "lib/apipie/routing.rb"
- "lib/apipie/rspec/response_validation_helper.rb"
- "lib/apipie/swagger_generator.rb"
- "lib/apipie/see_description.rb"
- "lib/apipie/static_dispatcher.rb"
- "lib/apipie/tag_list_description.rb"
- "lib/apipie/validator.rb"
- "lib/generators/apipie/install/install_generator.rb"
- "lib/generators/apipie/views_generator.rb"
- spec/support/custom_bool_validator.rb
- spec/lib/validators/array_validator_spec.rb
- spec/dummy/**/*.rb
Expand All @@ -122,6 +123,6 @@ Naming/BlockForwarding:

Lint/MissingSuper:
Exclude:
- 'lib/apipie/errors.rb'
- 'lib/apipie/response_description_adapter.rb'
- 'lib/apipie/validator.rb'
- "lib/apipie/errors.rb"
- "lib/apipie/response_description_adapter.rb"
- "lib/apipie/validator.rb"
8 changes: 7 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ resource_id
name
Human readable name of resource. By default ``class.name.humanize`` is used.

- Can be specified as a proc, which will receive the controller class as an argument.
- Can be a symbol, which will be sent to the controller class to get the name.
- Can be a string, which will be used as is.

short (also short_description)
Short description of the resource (it's shown on both the list of resources, and resource details)

Expand Down Expand Up @@ -1180,7 +1184,7 @@ Here is an example of how to rescue and process a +ParamMissing+ or
+ParamInvalid+ error from within the ApplicationController.

.. code:: ruby

class ApplicationController < ActionController::Base

# ParamError is superclass of ParamMissing, ParamInvalid
Expand Down Expand Up @@ -1928,11 +1932,13 @@ And if you write one on your own, don't hesitate to share it with us!
Since this gem does not have a Gemfile, you need to specify it in your shell with:

.. code:: shell

BUNDLE_GEMFILE='gemfiles/Gemfile.rails61'

Then, you can install dependencies and run the test suite:

.. code:: shell

> bundle install
> bundle exec rspec

Expand Down
23 changes: 17 additions & 6 deletions lib/apipie/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,26 @@ def define_resource_description(controller, version, dsl_data = nil)
# resource_description? It's used to derivate the default value of
# versions for methods.
def controller_versions(controller)
ret = @controller_versions[controller.to_s]
return ret unless ret.empty?
if controller == ActionController::Base || controller.nil?
return [Apipie.configuration.default_version]
else
return controller_versions(controller.to_s.constantize.superclass)
value_from_parents(controller, default: [Apipie.configuration.default_version]) do |c|
ret = @controller_versions[c.to_s]
ret unless ret.empty?
end
end

# Recursively walks up the controller hierarchy looking for a value
# from the block.
# Stops at ActionController::Base.
# @param [Class] controller controller to start from
# @param [Array] args arguments passed to the block
# @param [Object] default default value to return if no value is found
# @param [Proc] block block to call with controller and args
def value_from_parents(controller, *args, default: nil, &block)
return default if controller == ActionController::Base || controller == AbstractController::Base || controller.nil?

thing = yield(controller, *args)
thing || value_from_parents(controller.superclass, *args, default: default, &block)
end

def set_controller_versions(controller, versions)
@controller_versions[controller.to_s] = versions
end
Expand Down
27 changes: 25 additions & 2 deletions lib/apipie/resource_description.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def initialize(controller, id, dsl_data = nil, version = nil)
@controller = controller
@_id = id
@_version = version || Apipie.configuration.default_version
@_parent = Apipie.get_resource_description(controller.superclass, version)
@_parent = Apipie.value_from_parents(controller.superclass, version) do |parent, ver|
Apipie.get_resource_description(parent, ver)
end

update_from_dsl_data(dsl_data) if dsl_data
end
Expand Down Expand Up @@ -60,7 +62,16 @@ def _api_base_url
end

def name
@name ||= @_resource_name.presence || @_id.split('-').map(&:capitalize).join('::')
@name ||= case resource_name
when Proc
resource_name.call(controller)
when Symbol
controller.public_send(resource_name)
when String
resource_name
else
default_name
end
end
alias _name name

Expand Down Expand Up @@ -123,5 +134,17 @@ def to_json(method_name = nil, lang = nil)
}
end

protected

def resource_name
@_resource_name.presence || @_parent&.resource_name
end

private

def default_name
@_id.split('-').map(&:capitalize).join('::')
end

end
end
13 changes: 10 additions & 3 deletions spec/controllers/api/v2/architectures_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
require 'spec_helper'

describe Api::V2::ArchitecturesController do
let(:resource_description) { Apipie.get_resource_description(described_class, "2.0") }

describe "resource description" do
subject { Apipie.get_resource_description(Api::V2::ArchitecturesController, "2.0") }
describe 'version' do
subject { resource_description._version }

it "should be version 2.0" do
expect(subject._version).to eq('2.0')
it { is_expected.to eq('2.0') }
end

describe 'name' do
subject { resource_description.name }

it { is_expected.to eq('Architectures') }
end
end
end
16 changes: 16 additions & 0 deletions spec/controllers/api/v2/nested/resources_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
require 'spec_helper'

describe Api::V2::Nested::ResourcesController do
let(:resource_description) { Apipie.get_resource_description(described_class, "2.0") }

describe "resource description" do
describe 'version' do
subject { resource_description._version }

it { is_expected.to eq('2.0') }
end

describe 'name' do
subject { resource_description.name }

it { is_expected.to eq('Rsrcs') }
end
end

describe '.get_resource_id' do
subject { Apipie.get_resource_id(Api::V2::Nested::ResourcesController) }

Expand Down
19 changes: 19 additions & 0 deletions spec/controllers/api/v2/sub/footguns_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'spec_helper'

describe Api::V2::Sub::FootgunsController do
let(:resource_description) { Apipie.get_resource_description(described_class, '2.0') }

describe 'resource description' do
describe 'version' do
subject { resource_description._version }

it { is_expected.to eq('2.0') }
end

describe 'name' do
subject { resource_description.name }

it { is_expected.to eq('snugtooF') }
end
end
end
6 changes: 6 additions & 0 deletions spec/dummy/app/controllers/api/v2/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ class BaseController < Api::BaseController
api_version '2.0'
app_info 'Version 2.0 description'
api_base_url '/api/v2'

name :reversed_name
end

def self.reversed_name
controller_name.capitalize.reverse
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/dummy/app/controllers/api/v2/empty_middle_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Api
module V2
class EmptyMiddleController < V2::BaseController
# This is an empty controller, used to test cases where controllers
# may inherit from a middle controler that does not define a resource_description,
# but the middle controller's parent does.
end
end
end
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Api
module V2
class Nested::ResourcesController < V2::BaseController
resource_description do
name 'Resources'
resource_description do
name ->(controller) { controller.controller_name.delete('aeiou').capitalize }
resource_id "resource"
end
api :GET, "/nested/resources/", "List all nested resources."
Expand Down
30 changes: 30 additions & 0 deletions spec/dummy/app/controllers/api/v2/sub/footguns_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Api
module V2
module Sub
class FootgunsController < V2::EmptyMiddleController
resource_description do
short 'Footguns are bad'
end

api :GET, '/footguns/', 'List all footguns.'
def index; end

api :GET, '/footguns/:id/', 'Show a footgun.'
def show; end

api :POST, '/footguns/', 'Create a footgun.'
def create; end

api :PUT, '/footguns/:id/', 'Update a footgun.'
param :footgun, Hash, :required => true do
param :name, String
end
def update; end

api! 'Delete a footgun.'
api_version '2.0' # forces removal of the method description
def destroy; end
end
end
end
end