-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathresource_generator.rb
More file actions
208 lines (169 loc) · 5.98 KB
/
resource_generator.rb
File metadata and controls
208 lines (169 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
module Jsonapi
class ResourceGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
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
unless model_klass
raise "You must define a #{class_name} model before generating the corresponding resource."
end
generate_controller
generate_serializer
generate_application_resource unless application_resource_defined?
generate_spec_payload
if actions?('create', 'update')
generate_strong_resource
end
generate_route
generate_tests
generate_resource
generate_swagger if docs_controller?
end
private
def actions
@options['actions'] || %w(index show create update destroy)
end
def actions?(*methods)
methods.any? { |m| actions.include?(m) }
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
def generate_serializer
to = File.join('app/serializers', class_path, "#{serializable_file_name}.rb")
template('serializer.rb.erb', to)
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 docs_controller?
File.exists?('app/controllers/docs_controller.rb')
end
def generate_swagger
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
code
end
end
def generate_spec_payload
to = File.join('spec/payloads', class_path, "#{file_name}.rb")
template('payload.rb.erb', to)
end
def generate_strong_resource
code = " strong_resource :#{singular_table_name} do\n"
attributes.each do |a|
type = a.type
type = :string if type == :text
type = :number if [:float, :decimal].include?(type)
code << " attribute :#{a.name}, :#{type}\n"
end
code << " end\n"
inject_into_file 'config/initializers/strong_resources.rb', after: "StrongResources.configure do\n" do
code
end
end
def generate_route
code = " resources :#{type}"
code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
code << "\n"
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", url, "index_spec.rb"
template('index_request_spec.rb.erb', to)
end
if actions?('show')
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", url, "create_spec.rb"
template('create_request_spec.rb.erb', to)
end
if actions?('update')
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", url, "destroy_spec.rb"
template('destroy_request_spec.rb.erb', to)
end
end
def generate_resource
to = File.join('app/resources', class_path, "#{file_name}_resource.rb")
template('resource.rb.erb', to)
end
def jsonapi_config
File.exists?('.jsonapicfg.yml') ? YAML.load_file('.jsonapicfg.yml') : {}
end
def update_config!(attrs)
config = jsonapi_config.merge(attrs)
File.open('.jsonapicfg.yml', 'w') { |f| f.write(config.to_yaml) }
end
def prompt(header: nil, description: nil, default: nil)
say(set_color("\n#{header}", :magenta, :bold)) if header
say("\n#{description}") if description
answer = ask(set_color("\n(default: #{default}):", :magenta, :bold))
answer = default if answer.blank? && default != 'nil'
say(set_color("\nGot it!\n", :white, :bold))
answer
end
def api_namespace
@api_namespace ||= begin
ns = jsonapi_config['namespace']
if ns.blank?
ns = prompt \
header: "What is your API namespace?",
description: "This will be used as a route prefix, e.g. if you want the route '/books_api/v1/authors' your namespace would be 'books_api'",
default: 'api'
update_config!('namespace' => ns)
end
ns
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.model_name.plural
end
def url
model_klass.model_name.collection
end
end
end