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.
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,6 @@
1
+ class Tiun::CoreController < ActionController::Base
2
+ include Tiun::CoreHelper
3
+ include Tiun::Base
4
+ end
5
+
6
+ CoreController = Tiun::CoreController
@@ -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
@@ -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
@@ -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