Skip to content

Teach jsonapi:resource generator to respect namespaced models and controllers #141

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
70 changes: 40 additions & 30 deletions lib/generators/jsonapi/resource_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ class ResourceGenerator < ::Rails::Generators::NamedBase

argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"

class_option :'omit-comments',
type: :boolean,
default: false,
aliases: ['--omit-comments', '-c'],
desc: 'Generate without documentation comments'
class_option :'actions',
type: :array,
default: nil,
aliases: ['--actions', '-a'],
desc: 'Array of controller actions to support, e.g. "index show destroy"'
class_option :omit_comments,
type: :boolean,
default: true,
aliases: %w[-c],
desc: 'Generate without documentation comments'
class_option :actions,
type: :array,
default: nil,
aliases: %w[-a],
desc: 'Array of controller actions to support, e.g. "index show destroy"'

desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
def copy_resource_file
Expand Down Expand Up @@ -56,7 +56,7 @@ def generate_controller
end

def generate_serializer
to = File.join('app/serializers', class_path, "serializable_#{file_name}.rb")
to = File.join('app/serializers', class_path, "#{serializable_file_name}.rb")
template('serializer.rb.erb', to)
end

Expand All @@ -74,7 +74,7 @@ def docs_controller?
end

def generate_swagger
code = " jsonapi_resource '/v1/#{type}'"
code = " jsonapi_resource '/v1/#{url}'"
code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
code << "\n"
inject_into_file 'app/controllers/docs_controller.rb', before: /^end/ do
Expand All @@ -88,7 +88,7 @@ def generate_spec_payload
end

def generate_strong_resource
code = " strong_resource :#{file_name} do\n"
code = " strong_resource :#{singular_table_name} do\n"
attributes.each do |a|
type = a.type
type = :string if type == :text
Expand All @@ -106,44 +106,42 @@ def generate_route
code = " resources :#{type}"
code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
code << "\n"
inject_into_file 'config/routes.rb', after: "scope path: '/v1' do\n" do

unless type == url
code = code.gsub("resources :#{type}", "resources :#{file_name.pluralize}")
url.split('/')[0..-2].reverse.each do |namespace|
code = " namespace :#{namespace} do\n#{indent(code).chomp}\n end\n"
end
end

