Commit e03dbf7f by liyijie

Add simple controller generate feature

parent bf684a51
...@@ -37,6 +37,7 @@ GEM ...@@ -37,6 +37,7 @@ GEM
builder (3.2.3) builder (3.2.3)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
crass (1.0.3) crass (1.0.3)
diff-lcs (1.3)
erubi (1.7.0) erubi (1.7.0)
has_scope (0.7.1) has_scope (0.7.1)
actionpack (>= 4.1, < 5.2) actionpack (>= 4.1, < 5.2)
...@@ -82,6 +83,19 @@ GEM ...@@ -82,6 +83,19 @@ GEM
responders (2.4.0) responders (2.4.0)
actionpack (>= 4.2.0, < 5.3) actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.0)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.0)
thor (0.20.0) thor (0.20.0)
thread_safe (0.3.6) thread_safe (0.3.6)
tzinfo (1.2.4) tzinfo (1.2.4)
...@@ -94,6 +108,7 @@ PLATFORMS ...@@ -94,6 +108,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
bundler (~> 1.16) bundler (~> 1.16)
rake (~> 10.0) rake (~> 10.0)
rspec
simple_controller! simple_controller!
BUNDLED WITH BUNDLED WITH
......
class SimpleController::Base < ::InheritedResources::Base
respond_to :json
def index
index!( { template: "#{self.class.view_class_path}/index" } )
end
def show
show!( { template: "#{self.class.view_class_path}/show" } )
end
def create
create!( { template: "#{self.class.view_class_path}/show", status: 201 } )
end
protected
def self.view_path path
@view_class_path = path
end
def self.view_class_path
@view_class_path
end
def collection
get_collection_ivar || set_collection_ivar(end_of_association_chain.ransack(params[:q]).result.paginate(page: params[:page], per_page: params[:per_page]))
end
end
Description:
Explain the generator
Example:
rails generate simple_controller Thing
This will create:
what/will/it/create
class SimpleControllerGenerator < Rails::Generators::NamedBase
include Rails::Generators::ResourceHelpers
source_root File.expand_path('../templates', __FILE__)
class_option :view, type: :string, desc: "View files generate folder"
class_option :model, type: :string, desc: "Model name for extract attributes"
class_option :auth, type: :string, desc: "Authentication model name"
class_option 'no-swagger', type: :boolean, desc: "Do not generate swagger spec file"
def setup
@routes = RSpec::Rails::Swagger::RouteParser.new(controller_path).routes
end
def create_controller_files
template_file = "controllers/controller.rb"
template template_file, File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
end
def copy_view_files
%w(index show _single _simple _detail).each do |view|
filename = filename_with_extensions(view)
template "views/#{filename}", File.join('app/views', view_class_path, filename)
end
end
def create_swagger_files
return if options["no-swagger"]
template_file = "specs/spec.rb"
template template_file, File.join("spec/requests", controller_class_path, "#{controller_file_name}_spec.rb")
end
protected
def view_class_path
return options.view if options.view.present?
if controller_class_path.size > 1
File.join controller_class_path[0], plural_name
else
plural_name
end
end
def controller_path
File.join controller_class_path, plural_name
end
def auth
options.auth&.camelcase if options.auth.present?
end
def response_status action
case action
when 'get'
200
when 'post'
201
else
204
end
end
def resource_class
@resource_class ||= begin
options.model.constantize if options.model.present?
rescue NameError
nil
end
@resource_class ||= begin
namespaced_class = controller_class_name.singularize
namespaced_class.constantize
rescue NameError
nil
end
# Second priority is the top namespace model, e.g. EngineName::Article for EngineName::Admin::ArticlesController
@resource_class ||= begin
namespaced_classes = controller_class_name.split('::')
namespaced_class = [namespaced_classes.first, namespaced_classes.last].join('::').singularize
namespaced_class.constantize
rescue NameError
nil
end
# Third priority the camelcased c, i.e. UserGroup
@resource_class ||= begin
camelcased_class = controller_class_name.singularize
camelcased_class.constantize
rescue NameError
nil
end
# Otherwise use the Group class, or fail
@resource_class ||= begin
class_name = controller_class_name.classify
class_name.constantize
rescue NameError => e
raise unless e.message.include?(class_name)
nil
end
@resource_class
end
def attributes_names
begin
resource_class.columns.map(&:name) - %w(id created_at updated_at)
rescue NameError
[]
end
end
def filename_with_extensions(name)
[name, :json, :jbuilder] * '.'
end
def attributes_list_with_timestamps
attributes_list(%w(id created_at updated_at) + attributes_names)
end
def attributes_list(attributes = attributes_names)
attributes.map { |a| ":#{a}"} * ', '
end
end
class <%= controller_class_name %>Controller < SimpleController::Base
view_path '<%= view_class_path %>'
<% if auth.present? %>acts_as_auth_action :<%= auth.downcase %><% end %>
private
def <%= singular_name %>_params
params.require(:<%= singular_name %>).permit(
<%= attributes_list(attributes_names) %>
)
end
end
require 'swagger_helper'
<%= singular_name.upcase %>_REF = {
type: :object, properties: {
<%= singular_name %>: {
type: :object, properties: {
<% resource_class.columns_hash.except('id', 'created_at', 'updated_at').values.each do |column| -%>
<%= column.name %>: { type: :<%= column.type %>, description: '' },
<% end -%>
}
}
}
}
<%= singular_name.upcase %>_VALUE = FactoryBot.attributes_for(:<%= resource_class.to_s.underscore.sub('/', '_') %>)
RSpec.describe '<%= controller_path %>', type: :request, capture_examples: true, tags: ["<%= controller_class_path.join(' ') %>"] do
before :each do
<% if auth.present? -%>
@auth = <%= auth %>.register "auth", "password"
@auth_token = <%= auth %>.login "auth", "password"
<% end -%>
@<%= plural_name %> = FactoryBot.create_list(:<%= resource_class.to_s.underscore.sub('/', '_') %>, 5)
end
<% @routes.each do | template, path_item | -%>
path '<%= template %>' do
<% if auth.present? -%>
parameter '<%= auth %>-Token', in: :header ,type: :string
let('<%= auth %>-Token') { @auth_token.token }
<% end -%>
<% unless path_item[:params].empty? -%>
<% path_item[:params].each do |param| -%>
parameter '<%= param %>', in: :path, type: :string
<% end -%>
<% path_item[:params].each do |param| -%>
let(:<%= param %>) { @<%= plural_name %>.first.id }
<% end -%>
<% end -%>
<% path_item[:actions].each do | action, details | -%>
<% next if action == 'put' -%>
<%= action %>(summary: '<%= details[:summary] %>') do
produces 'application/json'
consumes 'application/json'
<% if ['post', 'patch'].include? action -%>
parameter :<%= singular_name %>, in: :body, schema: <%=
singular_name.upcase %>_REF
let(:<%= singular_name %>) do
{ <%= singular_name %>: <%= singular_name.upcase %>_VALUE }
end
<% end -%>
response(<%= response_status action %>, description: 'successful') do
# it { p JSON.parse(response.body) }
end
end
<% end -%>
end
<% end -%>
end
json.partial! "<%= view_class_path %>/single", <%= singular_name %>: <%= singular_name %>
json.partial! "<%= view_class_path %>/single", <%= singular_name %>: <%= singular_name %>
json.extract! <%= singular_name %>, <%= attributes_list_with_timestamps %>
json.current_page @<%= plural_name %>.current_page
json.total_page @<%= plural_name %>.total_pages
json.<%= plural_name %> @<%= plural_name %>, partial: '<%= view_class_path %>/simple', as: :<%= singular_name %>
json.partial! "<%= view_class_path %>/detail", <%= singular_name %>: @<%= singular_name %>
module RSpec
module Rails
module Swagger
class RouteParser
attr_reader :controller
def initialize(controller)
@controller = controller
end
def routes
::Rails.application.routes.routes.select do |route|
route.defaults[:controller] == controller
end.reduce({}) do |tree, route|
path = path_from(route)
verb = verb_from(route)
tree[path] ||= { params: params_from(route), actions: {} }
tree[path][:actions][verb] = { summary: summary_from(route) }
tree
end
end
private
def path_from(route)
route.path.spec.to_s
.chomp('(.:format)') # Ignore any format suffix
.gsub(/:([^\/.?]+)/, '{\1}') # Convert :id to {id}
end
def verb_from(route)
verb = route.verb
if verb.kind_of? String
verb.downcase
else
verb.source.gsub(/[$^]/, '').downcase
end
end
def summary_from(route)
verb = route.requirements[:action]
noun = route.requirements[:controller].split('/').last.singularize
# Apply a few customizations to make things more readable
case verb
when 'index'
verb = 'list'
noun = noun.pluralize
when 'destroy'
verb = 'delete'
end
"#{verb} #{noun}"
end
def params_from(route)
route.segments - ['format']
end
end
end
end
end
require "simple_controller/version" require 'responders'
require 'ransack'
require 'inherited_resources'
module SimpleController module SimpleController
# Your code goes here... autoload :VERSION, 'simple_controller/version'
autoload :Base, 'simple_controller/base'
end end
...@@ -28,11 +28,13 @@ Gem::Specification.new do |spec| ...@@ -28,11 +28,13 @@ Gem::Specification.new do |spec|
end end
spec.bindir = "exe" spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"] spec.require_paths = ["lib", "app"]
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_dependency "ransack" spec.add_dependency "ransack"
spec.add_dependency "inherited_resources" spec.add_dependency "inherited_resources"
spec.add_dependency "will_paginate", '~> 3.1.0' spec.add_dependency "will_paginate", '~> 3.1.0'
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rspec"
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment