tiun 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/TODO +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/tiun +5 -0
- data/lib/config/routes.rb +16 -0
- data/lib/layouts/tiun.html.erb +10 -0
- data/lib/tiun/actor/validate.rb +9 -0
- data/lib/tiun/actor.rb +34 -0
- data/lib/tiun/attributes.rb +33 -0
- data/lib/tiun/autocontroller.rb.erb +18 -0
- data/lib/tiun/autolistserializer.rb.erb +9 -0
- data/lib/tiun/automigration.rb.erb +11 -0
- data/lib/tiun/automodel.rb.erb +7 -0
- data/lib/tiun/autopolicy.rb.erb +5 -0
- data/lib/tiun/autoserializer.rb.erb +9 -0
- data/lib/tiun/base.rb +371 -0
- data/lib/tiun/cli.rb +88 -0
- data/lib/tiun/core_controller.rb +6 -0
- data/lib/tiun/core_helper.rb +13 -0
- data/lib/tiun/engine.rb +35 -0
- data/lib/tiun/list_serializer.rb +20 -0
- data/lib/tiun/meta_controller.rb +13 -0
- data/lib/tiun/migration.rb +105 -0
- data/lib/tiun/mixins.rb +48 -0
- data/lib/tiun/model.rb +108 -0
- data/lib/tiun/policy.rb +71 -0
- data/lib/tiun/serializer.rb +31 -0
- data/lib/tiun/version.rb +5 -0
- data/lib/tiun.rb +491 -0
- data/tiun.gemspec +54 -0
- metadata +343 -0
data/lib/tiun/base.rb
ADDED
@@ -0,0 +1,371 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module Tiun::Base
|
4
|
+
class OutOfRangeError < StandardError; end
|
5
|
+
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_reader :objects, :object
|
10
|
+
|
11
|
+
|
12
|
+
#attr_accessor :params
|
13
|
+
#define_callbacks :subscribe
|
14
|
+
# include Kaminari::ConfigurationMethods
|
15
|
+
#class_attribute :_helpers
|
16
|
+
#self._helpers = Module.new
|
17
|
+
#include ActiveSupport::Configurable
|
18
|
+
#config_accessor :co
|
19
|
+
# helper_method :cookies if defined?(helper_method)
|
20
|
+
# around_perform do |job, block, _|
|
21
|
+
# I18n.with_locale(I18n.locale.to_s, &block)
|
22
|
+
# include Pundit
|
23
|
+
|
24
|
+
# before_action :authenticate_user!
|
25
|
+
# before_action :set_tokens, only: %i(index)
|
26
|
+
# before_action :set_page, only: %i(index)
|
27
|
+
# before_action :set_locales
|
28
|
+
before_action :new_object, only: %i(create)
|
29
|
+
before_action :fetch_object, only: %i(show update destroy)
|
30
|
+
before_action :fetch_objects, only: %i(index)
|
31
|
+
before_action :parse_range, only: %i(index ac)
|
32
|
+
after_action :return_range, only: %i(index ac)
|
33
|
+
# before_action :authorize!
|
34
|
+
|
35
|
+
rescue_from Exception, with: :exception
|
36
|
+
rescue_from ActionView::MissingTemplate, with: :missing_template
|
37
|
+
rescue_from ActionController::ParameterMissing, with: :unprocessable_entity
|
38
|
+
rescue_from ActiveRecord::RecordNotUnique,
|
39
|
+
ActiveRecord::RecordInvalid,
|
40
|
+
ActiveRecord::RecordNotSaved,
|
41
|
+
CarrierWave::IntegrityError, with: :unprocessable_entity
|
42
|
+
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
43
|
+
rescue_from OutOfRangeError, with: :out_of_range
|
44
|
+
|
45
|
+
# has_scope :with_token, only: %i(index all)
|
46
|
+
# has_scope :with_tokens, only: %i(index), type: :array
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# GET /<objects>/
|
51
|
+
def index
|
52
|
+
respond_to do |format|
|
53
|
+
format.html { render :index }
|
54
|
+
format.json { render status: code, json: serialize_collection(paged_objects) }
|
55
|
+
format.jsonp { render status: code, json: serialize_collection(paged_objects) }
|
56
|
+
format.any { render status: code, json: serialize_collection(paged_objects), content_type: 'application/json' }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# POST /<objects>/create.json
|
61
|
+
def create
|
62
|
+
object.save!
|
63
|
+
|
64
|
+
respond_to do |format|
|
65
|
+
format.html
|
66
|
+
format.json { render json: serialize(object) }
|
67
|
+
format.jsonp { head :ok }
|
68
|
+
format.any { render json: serialize(object), content_type: 'application/json' }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# PUT /<objects>/:id.json
|
73
|
+
def update
|
74
|
+
object.update!(permitted_params)
|
75
|
+
|
76
|
+
respond_to do |format|
|
77
|
+
format.html
|
78
|
+
format.json { render json: serialize(object) }
|
79
|
+
format.jsonp { head :ok }
|
80
|
+
format.any { render json: serialize(object), content_type: 'application/json' }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# GET /<objects>/:id.json
|
85
|
+
def show
|
86
|
+
respond_to do |format|
|
87
|
+
format.html
|
88
|
+
format.json { render json: serialize(object) }
|
89
|
+
format.jsonp { render json: serialize(object) }
|
90
|
+
format.any { render json: serialize(object), content_type: 'application/json' }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# DELETE /<objects>/:id.json
|
95
|
+
def destroy
|
96
|
+
object.destroy
|
97
|
+
|
98
|
+
respond_to do |format|
|
99
|
+
format.html
|
100
|
+
format.json { render json: serialize(object) }
|
101
|
+
format.jsonp { head :ok }
|
102
|
+
format.any { render json: serialize(object), content_type: 'application/json' }
|
103
|
+
end
|
104
|
+
rescue Exception
|
105
|
+
binding.pry
|
106
|
+
end
|
107
|
+
|
108
|
+
# GET /<objects>/ac.json
|
109
|
+
def ac
|
110
|
+
@objects = apply_scopes(model)
|
111
|
+
|
112
|
+
respond_to do |format|
|
113
|
+
format.json {
|
114
|
+
render json: {
|
115
|
+
list: objects.limit(ac_limit).jsonize(ac_context),
|
116
|
+
total: objects.total_count
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# def dashboard
|
123
|
+
# settings = { settings: Tiun.settings, locales: @locales }
|
124
|
+
#
|
125
|
+
# render inline: react_component('Dashboard', settings), layout: 'tiun'
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
protected
|
129
|
+
|
130
|
+
def code
|
131
|
+
total == 0 ? 204 : total > range.end + 1 ? 206 : 200
|
132
|
+
end
|
133
|
+
|
134
|
+
def model_name
|
135
|
+
@_model_name ||= self.class.to_s.gsub(/.*::/, "").gsub("Controller", "").singularize
|
136
|
+
end
|
137
|
+
|
138
|
+
def param_name
|
139
|
+
@_param_name ||= model_name.tableize.singularize
|
140
|
+
end
|
141
|
+
|
142
|
+
def model
|
143
|
+
@_model ||= model_name.constantize
|
144
|
+
end
|
145
|
+
|
146
|
+
# def serializer
|
147
|
+
# @serializer ||= "#{model_name}Serializer".constantize
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# def list_serializer
|
151
|
+
# @list_serializer ||= "#{model_name}ListSerializer".constantize
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
def policy
|
155
|
+
@_policy ||= "#{model_name}Policy".constantize
|
156
|
+
end
|
157
|
+
|
158
|
+
# def objects_serializer
|
159
|
+
# list_serializer.new(model: model)
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
def apply_scopes model
|
163
|
+
model
|
164
|
+
end
|
165
|
+
|
166
|
+
# def policy_name
|
167
|
+
# @policy_name ||= Object.const_get(model.name + "Policy")
|
168
|
+
# rescue NameError
|
169
|
+
# @policy_name = Object.const_get("Tiun::#{model.name}Policy")
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
def not_found _e
|
173
|
+
render json: { args: params.permit(*permitted_filter).to_h }, status: 404
|
174
|
+
end
|
175
|
+
|
176
|
+
def out_of_range _e
|
177
|
+
render json: { args: params.permit(*permitted_filter).to_h }, status: 416
|
178
|
+
end
|
179
|
+
|
180
|
+
def unprocessable_entity e
|
181
|
+
errors = @object && @object.errors.any? && @object.errors || e.to_s
|
182
|
+
args = params.permit(*permitted_filter).to_h
|
183
|
+
render json: { args: args, error: errors }, status: :unprocessable_entity
|
184
|
+
end
|
185
|
+
|
186
|
+
def parameter_missing e
|
187
|
+
error = "[#{e.class}]> #{e.message} \n\t #{e.backtrace.join("\n\t")}"
|
188
|
+
args = params.permit(*permitted_filter).to_h
|
189
|
+
render json: { args: args, error: error }, status: 500
|
190
|
+
end
|
191
|
+
|
192
|
+
def missing_template e
|
193
|
+
error = "[#{e.class}]> #{e.message} \n\t #{e.backtrace.join("\n\t")}"
|
194
|
+
args = params.permit(*permitted_filter).to_h
|
195
|
+
render json: { args: args, error: error }, status: 500
|
196
|
+
end
|
197
|
+
|
198
|
+
def exception e
|
199
|
+
error = "[#{e.class}]> #{e.message} \n\t #{e.backtrace.join("\n\t")}"
|
200
|
+
args = params.permit(*permitted_filter).to_h
|
201
|
+
render json: { args: args, error: error }, status: 500
|
202
|
+
end
|
203
|
+
|
204
|
+
def ac_limit
|
205
|
+
500
|
206
|
+
end
|
207
|
+
|
208
|
+
def context
|
209
|
+
@_context ||= { except: supplement_names, locales: locales }
|
210
|
+
end
|
211
|
+
|
212
|
+
def ac_context
|
213
|
+
@_ac_context ||= { only: %i(key value) }
|
214
|
+
end
|
215
|
+
|
216
|
+
def permitted_filter
|
217
|
+
@_permitted_filter ||= {}
|
218
|
+
@_permitted_filter[action_name] ||=
|
219
|
+
if c = self.class.instance_variable_get(:@context)[action_name]
|
220
|
+
c.args.map {|x|x.name}
|
221
|
+
else
|
222
|
+
[model.primary_key]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def permitted_self
|
227
|
+
@_permitted_self ||= {}
|
228
|
+
@_permitted_self[action_name] ||=
|
229
|
+
if c = self.class.instance_variable_get(:@context)[action_name]
|
230
|
+
c.args.map {|x|x.name}
|
231
|
+
else
|
232
|
+
model.attribute_types.keys
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def permitted_children
|
237
|
+
@_permitted_children ||=
|
238
|
+
model.nested_attributes_options.reduce({}) do |res, (name, opts)|
|
239
|
+
child_model = model.reflections[name.to_s].klass
|
240
|
+
value = child_model.attribute_types.keys
|
241
|
+
value << :_destroy if opts[:allow_destroy]
|
242
|
+
res[name] = value - supplement_names.map(&:to_s)
|
243
|
+
|
244
|
+
res
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def permitted_params
|
249
|
+
params.require(param_name).permit(*permitted_self, **permitted_children)
|
250
|
+
end
|
251
|
+
|
252
|
+
def supplement_names
|
253
|
+
%i(created_at updated_at tsv)
|
254
|
+
end
|
255
|
+
|
256
|
+
def total
|
257
|
+
%i(total_size total_count count).reduce(nil) do |count, method|
|
258
|
+
objects.respond_to?(method) && !count ? objects.send(method) : count
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def paged_objects
|
263
|
+
objects.range(range)
|
264
|
+
end
|
265
|
+
|
266
|
+
def render_type type
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_properties
|
270
|
+
@get_properties ||=
|
271
|
+
if k = self.class.instance_variable_get(:@context)[action_name]&.kind
|
272
|
+
Tiun.type_attributes_for(k)
|
273
|
+
else
|
274
|
+
model.attribute_types.keys
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def serialize_collection collection
|
279
|
+
collection.jsonize(context.merge(only: get_properties))
|
280
|
+
# format.json { render :index, json: objects, locales: locales,
|
281
|
+
# serializer: objects_serializer,
|
282
|
+
# each_serializer: serializer,
|
283
|
+
# total: objects.total_count,
|
284
|
+
# page: page,
|
285
|
+
# per: per }
|
286
|
+
end
|
287
|
+
|
288
|
+
def serialize object
|
289
|
+
object.jsonize(context.merge(only: get_properties))
|
290
|
+
# format.json { render :show, json: @object, locales: @locales,
|
291
|
+
# serializer: serializer }
|
292
|
+
end
|
293
|
+
#
|
294
|
+
|
295
|
+
def authorize!
|
296
|
+
binding.pry
|
297
|
+
if !policy.new(current_user, @object).send(action_name + '?')
|
298
|
+
raise Tiun::Policy::NotAuthorizedError, "not allowed to do #{action_name} this #{@object.inspect}"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def per
|
303
|
+
@per ||= (params[:per] ||
|
304
|
+
if args = self.class.instance_variable_get(:@context)[action_name]&.args
|
305
|
+
args.reduce(nil) { |res, x| res || x.name == "per" && x.default || nil }
|
306
|
+
end || 25).to_i
|
307
|
+
end
|
308
|
+
|
309
|
+
def page
|
310
|
+
@page ||= (params[:page] || 1).to_i
|
311
|
+
end
|
312
|
+
|
313
|
+
def locales
|
314
|
+
@locales ||= [I18n.locale]
|
315
|
+
end
|
316
|
+
|
317
|
+
def set_tokens
|
318
|
+
@tokens ||= params[:with_tokens] || []
|
319
|
+
end
|
320
|
+
|
321
|
+
def default_arg
|
322
|
+
self.class.instance_variable_get(:@default_arg)[action_name]
|
323
|
+
rescue NameError
|
324
|
+
"id"
|
325
|
+
end
|
326
|
+
|
327
|
+
def range
|
328
|
+
@range
|
329
|
+
end
|
330
|
+
|
331
|
+
# callbacks
|
332
|
+
def parse_range
|
333
|
+
@range =
|
334
|
+
if /(?<b>[0-9]+)-(?<e>[0-9]+)/ =~ request.headers["Range"]
|
335
|
+
(b.to_i..e.to_i - 1)
|
336
|
+
else
|
337
|
+
((page - 1) * per..(page - 1) * per + per - 1)
|
338
|
+
end
|
339
|
+
|
340
|
+
raise OutOfRangeError if total > 0 && @range.begin >= total || @range.end < @range.begin
|
341
|
+
end
|
342
|
+
|
343
|
+
def new_object
|
344
|
+
@object = model.new(permitted_params)
|
345
|
+
end
|
346
|
+
|
347
|
+
def fetch_objects
|
348
|
+
@objects = apply_scopes(model)#.page(params[:page])
|
349
|
+
end
|
350
|
+
|
351
|
+
def fetch_object
|
352
|
+
arg_name = default_arg
|
353
|
+
|
354
|
+
@object ||=
|
355
|
+
if !arg_name
|
356
|
+
model.find(params[model.primary_key])
|
357
|
+
elsif model.respond_to?("by_#{arg_name}")
|
358
|
+
model.send("by_#{arg_name}", params[arg_name]).first
|
359
|
+
else
|
360
|
+
model.where({ arg_name => params[arg_name] }).first
|
361
|
+
end || raise(ActiveRecord::RecordNotFound)
|
362
|
+
end
|
363
|
+
|
364
|
+
def return_range
|
365
|
+
response.headers["Accept-Ranges"] = "records"
|
366
|
+
response.headers["Content-Range"] = "records #{range.begin}-#{range.end}/#{total}"
|
367
|
+
response.headers["Content-Length"] = [range.end + 1, total].min - range.begin
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
Base = Tiun::Base
|
data/lib/tiun/cli.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
require 'tiun'
|
5
|
+
require 'tiun/actor'
|
6
|
+
|
7
|
+
class Tiun::CLI
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
config: nil,
|
10
|
+
}.to_os
|
11
|
+
|
12
|
+
def option_parser
|
13
|
+
@option_parser ||=
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: setup.rb [options & actions]"
|
16
|
+
|
17
|
+
opts.on("-c", "--use-config=CONFIG", String, "use specific tiun config file to apply") do |config|
|
18
|
+
options[:config] = config
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
22
|
+
options[:verbose] = v
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-h", "--help", "This help") do |v|
|
26
|
+
puts opts
|
27
|
+
puts "\nActions: \n#{actions.join("\n")}"
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if @argv
|
33
|
+
@option_parser.default_argv.replace(@argv)
|
34
|
+
elsif @option_parser.default_argv.empty?
|
35
|
+
@option_parser.default_argv << "-h"
|
36
|
+
end
|
37
|
+
|
38
|
+
@option_parser
|
39
|
+
end
|
40
|
+
|
41
|
+
def options
|
42
|
+
@options ||= DEFAULT_OPTIONS.dup
|
43
|
+
end
|
44
|
+
|
45
|
+
def actions
|
46
|
+
@actions ||= parse.actions.select { |a| Tiun::Actor.kinds.include?(a) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def tiun
|
50
|
+
@tiun ||= options.config &&
|
51
|
+
Tiun.setup_with(options.config) ||
|
52
|
+
Tiun.setup
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_parse
|
56
|
+
@parse = OpenStruct.new(options: options, actions: option_parser.default_argv)
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse!
|
60
|
+
return @parse if @parse
|
61
|
+
|
62
|
+
option_parser.parse!
|
63
|
+
|
64
|
+
default_parse
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse
|
68
|
+
parse!
|
69
|
+
rescue OptionParser::InvalidOption
|
70
|
+
default_parse
|
71
|
+
end
|
72
|
+
|
73
|
+
def run
|
74
|
+
actions.reduce({}.to_os) do |res, action_name|
|
75
|
+
res[action_name] = Tiun::Actor.for!(action_name, tiun)
|
76
|
+
|
77
|
+
res
|
78
|
+
end.map do |action_name, actor|
|
79
|
+
actor.apply_to(Tiun)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize argv = nil
|
84
|
+
@argv = argv&.split(/\s+/)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Cli = Tiun::CLI
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ::Tiun::CoreHelper
|
2
|
+
include ActionView::Helpers::TagHelper
|
3
|
+
|
4
|
+
def react_component name, props = {}, options = {}, &block
|
5
|
+
html_options = options.reverse_merge(data: {
|
6
|
+
react_class: name,
|
7
|
+
react_props: (props.is_a?(String) ? props : props.to_json)
|
8
|
+
})
|
9
|
+
content_tag(:div, '', html_options, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
CoreHelper = ::Tiun::CoreHelper
|
data/lib/tiun/engine.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require 'tiun/model'
|
5
|
+
|
6
|
+
class Tiun::Engine < ::Rails::Engine
|
7
|
+
isolate_namespace Tiun
|
8
|
+
|
9
|
+
engine_name 'tiun'
|
10
|
+
|
11
|
+
paths["app"] << "lib"
|
12
|
+
paths["app/controllers"] << "lib"
|
13
|
+
paths["app/views"] << "lib"
|
14
|
+
# paths["config"] << "lib/config"
|
15
|
+
# paths["config/locales"] << "lib/locales"
|
16
|
+
paths["config/routes.rb"] << "lib/config/routes.rb"
|
17
|
+
# require 'pry'
|
18
|
+
# binding.pry
|
19
|
+
|
20
|
+
# initializer "tiun", group: :all do |app|
|
21
|
+
# PgHero.time_zone = PgHero.config["time_zone"] if PgHero.config["time_zone"]
|
22
|
+
#
|
23
|
+
|
24
|
+
# end
|
25
|
+
|
26
|
+
# config.autoload_paths += %W(#{Tiun::Engine.root}/lib/tiun/autoloads)
|
27
|
+
# config.autoload_paths += %W(#{Tiun::Engine.root}/lib/tiun/views)
|
28
|
+
|
29
|
+
config.to_prepare do
|
30
|
+
::ActiveRecord::Base.extend(Tiun::Model)
|
31
|
+
Tiun.setup
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Engine = Tiun::Engine
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ::Tiun::ListSerializer
|
2
|
+
def as_json *_args
|
3
|
+
serializable_hash
|
4
|
+
end
|
5
|
+
|
6
|
+
def to_json *_args
|
7
|
+
serializable_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def serializable_hash(*_args)
|
11
|
+
binding.pry
|
12
|
+
{
|
13
|
+
list: super,
|
14
|
+
page: @options[:page] || 1,
|
15
|
+
total: @options[:total] || @object.count
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ListSerializer = ::Tiun::ListSerializer
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Tiun::MetaController < ActionController::Base
|
2
|
+
#before_action :authenticate_user!
|
3
|
+
#before_action :authorize!
|
4
|
+
|
5
|
+
# GET /meta/
|
6
|
+
def index
|
7
|
+
joint = Tiun.config.values.reduce({}) {|res, h| res.merge(h) }
|
8
|
+
#binding.pry
|
9
|
+
render json: joint.as_json
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
MetaController = Tiun::MetaController
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'tiun/version'
|
2
|
+
|
3
|
+
module Tiun::Migration
|
4
|
+
Proxy = Struct.new(:name, :timestamp, :code) do
|
5
|
+
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
|
6
|
+
|
7
|
+
def basename
|
8
|
+
""
|
9
|
+
# TODO fake name file
|
10
|
+
end
|
11
|
+
|
12
|
+
def scope
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def version
|
17
|
+
timestamp.utc.strftime("%Y%m%d%H%M%S").to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def migration
|
23
|
+
@migration ||= load_migration
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_migration
|
27
|
+
Tiun.string_eval(code, name)
|
28
|
+
name.constantize.new(name, version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(name, timestamp, code)
|
32
|
+
super
|
33
|
+
@migration = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
MigrationTemplate = ERB.new(IO.read(File.join(File.dirname(__FILE__), "automigration.rb.erb")))
|
38
|
+
|
39
|
+
EMBED = <<-END
|
40
|
+
class ActiveRecord::MigrationContext
|
41
|
+
alias :__old_migrations :migrations
|
42
|
+
|
43
|
+
def migrations
|
44
|
+
(Tiun.migrations | __old_migrations).sort_by(&:version)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
END
|
48
|
+
|
49
|
+
def base_migration
|
50
|
+
@base_migration ||= ActiveRecord::Migration[5.2]
|
51
|
+
end
|
52
|
+
|
53
|
+
def migration_name_for type
|
54
|
+
"Create" + type.name.pluralize.camelize
|
55
|
+
end
|
56
|
+
|
57
|
+
def fields_for type
|
58
|
+
(type.fields || []) | (defaults.fields || [])
|
59
|
+
end
|
60
|
+
|
61
|
+
def migration_fields_for type
|
62
|
+
fields_for(type).map do |attrs|
|
63
|
+
type = detect_type(attrs.kind)
|
64
|
+
type && { name: attrs.name, type: type, options: {} } || nil
|
65
|
+
end.compact
|
66
|
+
end
|
67
|
+
|
68
|
+
def migrations
|
69
|
+
return @migrations if @migrations
|
70
|
+
|
71
|
+
@migrations =
|
72
|
+
types.reduce([]) do |migrations, type|
|
73
|
+
next migrations if type.parent
|
74
|
+
|
75
|
+
migration_name = migration_name_for(type)
|
76
|
+
|
77
|
+
if migrations.any? { |m| m.name == migration_name }
|
78
|
+
error :duplicated_migration_found, {name: migration_name, type: type}
|
79
|
+
else
|
80
|
+
table_title = table_title_for(type)
|
81
|
+
migration_fields = migration_fields_for(type)
|
82
|
+
code = MigrationTemplate.result(binding)
|
83
|
+
timestamp = type.timestamp
|
84
|
+
|
85
|
+
if timestamp
|
86
|
+
migrations << Proxy.new(migration_name, type.timestamp, code)
|
87
|
+
else
|
88
|
+
error :no_timestamp_defined_for_migration, {name: migration_name, code: code}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
migrations
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def search_all_for kind, value
|
97
|
+
send(kind).select {|x| x.name == value }
|
98
|
+
end
|
99
|
+
|
100
|
+
def setup_migrations
|
101
|
+
Kernel.module_eval(EMBED)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
Migration = Tiun::Migration
|
data/lib/tiun/mixins.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class Object
|
2
|
+
def blank?
|
3
|
+
case self
|
4
|
+
when NilClass, FalseClass
|
5
|
+
true
|
6
|
+
when TrueClass
|
7
|
+
false
|
8
|
+
when Hash, Array
|
9
|
+
!self.any?
|
10
|
+
else
|
11
|
+
self.to_s == ""
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_os
|
16
|
+
if self.respond_to?(:to_h)
|
17
|
+
if self.is_a?(OpenStruct)
|
18
|
+
self
|
19
|
+
else
|
20
|
+
OpenStruct.new(self.to_h.map {|(x, y)| [x.to_s, y.respond_to?(:map) && y.to_os || y] }.to_h)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
OpenStruct.new("" => self)
|
24
|
+
end
|
25
|
+
rescue
|
26
|
+
binding.pry
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Array
|
31
|
+
def to_os
|
32
|
+
self.map {|x| x.to_os }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class OpenStruct
|
37
|
+
def map *args, &block
|
38
|
+
res = self.class.new
|
39
|
+
|
40
|
+
self.each_pair do |key, value|
|
41
|
+
res[key] = block[key, value]
|
42
|
+
end
|
43
|
+
|
44
|
+
res
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Mixins = Object
|