inject_into_file 'config/routes.rb', after: /scope path: (['"])\/v1(['"]) do\n/ do
code
end
end

def generate_tests
if actions?('index')
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"index_spec.rb"
to = File.join "spec/api/v1", url, "index_spec.rb"
template('index_request_spec.rb.erb', to)
end

if actions?('show')
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"show_spec.rb"
to = File.join "spec/api/v1", url, "show_spec.rb"
template('show_request_spec.rb.erb', to)
end

if actions?('create')
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"create_spec.rb"
to = File.join "spec/api/v1", url, "create_spec.rb"
template('create_request_spec.rb.erb', to)
end

if actions?('update')
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"update_spec.rb"
to = File.join "spec/api/v1", url, "update_spec.rb"
template('update_request_spec.rb.erb', to)
end

if actions?('destroy')
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"destroy_spec.rb"
to = File.join "spec/api/v1", url, "destroy_spec.rb"
template('destroy_request_spec.rb.erb', to)
end
end
Expand Down Expand Up @@ -187,12 +185,24 @@ def api_namespace
end
end

def serializable_file_name
"serializable_#{file_name}"
end

def serializable_class_name
(class_path + [serializable_file_name]).map!(&:camelize).join("::")
end

def model_klass
class_name.safe_constantize
end

def type
model_klass.name.underscore.pluralize
model_klass.model_name.plural
end

def url
model_klass.model_name.collection
end
end
end
2 changes: 1 addition & 1 deletion lib/generators/jsonapi/templates/controller.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class <%= model_klass.name.pluralize %>Controller < ApplicationController
# Reference a strong resource payload defined in
# config/initializers/strong_resources.rb
<%- end -%>
strong_resource :<%= file_name %>
strong_resource :<%= singular_table_name %>
<%- end -%>
<%- if actions?('create', 'update') -%>
<%- unless omit_comments? -%>
Expand Down
10 changes: 4 additions & 6 deletions lib/generators/jsonapi/templates/create_request_spec.rb.erb
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
require 'rails_helper'

RSpec.describe "<%= type %>#create", type: :request do
RSpec.describe "<%= url %>#create", type: :request do
subject(:make_request) do
jsonapi_post "/<%= api_namespace %>/v1/<%= type %>", payload
jsonapi_post "/<%= api_namespace %>/v1/<%= url %>", payload
end

describe 'basic create' do
let(:payload) do
{
data: {
type: '<%= type %>',
attributes: {
# ... your attrs here
}
attributes: attributes_for(:<%= singular_table_name %>)
}
}
end
Expand All @@ -23,7 +21,7 @@ RSpec.describe "<%= type %>#create", type: :request do
}.to change { <%= model_klass %>.count }.by(1)
<%= file_name %> = <%= model_klass %>.last

assert_payload(:<%= file_name %>, <%= file_name %>, json_item)
assert_payload(:<%= singular_table_name %>, <%= file_name %>, json_item)
end
end
end
6 changes: 3 additions & 3 deletions lib/generators/jsonapi/templates/destroy_request_spec.rb.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
require 'rails_helper'

RSpec.describe "<%= type %>#destroy", type: :request do
RSpec.describe "<%= url %>#destroy", type: :request do
subject(:make_request) do
jsonapi_delete "/<%= api_namespace %>/v1/<%= type %>/#{<%= file_name %>.id}"
jsonapi_delete "/<%= api_namespace %>/v1/<%= url %>/#{<%= singular_table_name %>.id}"
end

describe 'basic destroy' do
let!(:<%= file_name %>) { create(:<%= file_name %>) }
let!(:<%= singular_table_name %>) { create(:<%= singular_table_name %>) }

it 'updates the resource' do
expect {
Expand Down
14 changes: 7 additions & 7 deletions lib/generators/jsonapi/templates/index_request_spec.rb.erb
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
require 'rails_helper'

RSpec.describe "<%= file_name.pluralize %>#index", type: :request do
RSpec.describe "<%= url %>#index", type: :request do
let(:params) { {} }

subject(:make_request) do
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>",
jsonapi_get "/<%= api_namespace %>/v1/<%= url %>",
params: params
end

describe 'basic fetch' do
let!(:<%= file_name %>1) { create(:<%= file_name %>) }
let!(:<%= file_name %>2) { create(:<%= file_name %>) }
let!(:<%= singular_table_name %>1) { create(:<%= singular_table_name %>) }
let!(:<%= singular_table_name %>2) { create(:<%= singular_table_name %>) }

it 'serializes the list correctly' do
make_request
expect(json_ids(true)).to match_array([<%= file_name %>1.id, <%= file_name %>2.id])
assert_payload(:<%= file_name %>, <%= file_name %>1, json_items[0])
assert_payload(:<%= file_name %>, <%= file_name %>2, json_items[1])
expect(json_ids(true)).to match_array([<%= singular_table_name %>1.id, <%= singular_table_name %>2.id])
assert_payload(:<%= singular_table_name %>, <%= singular_table_name %>1, json_items[0])
assert_payload(:<%= singular_table_name %>, <%= singular_table_name %>2, json_items[1])
end
end
end
2 changes: 1 addition & 1 deletion lib/generators/jsonapi/templates/payload.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#
# For more information, see https://jsonapi-suite.github.io/jsonapi_spec_helpers/
<%- end -%>
JsonapiSpecHelpers::Payload.register(:<%= file_name %>) do
JsonapiSpecHelpers::Payload.register(:<%= singular_table_name %>) do
<%- attributes.each do |a| -%>
<%- type = a.type == :boolean ? [TrueClass, FalseClass] : a.type.to_s.classify -%>
<%- type = String if a.type == :text -%>
Expand Down
2 changes: 1 addition & 1 deletion lib/generators/jsonapi/templates/serializer.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# We use jsonapi-rb, which is similar to active_model_serializers.
<%- end -%>
<% module_namespacing do -%>
class Serializable<%= class_name %> < JSONAPI::Serializable::Resource
class <%= serializable_class_name %> < JSONAPI::Serializable::Resource
type :<%= type %>

<%- unless omit_comments? -%>
Expand Down
8 changes: 4 additions & 4 deletions lib/generators/jsonapi/templates/show_request_spec.rb.erb
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
require 'rails_helper'

RSpec.describe "<%= file_name.pluralize %>#show", type: :request do
RSpec.describe "<%= url %>#show", type: :request do
let(:params) { {} }

subject(:make_request) do
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>/#{<%= file_name %>.id}",
jsonapi_get "/<%= api_namespace %>/v1/<%= url %>/#{<%= singular_table_name %>.id}",
params: params
end

describe 'basic fetch' do
let!(:<%= file_name %>) { create(:<%= file_name %>) }
let!(:<%= singular_table_name %>) { create(:<%= singular_table_name %>) }

it 'serializes the resource correctly' do
make_request
assert_payload(:<%= file_name %>, <%= file_name %>, json_item)
assert_payload(:<%= singular_table_name %>, <%= singular_table_name %>, json_item)
end
end
end
18 changes: 8 additions & 10 deletions lib/generators/jsonapi/templates/update_request_spec.rb.erb
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
require 'rails_helper'

RSpec.describe "<%= type %>#update", type: :request do
RSpec.describe "<%= url %>#update", type: :request do
subject(:make_request) do
jsonapi_put "/<%= api_namespace %>/v1/<%= type %>/#{<%= file_name %>.id}", payload
jsonapi_put "/<%= api_namespace %>/v1/<%= url %>/#{<%= singular_table_name %>.id}", payload
end

describe 'basic update' do
let!(:<%= file_name %>) { create(:<%= file_name %>) }
let!(:<%= singular_table_name %>) { create(:<%= singular_table_name %>) }

let(:payload) do
{
data: {
id: <%= file_name %>.id.to_s,
id: <%= singular_table_name %>.id.to_s,
type: '<%= type %>',
attributes: {
# ... your attrs here
}
attributes: attributes_for(:<%= singular_table_name %>)
}
}
end

# Replace 'xit' with 'it' after adding attributes
xit 'updates the resource' do
it 'updates the resource' do
expect {
make_request
}.to change { <%= file_name %>.reload.attributes }
assert_payload(:<%= file_name %>, <%= file_name %>, json_item)
}.to change { <%= singular_table_name %>.reload.attributes }
assert_payload(:<%= singular_table_name %>, <%= singular_table_name %>, json_item)

# ... assert updates attributes ...
end
Expand Down
2 changes: 1 addition & 1 deletion lib/jsonapi_compliable/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def jsonapi(foo = 'bar', resource: nil, &blk)
# #
# # ...will only sideload comments
#
# @param [Hash, Array, Symbol] whitelist
# @param hash [Hash, Array, Symbol]
# @see Query#include_hash
def sideload_whitelist(hash)
self._sideload_whitelist = JSONAPI::IncludeDirective.new(hash).to_hash
Expand Down