Skip to content
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
197 changes: 139 additions & 58 deletions lib/generators/jsonapi/resource_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,85 +2,166 @@ module Jsonapi
class ResourceGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)

class_option :'no-controller', type: :boolean, default: false
class_option :'no-serializer', type: :boolean, default: false
class_option :'no-payload', type: :boolean, default: false
class_option :'no-strong-resources', type: :boolean, default: false
class_option :'no-test', type: :boolean, default: false

desc "This generator creates a resource file at app/resources"
class_option :'omit-comments',
type: :boolean,
default: false,
aliases: ['--omit-comments', '-c'],
desc: 'Generate without documentation comments'
class_option :'omit-controller',
type: :boolean,
default: false,
aliases: ['--omit-controller'],
desc: 'Generate without controller'
class_option :'omit-serializer',
type: :boolean,
default: false,
aliases: ['--omit-serializer', '-s'],
desc: 'Generate without serializer'
class_option :'omit-payload',
type: :boolean,
default: false,
aliases: ['--omit-payload', '-p'],
desc: 'Generate without spec payload'
class_option :'omit-strong-resource',
type: :boolean,
default: false,
aliases: ['--omit-strong-resource', '-r'],
desc: 'Generate without strong resource'
class_option :'omit-route',
type: :boolean,
default: false,
aliases: ['--omit-route'],
desc: 'Generate without specs'
class_option :'omit-tests',
type: :boolean,
default: false,
aliases: ['--omit-tests', '-t'],
desc: 'Generate without specs'

desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
def copy_resource_file
unless @options['no-controller']
to = File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
template('controller.rb.erb', to)
unless model_klass
raise "You must define a #{class_name} model before generating the corresponding resource."
end

unless @options['no-serializer']
to = File.join('app/serializers', class_path, "serializable_#{file_name}.rb")
template('serializer.rb.erb', to)
end
generate_controller unless omit_controller?
generate_serializer unless omit_serializer?
generate_application_resource unless application_resource_defined?
generate_spec_payload unless omit_spec_payload?
generate_strong_resource unless omit_strong_resource?
generate_route unless omit_route?
generate_tests unless omit_tests?
generate_resource
end

unless 'ApplicationResource'.safe_constantize
to = File.join('app/resources', class_path, "application_resource.rb")
template('application_resource.rb.erb', to)
end
private

unless @options['no-payload']
to = File.join('spec/payloads', class_path, "#{file_name}.rb")
template('payload.rb.erb', to)
end
def omit_comments?
@options['omit-comments']
end

def generate_controller
to = File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
template('controller.rb.erb', to)
end

unless @options['no-strong-resources']
inject_into_file 'config/initializers/strong_resources.rb', after: "StrongResources.configure do\n" do <<-STR
def omit_controller?
@options['omit-controller']
end

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

def omit_serializer?
@options['omit-serializer']
end

def generate_application_resource
to = File.join('app/resources', class_path, "application_resource.rb")
template('application_resource.rb.erb', to)
end

def application_resource_defined?
'ApplicationResource'.safe_constantize.present?
end

def generate_spec_payload
to = File.join('spec/payloads', class_path, "#{file_name}.rb")
template('payload.rb.erb', to)
end

def omit_spec_payload?
@options['no-payload']
end

def generate_strong_resource
code = <<-STR
strong_resource :#{file_name} do
# Your attributes go here, e.g.
# attribute :name, :string
end

STR
end
STR
inject_into_file 'config/initializers/strong_resources.rb', after: "StrongResources.configure do\n" do
code
end
end

def omit_strong_resource?
@options['no-strong-resources']
end

unless @options['no-route']
inject_into_file 'config/routes.rb', after: "scope '/api' do\n scope '/v1' do\n" do <<-STR
def generate_route
code = <<-STR
resources :#{type}
STR
end
STR
inject_into_file 'config/routes.rb', after: "scope path: '/api' do\n scope path: '/v1' do\n" do
code
end
end

unless @options['no-test']
to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"index_spec.rb"
template('index_request_spec.rb.erb', to)

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

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

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

to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"destroy_spec.rb"
template('destroy_request_spec.rb.erb', to)
end
def omit_route?
@options['no-route']
end

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

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

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

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

to = File.join "spec/api/v1/#{file_name.pluralize}",
class_path,
"destroy_spec.rb"
template('destroy_request_spec.rb.erb', to)
end

def omit_tests?
@options['no-test']
end

def generate_resource
to = File.join('app/resources', class_path, "#{file_name}_resource.rb")
template('resource.rb.erb', to)
end

private

