Module: Tiun
Defined Under Namespace
Modules: Actor, Attributes, Auth, Base, CoreHelper, ListSerializer, Migration, Model, Policy, Serializer
Classes: CLI, CoreController, Engine, InvalidControllerError, InvalidModelError, MetaController, NoRailsError
Constant Summary
collapse
- MAP =
{
'get' => {
%r{(?:/(?<c>[^:/]+)).json} => 'index',
%r{(?:/(?<c>[^/]+)/:[^/]+).json} => 'show',
},
'post' => 'create',
'patch' => 'update',
'put' => 'update',
'delete' => 'destroy'
}
- TYPE_MAP =
{
"string" => "string",
"sequence" => "integer",
"uri" => "string",
"list" => nil,
"json" => "jsonb",
"enum" => "string",
}
- TEMPLATES =
{
model: ERB.new(IO.read(File.join(File.dirname(__FILE__), "tiun", "automodel.rb.erb"))),
policy: ERB.new(IO.read(File.join(File.dirname(__FILE__), "tiun", "autopolicy.rb.erb"))),
controller: ERB.new(IO.read(File.join(File.dirname(__FILE__), "tiun", "autocontroller.rb.erb"))),
}
- VERSION =
"0.0.2.1"
Constants included
from Migration
Migration::EMBED, Migration::MigrationTemplate
Constants included
from Attributes
Attributes::AR_MAP
Class Method Summary
collapse
-
.action_names_for(context) ⇒ Object
-
.append_config(file) ⇒ Object
-
.base_controller ⇒ Object
-
.base_model(name = nil) ⇒ Object
-
.config ⇒ Object
-
.config_reduce(config, default) ⇒ Object
-
.constantize(name) ⇒ Object
def model_names settings.keys.map(&:to_s) end.
-
.controller_default_arg_for(context) ⇒ Object
-
.controller_name_for(context) ⇒ Object
-
.controller_title_for(context) ⇒ Object
-
.controllers ⇒ Object
def serializers @serializers ||= [] end.
-
.default_route ⇒ Object
-
.defaults ⇒ Object
-
.detect_type(type_in) ⇒ Object
-
.draw_routes(e) ⇒ Object
-
.error(code, options) ⇒ Object
-
.error_codes ⇒ Object
-
.errors ⇒ Object
-
.find_type(type_names_in) ⇒ Object
find type record in type record table for last version of.
-
.kind_for(context) ⇒ Object
-
.load_defaults_from(config) ⇒ Object
-
.load_routes_from(config) ⇒ Object
-
.load_types_from(config) ⇒ Object
-
.model_name_for(context) ⇒ Object
-
.model_title_for(context) ⇒ Object
-
.models ⇒ Object
-
.parse_objects(kind, config) ⇒ Object
-
.policies ⇒ Object
-
.policy_title_for(context) ⇒ Object
-
.root ⇒ Object
def type_of kind case kind when ‘string’ :“ActiveModel::Type::String” when ‘integer’, ‘index’ :“ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer” else error :invalid_attribute_type_for_kind, { kind: kind } end end def plain_parm parm case parm when String array = parm.split( /s*,s*/ ).
-
.route_title_for(context) ⇒ Object
-
.routes ⇒ Object
-
.search_all_for(kind, value) ⇒ Object
-
.search_for(kind, value) ⇒ Object
-
.settings ⇒ Object
-
.setup ⇒ Object
-
.setup_if_not ⇒ Object
-
.setup_with(*files) ⇒ Object
-
.string_eval(string, name) ⇒ Object
-
.sublings_for(type_name_in, kind_in = %i(read write)) ⇒ Object
generates a hash to collect all subling relation for the specificed kind type.
-
.table_exist?(name) ⇒ Boolean
-
.table_title_for(type) ⇒ Object
-
.template_controller_for(context) ⇒ Object
-
.tiuns ⇒ Object
-
.type_attributes_for(type_name_in, kind = %i(read write)) ⇒ Object
type_attributes_for renders attributes array for the type name or type itself specified, if no type name has been found, it returns a blank array.
-
.types ⇒ Object
-
.valid? ⇒ Boolean
Methods included from Migration
base_migration, fields_for, migration_fields_for, migration_name_for, migrations, search_all_for, setup_migrations
Methods included from Attributes
attribute_name_for, attribute_types_for, attributes
Class Method Details
.action_names_for(context) ⇒ Object
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
# File 'lib/tiun.rb', line 207
def action_names_for context
actions = (context["methods"] || {}).map do |method|
method_name = method.name
rule = MAP[method_name]
action =
method.action || (rule.is_a?(String) && rule || rule.reduce(nil) do |a, (re, action)|
a || context.path =~ re && action || nil
end)
if ! action
error :no_action_detected_for_resource_method, { name: context.name, method: method_name }
end
action ? [action, method] : nil
end.compact.to_h
if actions.blank?
error :no_valid_method_defined_for_resource, { name: context.name }
end
actions
end
|
.append_config(file) ⇒ Object
371
372
373
374
|
# File 'lib/tiun.rb', line 371
def append_config file
c = YAML.load(IO.read( file )).to_os
config[ File.expand_path( file )] = c
end
|
.base_controller ⇒ Object
405
406
407
|
# File 'lib/tiun.rb', line 405
def base_controller
@base_controller ||= defined?(ApplicationController) ? ApplicationController : ActionController::Base
end
|
.base_model(name = nil) ⇒ Object
401
402
403
|
# File 'lib/tiun.rb', line 401
def base_model name = nil
@base_model ||= table_exist?(name) ? ActiveRecord::Base : ActiveModel::Model
end
|
359
360
361
|
# File 'lib/tiun.rb', line 359
def config
@config ||= {}
end
|
.config_reduce(config, default) ⇒ Object
243
244
245
246
247
248
249
250
251
252
253
254
255
|
# File 'lib/tiun.rb', line 243
def config_reduce config, default
config.resources.reduce(default) do |res, context|
if context.name.is_a?(String)
yield(res, context.name, context)
else
res
end
end
rescue NoMethodError
error :no_resources_section_defined_in_config, {config: config, default: default}
default
end
|
.constantize(name) ⇒ Object
def model_names
settings.keys.map(&:to_s)
end
392
393
394
395
|
# File 'lib/tiun.rb', line 392
def constantize name
name.constantize
rescue NameError
end
|
.controller_default_arg_for(context) ⇒ Object
121
122
123
|
# File 'lib/tiun.rb', line 121
def controller_default_arg_for context
context.key || /:(?<arg>[^\.]+)/.match(context.path)&.[](:arg)
end
|
.controller_name_for(context) ⇒ Object
99
100
101
102
103
104
105
106
107
|
# File 'lib/tiun.rb', line 99
def controller_name_for context
if type = find_type(kind_for(context))
type.controller || type.model || type.name
else
context.controller ||
%r{^(?:(?<c>.+)/:[^/]+|/(?<c>[^:]+)).json} =~ context.path && c ||
context.name.split(".").first
end
end
|
.controller_title_for(context) ⇒ Object
115
116
117
118
119
|
# File 'lib/tiun.rb', line 115
def controller_title_for context
name = controller_name_for(context)
name ? name.pluralize.camelize + 'Controller' : raise(InvalidControllerError)
end
|
.controllers ⇒ Object
def serializers
@serializers ||= []
end
def list_serializers
@list_serializers ||= []
end
351
352
353
|
# File 'lib/tiun.rb', line 351
def controllers
@controllers ||= []
end
|
.default_route ⇒ Object
302
303
304
305
306
307
308
309
|
# File 'lib/tiun.rb', line 302
def default_route
{
uri: '/meta.json',
path: 'meta#index',
kind: 'get',
options: { defaults: { format: 'json' }, constraints: { format: 'json' }}
}
end
|
363
364
365
|
# File 'lib/tiun.rb', line 363
def defaults
@defaults ||= {}.to_os
end
|
.detect_type(type_in) ⇒ Object
194
195
196
197
198
199
200
201
|
# File 'lib/tiun.rb', line 194
def detect_type type_in
type = TYPE_MAP[type_in]
type || !type.nil? && "reference" || nil
end
|
.draw_routes(e) ⇒ Object
311
312
313
314
315
316
317
|
# File 'lib/tiun.rb', line 311
def draw_routes e
setup_if_not
routes.each do |route|
attrs = { route[:uri] => route[:path] }.merge(route[:options])
e.send(route[:kind], **attrs)
end
end
|
.error(code, options) ⇒ Object
417
418
419
|
# File 'lib/tiun.rb', line 417
def error code, options
errors << { code: code, options: options }
end
|
.error_codes ⇒ Object
327
328
329
|
# File 'lib/tiun.rb', line 327
def error_codes
@error_codes ||= []
end
|
367
368
369
|
# File 'lib/tiun.rb', line 367
def errors
@errors ||= []
end
|
.find_type(type_names_in) ⇒ Object
find type record in type record table for last version of
153
154
155
156
157
158
159
|
# File 'lib/tiun.rb', line 153
def find_type type_names_in
type_names = "#{type_names_in}".split(/\s+/)
types.reduce(nil) do |t, type|
type_names.include?(type.name) && (!t || !t.version || type.version && t.version < type.version) ? type : t
end unless type_names.blank?
end
|
.kind_for(context) ⇒ Object
85
86
87
|
# File 'lib/tiun.rb', line 85
def kind_for context
context.methods.reduce(nil) { |k, m| k || m.kind }
end
|
.load_defaults_from(config) ⇒ Object
257
258
259
|
# File 'lib/tiun.rb', line 257
def load_defaults_from config
@defaults = defaults.to_h.merge(config.defaults.to_h).to_os
end
|
.load_routes_from(config) ⇒ Object
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
|
# File 'lib/tiun.rb', line 277
def load_routes_from config
@routes =
config_reduce(config, routes) do |r, name, context|
controller = route_title_for(context)
actions = action_names_for(context)
actions.reduce(r) do |res, (action, method)|
/(\.(?<format>[^.]+))$/ =~ context.path
path =
/(?<pre>.*)<(?<key>\w+)>(?<post>.*)/ =~ context.path &&
"#{pre}:#{key}#{post}" || context.path
if res.select {|x| x[:uri] == path && x[:kind] == action }.blank?
attrs = { uri: path, path: "#{controller}##{action}", kind: method.name }
attrs = attrs.merge(options: {defaults: { format: format }, constraints: { format: format }}) if format
res | [attrs]
else
res
end
end
end
end
|
.load_types_from(config) ⇒ Object
203
204
205
|
# File 'lib/tiun.rb', line 203
def load_types_from config
@types = types | (config.types || [])
end
|
.model_name_for(context) ⇒ Object
89
90
91
92
93
94
95
96
97
|
# File 'lib/tiun.rb', line 89
def model_name_for context
if type = find_type(kind_for(context))
type.model || type.name
else
context.model ||
%r{(?:/(?<c>[^/]+)/:[^/]+|/(?<c>[^:/]+)).json} =~ context.path && c ||
context.name.split(".").first
end
end
|
.model_title_for(context) ⇒ Object
109
110
111
112
113
|
# File 'lib/tiun.rb', line 109
def model_title_for context
name = model_name_for(context)
name ? name.singularize.camelize : raise(InvalidModelError)
end
|
335
336
337
|
# File 'lib/tiun.rb', line 335
def models
@models ||= []
end
|
.parse_objects(kind, config) ⇒ Object
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
# File 'lib/tiun.rb', line 261
def parse_objects kind, config
config_reduce(config, send(kind.to_s.pluralize)) do |res, name, context|
object_name = send("#{kind}_title_for", context)
unless search_for(kind.to_s.pluralize, object_name)
object_in = constantize(object_name)
code = TEMPLATES[kind].result(binding)
object = string_eval(code, object_name)
res | [{ name: object_name, code: code, const: object }.to_os]
else
res
end
end
end
|
339
340
341
|
# File 'lib/tiun.rb', line 339
def policies
@policies ||= []
end
|
.policy_title_for(context) ⇒ Object
139
140
141
|
# File 'lib/tiun.rb', line 139
def policy_title_for context
context.policy || model_name_for(context).singularize.camelize + "Policy"
end
|
def type_of kind
case kind
when 'string'
:"ActiveModel::Type::String"
when 'integer', 'index'
:"ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer"
else
error :invalid_attribute_type_for_kind, { kind: kind }
end
end
def plain_parm parm
case parm
when String
array = parm.split( /\s*,\s*/ )
if array.size > 1
plain_array( array )
else
array.first.to_sym
end
when Hash
plain_hash( parm )
when Array
plain_array( parm )
else
nil
end
end
def plain_hash hash
hash.map do |( key, parms )|
[ key.to_sym, plain_parm( parms )]
end.to_h
end
def plain_array array
array.map do | parm |
plain_parm( parm )
end.flatten
end
def setup_classes settings
settings.each do | (model_name, tiun) |
name = -> { model_name.to_s.camelize }
names = -> { model_name.to_s.pluralize.camelize }
params = -> { tiun[:fields].map(&:first) }
binding.pry
controller_rb = <<-RB
class #{names[]}Controller < #{base_controller}
include Tiun::Base
def model
::#{name[]} ;end
def object_serializer
#{name[]}Serializer ;end
def objects_serializer
#{names[]}Serializer
rescue NameError
Tiun::PagedCollectionSerializer ;end
def permitted_params
params.require( '#{model_name}' ).permit( #{params[]} ) ;end;end
RB
policy_rb = <<-RB
class #{name[]}Policy
include Tiun::Policy
end
RB
class_eval(controller_rb)
class_eval(policy_rb)
end
end
507
508
509
|
# File 'lib/tiun.rb', line 507
def root
Gem::Specification.find_by_name( "tiun" ).full_gem_path
end
|
.route_title_for(context) ⇒ Object
125
126
127
128
129
|
# File 'lib/tiun.rb', line 125
def route_title_for context
name = controller_name_for(context)
name ? name.pluralize.tableize : raise(InvalidControllerError)
end
|
355
356
357
|
# File 'lib/tiun.rb', line 355
def routes
@routes ||= [default_route]
end
|
.search_all_for(kind, value) ⇒ Object
323
324
325
|
# File 'lib/tiun.rb', line 323
def search_all_for kind, value
send(kind).select {|x| x.name == value }
end
|
.search_for(kind, value) ⇒ Object
319
320
321
|
# File 'lib/tiun.rb', line 319
def search_for kind, value
send(kind).find {|x| x.name == value }
end
|
376
377
378
|
# File 'lib/tiun.rb', line 376
def settings
@settings ||= setup_classes(tiuns.map { | model | [ model.name.underscore.to_sym, model.tiun ]}.to_h)
end
|
54
55
56
57
58
59
60
61
|
# File 'lib/tiun.rb', line 54
def setup
if defined?(::Rails) && ::Rails.root && !@config
files = Dir.glob(::Rails.root&.join("config", "tiun", "*.{yml,yaml}")) |
Dir.glob(::Rails.root&.join("config", "tiun.{yml,yaml}"))
setup_with(*files)
end
end
|
.setup_if_not ⇒ Object
63
64
65
|
# File 'lib/tiun.rb', line 63
def setup_if_not
@setup ||= setup
end
|
.setup_with(*files) ⇒ Object
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
# File 'lib/tiun.rb', line 67
def setup_with *files
setup_migrations
files.each do |file|
config = append_config(file)
load_types_from(config)
load_defaults_from(config)
%i(model controller policy).each do |kind|
instance_variable_set("@#{kind.to_s.pluralize}", parse_objects(kind, config))
end
load_routes_from(config)
end
config
end
|
.string_eval(string, name) ⇒ Object
232
233
234
235
236
237
238
239
240
241
|
# File 'lib/tiun.rb', line 232
def string_eval string, name
tokens = name.split("::")[0...-1]
default = tokens[0].blank? && Object || Object.const_get(tokens[0])
(tokens[1..-1] || []).reduce(default) do |o, token|
o.constants.include?(token.to_sym) && o.const_get(token) || o.const_set(token, Module.new)
end
eval(string)
end
|
.sublings_for(type_name_in, kind_in = %i(read write)) ⇒ Object
generates a hash to collect all subling relation for the specificed kind type
163
164
165
166
167
168
169
170
171
172
173
|
# File 'lib/tiun.rb', line 163
def sublings_for type_name_in, kind_in = %i(read write)
kind = [kind_in].flatten
type_attributes_for(type_name_in, kind).reduce({}) do |res, value_in|
if value_in.is_a?(Hash)
res.merge(value_in)
else
res
end
end
end
|
.table_exist?(name) ⇒ Boolean
397
398
399
|
# File 'lib/tiun.rb', line 397
def table_exist? name
ActiveRecord::Base.connection.data_source_exists?(name.to_s.tableize)
end
|
.table_title_for(type) ⇒ Object
131
132
133
|
# File 'lib/tiun.rb', line 131
def table_title_for context
context.table || model_name_for(context).tableize
end
|
.template_controller_for(context) ⇒ Object
409
410
411
412
413
414
415
|
# File 'lib/tiun.rb', line 409
def template_controller_for context
t = context.template&.camelize
self.const_get(t).const_get(:Controller)
rescue NameError, TypeError
::Tiun::Base
end
|
380
381
382
383
384
385
386
|
# File 'lib/tiun.rb', line 380
def tiuns
::Rails.configuration.paths['app/models'].to_a.each do | path |
Dir.glob("#{path}/**/*.rb").each { |file| require(file) }
end
ActiveRecord::Base.tiuns
end
|
.type_attributes_for(type_name_in, kind = %i(read write)) ⇒ Object
type_attributes_for renders attributes array for the type name or type itself specified, if no type name has been found, it returns a blank array.
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
# File 'lib/tiun.rb', line 178
def type_attributes_for type_name_in, kind = %i(read write)
type = type_name_in.is_a?(OpenStruct) ? type_name_in : find_type(type_name_in)
return [] unless type
type.fields.map do |x|
next nil unless x.to_h.keys.select { |y| /only$/ =~ y }.reduce(kind) { |k, prop| x[prop] ? ["#{prop}".sub("only","").to_sym] & k : k }.any?
if sub = Tiun.find_type(x.kind)
{ x.name => type_attributes_for(sub, kind) }
else
x.name
end
end.compact
end
|
331
332
333
|
# File 'lib/tiun.rb', line 331
def types
@types ||= []
end
|
.valid? ⇒ Boolean
421
422
423
|
# File 'lib/tiun.rb', line 421
def valid?
errors.blank?
end
|