def model_klass
class_name.safe_constantize
end
Expand Down
13 changes: 11 additions & 2 deletions lib/generators/jsonapi/templates/application_resource.rb.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
require 'jsonapi_compliable/adapters/active_record'

<%- unless omit_comments? -%>
# ApplicationResource is similar to ApplicationRecord - a base class that
# holds configuration/methods for subclasses.
# All Resources should inherit from ApplicationResource.
# Resource documentation: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Resource.html
<%- end -%>
class ApplicationResource < JsonapiCompliable::Resource
<%- unless omit_comments? -%>
# Use the ActiveRecord Adapter for all subclasses.
# Subclasses can still override this default.
# More on adapters: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Adapters/Abstract.html
<%- end -%>
use_adapter JsonapiCompliable::Adapters::ActiveRecord
end
34 changes: 34 additions & 0 deletions lib/generators/jsonapi/templates/controller.rb.erb
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
<% module_namespacing do -%>
class <%= model_klass.name.pluralize %>Controller < ApplicationController
<%- unless omit_comments? -%>
# Mark this as a JSONAPI controller, associating with the given resource
<%- end -%>
jsonapi resource: <%= model_klass %>Resource

<%- unless omit_comments? -%>
# Reference a strong resource payload defined in
# config/initializers/strong_resources.rb
<%- end -%>
strong_resource :<%= file_name %>

<%- unless omit_comments? -%>
# Run strong parameter validation for these actions.
# Invalid keys will be dropped.
# Invalid value types will log or raise based on the configuration
# ActionController::Parameters.action_on_invalid_parameters
<%- end -%>
before_action :apply_strong_params, only: [:create, :update]

<%- unless omit_comments? -%>
# Start with a base scope and pass to render_jsonapi
<%- end -%>
def index
<%= file_name.pluralize %> = <%= model_klass %>.all
render_jsonapi(<%= file_name.pluralize %>)
end

<%- unless omit_comments? -%>
# Call jsonapi_scope directly here so we can get behavior like
# sparse fieldsets and statistics.
<%- end -%>
def show
scope = jsonapi_scope(<%= model_klass %>.where(id: params[:id]))
render_jsonapi(scope.resolve.first, scope: false)
end

<%- unless omit_comments? -%>
# jsonapi_create will use the configured Resource (and adapter) to persist.
# This will handle nested relationships as well.
# On validation errors, render correct error JSON.
<%- end -%>
def create
<%= file_name %>, success = jsonapi_create.to_a

Expand All @@ -26,6 +51,11 @@ class <%= model_klass.name.pluralize %>Controller < ApplicationController
end
end

<%- unless omit_comments? -%>
# jsonapi_update will use the configured Resource (and adapter) to persist.
# This will handle nested relationships as well.
# On validation errors, render correct error JSON.
<%- end -%>
def update
<%= file_name %>, success = jsonapi_update.to_a

Expand All @@ -36,6 +66,10 @@ class <%= model_klass.name.pluralize %>Controller < ApplicationController
end
end

<%- unless omit_comments? -%>
# No need for any special logic here as no_content is jsonapi_compliant.
# Customize this if you have a more complex use case.
<%- end -%>
def destroy
<%= file_name %> = <%= model_klass %>.find(params[:id])
<%= file_name %>.destroy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require 'rails_helper'

RSpec.describe "<%= type %>#destroy", type: :request do
context 'basic destroy' do
let!(:<%= file_name %>) { create(:<%= file_name %>) }
let!(:<%= file_name %>) { FactoryGirl.create(:<%= file_name %>) }

it 'updates the resource' do
expect {
Expand Down
31 changes: 31 additions & 0 deletions lib/generators/jsonapi/templates/payload.rb.erb
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
<%- unless omit_comments? -%>
# Register a payload to validate against.
# Add expected attributes within this block, e.g.:
#
# key(:name)
#
# Optionally validate the type as well:
#
# key(:name, String)
#
# This will:
#
# * Compare record.name == json['name']
# * Ensure no extra keys are in the json payload
# * Ensure no values are nil (unless allow_nil: true is passed)
# * Ensures json['name'] is a string
#
# If you have custom serialization logic and want to compare against
# something other than "record.name", pass a block:
#
# key(:name) { |record| record.name.upcase }
#
# Or, if this is a one-off for a particular spec, do that customization at
# runtime:
#
# assert_payload(:person, person_record, json_item) do
# key(:name) { 'Homer Simpson' }
# end
#
# For more information, see https://jsonapi-suite.github.io/jsonapi_spec_helpers/
<%- end -%>
JsonapiSpecHelpers::Payload.register(:<%= file_name %>) do
end
Loading