motor-admin 0.1.13 → 0.1.18
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 +4 -4
- data/app/controllers/motor/data_controller.rb +18 -1
- data/lib/motor/admin.rb +1 -2
- data/lib/motor/alerts/scheduled_alerts_cache.rb +0 -4
- data/lib/motor/api_query/build_json.rb +5 -4
- data/lib/motor/api_query/sort.rb +28 -8
- data/lib/motor/build_schema/active_storage_attachment_schema.rb +1 -1
- data/lib/motor/build_schema/load_from_rails.rb +54 -26
- data/lib/motor/build_schema/persist_resource_configs.rb +1 -0
- data/lib/motor/build_schema/reorder_schema.rb +22 -2
- data/lib/motor/queries/run_query.rb +10 -5
- data/lib/motor/version.rb +1 -1
- data/ui/dist/{main-8f36a2746422efd1e8a8.css.gz → main-ca8fcc97a95fcfa80394.css.gz} +0 -0
- data/ui/dist/main-ca8fcc97a95fcfa80394.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +4 -4
- data/ui/dist/main-8f36a2746422efd1e8a8.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0120981ee7e496773ab8ac05d28e9edab1a2155cacec19636c99c088329065
|
4
|
+
data.tar.gz: 0766e8cef48de36d468baa8c189757e64bc1ea77361741f45aacb80fc19e74a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b657cd07a7c14867bf112d07ebd2caa7d0076fa650f5803bb710188aab4ed29df37cce6f63905f5bedde77ca6558800d3f57ceaefe14625b74de11c05b9b683e
|
7
|
+
data.tar.gz: be4bde0b32f4ff911569e9acc95f55757ef1a316d0940fd33c270e3b54ddc94043d0826cd21d54def98f3a75f7aa443f497ad07c3a3875e91a3a78b540c0f80d
|
@@ -8,6 +8,7 @@ module Motor
|
|
8
8
|
|
9
9
|
before_action :load_and_authorize_resource
|
10
10
|
before_action :load_and_authorize_association
|
11
|
+
before_action :wrap_io_params
|
11
12
|
|
12
13
|
def index
|
13
14
|
@resources = Motor::ApiQuery.call(@resources, params)
|
@@ -98,7 +99,23 @@ module Motor
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def resource_params
|
101
|
-
params
|
102
|
+
if params[:data].present?
|
103
|
+
params.require(:data).except(resource_class.primary_key).permit!
|
104
|
+
else
|
105
|
+
{}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def wrap_io_params(hash = params)
|
110
|
+
hash.each do |key, value|
|
111
|
+
if key == 'io'
|
112
|
+
hash[key] = StringIO.new(value.encode('ISO-8859-1'))
|
113
|
+
elsif value.is_a?(ActionController::Parameters)
|
114
|
+
wrap_io_params(value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
hash
|
102
119
|
end
|
103
120
|
end
|
104
121
|
end
|
data/lib/motor/admin.rb
CHANGED
@@ -8,10 +8,9 @@ module Motor
|
|
8
8
|
|
9
9
|
initializer 'motor.alerts.scheduler' do
|
10
10
|
config.after_initialize do |_app|
|
11
|
-
next
|
11
|
+
next unless defined?(Rails::Server)
|
12
12
|
|
13
13
|
Motor::Alerts::Scheduler::SCHEDULER_TASK.execute
|
14
|
-
Motor::Alerts::ScheduledAlertsCache::UPDATE_ALERTS_TASK.execute
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
@@ -3,10 +3,6 @@
|
|
3
3
|
module Motor
|
4
4
|
module Alerts
|
5
5
|
module ScheduledAlertsCache
|
6
|
-
UPDATE_ALERTS_TASK = Concurrent::TimerTask.new(
|
7
|
-
execution_interval: 2.minutes
|
8
|
-
) { Motor::Alerts::ScheduledAlertsCache.load_alerts }
|
9
|
-
|
10
6
|
CACHE_STORE = ActiveSupport::Cache::MemoryStore.new(size: 5.megabytes)
|
11
7
|
|
12
8
|
module_function
|
@@ -49,26 +49,27 @@ module Motor
|
|
49
49
|
|
50
50
|
params[:fields].each do |key, fields|
|
51
51
|
fields = fields.split(',') if fields.is_a?(String)
|
52
|
-
fields_hash = build_fields_hash(model, fields)
|
53
52
|
|
54
53
|
if key == model_name || model_name.split('/').last == key
|
55
|
-
json_params.merge!(
|
54
|
+
json_params.merge!(build_fields_hash(model, fields))
|
56
55
|
else
|
57
56
|
hash = find_key_in_params(json_params, key)
|
58
57
|
|
58
|
+
fields_hash = build_fields_hash(model.reflections[key]&.klass, fields)
|
59
|
+
|
59
60
|
hash.merge!(fields_hash)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
65
|
def build_fields_hash(model, fields)
|
65
|
-
columns = model.columns.map(&:name)
|
66
|
+
columns = model ? model.columns.map(&:name) : []
|
66
67
|
fields_hash = { 'only' => [], 'methods' => [] }
|
67
68
|
|
68
69
|
fields.each_with_object(fields_hash) do |field, acc|
|
69
70
|
if field.in?(columns)
|
70
71
|
acc['only'] << field
|
71
|
-
|
72
|
+
elsif model.nil? || model.instance_methods.include?(field.to_sym)
|
72
73
|
acc['methods'] << field
|
73
74
|
end
|
74
75
|
end
|
data/lib/motor/api_query/sort.rb
CHANGED
@@ -7,19 +7,39 @@ module Motor
|
|
7
7
|
|
8
8
|
module_function
|
9
9
|
|
10
|
-
def call(rel,
|
11
|
-
return rel if
|
10
|
+
def call(rel, param)
|
11
|
+
return rel if param.blank?
|
12
12
|
|
13
|
-
|
13
|
+
arel_order = build_arel_order(rel.klass, param)
|
14
|
+
join_params = build_join_params(rel.klass, param)
|
14
15
|
|
15
|
-
rel.order(
|
16
|
+
rel.order(arel_order).left_joins(join_params)
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
param.split(',').each_with_object({}) do |field,
|
20
|
-
|
19
|
+
def build_join_params(_model, param)
|
20
|
+
param.split(',').each_with_object({}) do |field, result|
|
21
|
+
key = field[FIELD_PARSE_REGEXP, 2]
|
22
|
+
*path, _ = key.split('.')
|
21
23
|
|
22
|
-
|
24
|
+
path.reduce(result) do |acc, fragment|
|
25
|
+
acc[fragment] = {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_arel_order(model, param)
|
31
|
+
param.split(',').map do |field|
|
32
|
+
direction, key = field.match(FIELD_PARSE_REGEXP).captures
|
33
|
+
*path, field = key.split('.')
|
34
|
+
|
35
|
+
reflection_model =
|
36
|
+
path.reduce(model) do |acc, fragment|
|
37
|
+
acc.reflections[fragment].klass
|
38
|
+
end
|
39
|
+
|
40
|
+
arel_column = reflection_model.arel_table[field]
|
41
|
+
|
42
|
+
direction.present? ? arel_column.desc : arel_column.asc
|
23
43
|
end
|
24
44
|
end
|
25
45
|
end
|
@@ -76,22 +76,67 @@ module Motor
|
|
76
76
|
def fetch_columns(model)
|
77
77
|
default_attrs = model.new.attributes
|
78
78
|
|
79
|
-
|
79
|
+
reference_columns = fetch_reference_columns(model)
|
80
|
+
|
81
|
+
table_columns =
|
82
|
+
model.columns.map do |column|
|
83
|
+
next if reference_columns.find { |c| c[:name] == column.name }
|
84
|
+
|
85
|
+
{
|
86
|
+
name: column.name,
|
87
|
+
display_name: column.name.humanize,
|
88
|
+
column_type: ActiveRecordUtils::Types::UNIFIED_TYPES[column.type.to_s] || column.type.to_s,
|
89
|
+
access_type: COLUMN_NAME_ACCESS_TYPES.fetch(column.name, ColumnAccessTypes::READ_WRITE),
|
90
|
+
default_value: default_attrs[column.name],
|
91
|
+
validators: fetch_validators(model, column.name),
|
92
|
+
reference: nil,
|
93
|
+
virtual: false
|
94
|
+
}
|
95
|
+
end.compact
|
96
|
+
|
97
|
+
reference_columns + table_columns
|
98
|
+
end
|
99
|
+
|
100
|
+
def fetch_reference_columns(model)
|
101
|
+
default_attrs = model.new.attributes
|
102
|
+
|
103
|
+
model.reflections.map do |name, ref|
|
104
|
+
next if !ref.has_one? && !ref.belongs_to?
|
105
|
+
|
106
|
+
begin
|
107
|
+
ref.klass
|
108
|
+
rescue StandardError
|
109
|
+
next
|
110
|
+
end
|
111
|
+
|
112
|
+
column_name = ref.belongs_to? ? ref.foreign_key.to_s : name
|
113
|
+
|
114
|
+
next if ref.klass.name == 'ActiveStorage::Blob'
|
115
|
+
|
116
|
+
is_attachment = ref.klass.name == 'ActiveStorage::Attachment'
|
117
|
+
|
80
118
|
{
|
81
|
-
name:
|
82
|
-
display_name:
|
83
|
-
column_type:
|
84
|
-
access_type:
|
85
|
-
default_value: default_attrs[
|
86
|
-
validators: fetch_validators(model,
|
119
|
+
name: column_name,
|
120
|
+
display_name: column_name.humanize,
|
121
|
+
column_type: is_attachment ? 'file' : 'integer',
|
122
|
+
access_type: ref.belongs_to? || is_attachment ? ColumnAccessTypes::READ_WRITE : ColumnAccessTypes::READ_ONLY,
|
123
|
+
default_value: default_attrs[column_name],
|
124
|
+
validators: fetch_validators(model, column_name),
|
125
|
+
reference: {
|
126
|
+
name: name,
|
127
|
+
model_name: ref.klass.name.underscore,
|
128
|
+
reference_type: ref.belongs_to? ? 'belongs_to' : 'has_one',
|
129
|
+
foreign_key: ref.foreign_key,
|
130
|
+
polymorphic: ref.polymorphic? || is_attachment
|
131
|
+
},
|
87
132
|
virtual: false
|
88
133
|
}
|
89
|
-
end
|
134
|
+
end.compact
|
90
135
|
end
|
91
136
|
|
92
137
|
def fetch_associations(model)
|
93
138
|
model.reflections.map do |name, ref|
|
94
|
-
next if ref.
|
139
|
+
next if ref.has_one? || ref.belongs_to?
|
95
140
|
|
96
141
|
begin
|
97
142
|
ref.klass
|
@@ -108,8 +153,6 @@ module Motor
|
|
108
153
|
display_name: name.humanize,
|
109
154
|
slug: name.underscore,
|
110
155
|
model_name: model_class.name.underscore,
|
111
|
-
model_slug: Utils.slugify(model_class),
|
112
|
-
association_type: fetch_association_type(ref),
|
113
156
|
foreign_key: ref.foreign_key,
|
114
157
|
polymorphic: ref.polymorphic? || model_class.name == 'ActiveStorage::Attachment',
|
115
158
|
visible: true
|
@@ -117,21 +160,6 @@ module Motor
|
|
117
160
|
end.compact
|
118
161
|
end
|
119
162
|
|
120
|
-
def fetch_association_type(association)
|
121
|
-
case association.association_class.to_s
|
122
|
-
when 'ActiveRecord::Associations::HasManyAssociation',
|
123
|
-
'ActiveRecord::Associations::HasManyThroughAssociation'
|
124
|
-
'has_many'
|
125
|
-
when 'ActiveRecord::Associations::HasOneAssociation',
|
126
|
-
'ActiveRecord::Associations::HasOneThroughAssociation'
|
127
|
-
'has_one'
|
128
|
-
when 'ActiveRecord::Associations::BelongsToAssociation'
|
129
|
-
'belongs_to'
|
130
|
-
else
|
131
|
-
raise ArgumentError, 'Unknown association type'
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
163
|
def fetch_validators(model, column_name)
|
136
164
|
model.validators_on(column_name).map do |validator|
|
137
165
|
case validator
|
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
module ReorderSchema
|
5
|
+
COLUMNS_DEFAULT_ORDER_WEIGHTS = {
|
6
|
+
id: 0,
|
7
|
+
updated_at: 2,
|
8
|
+
edited_at: 2,
|
9
|
+
created_at: 3,
|
10
|
+
inserted_at: 3,
|
11
|
+
deleted_at: 4,
|
12
|
+
archived_at: 4
|
13
|
+
}.with_indifferent_access
|
14
|
+
|
15
|
+
COLUMNS_DEFAULT_ORDER_WEIGHT = 1
|
16
|
+
|
5
17
|
module_function
|
6
18
|
|
7
19
|
# @param schema [Array<HashWithIndifferentAccess>]
|
@@ -19,7 +31,7 @@ module Motor
|
|
19
31
|
scopes_order = configs["resources.#{model[:name]}.scopes.order"]
|
20
32
|
|
21
33
|
model.merge(
|
22
|
-
columns: sort_by_name(model[:columns], columns_order, sort_alphabetically: false),
|
34
|
+
columns: sort_by_name(sort_columns(model[:columns]), columns_order, sort_alphabetically: false),
|
23
35
|
associations: sort_by_name(model[:associations], associations_order),
|
24
36
|
actions: sort_by_name(model[:actions], actions_order, sort_alphabetically: false),
|
25
37
|
tabs: sort_by_name(model[:tabs], tabs_order, sort_alphabetically: false),
|
@@ -39,11 +51,19 @@ module Motor
|
|
39
51
|
if order.present?
|
40
52
|
order.index(item[:name]) || Float::MAX
|
41
53
|
else
|
42
|
-
item[:display_name]
|
54
|
+
item[:display_name].to_s
|
43
55
|
end
|
44
56
|
end
|
45
57
|
end
|
46
58
|
|
59
|
+
def sort_columns(columns)
|
60
|
+
columns.each_with_object([]) do |column, acc|
|
61
|
+
weight = COLUMNS_DEFAULT_ORDER_WEIGHTS.fetch(column[:name], COLUMNS_DEFAULT_ORDER_WEIGHT)
|
62
|
+
|
63
|
+
(acc[weight] ||= []) << column
|
64
|
+
end.flatten.compact
|
65
|
+
end
|
66
|
+
|
47
67
|
# @return [Hash<String, HashWithIndifferentAccess>]
|
48
68
|
def load_configs
|
49
69
|
Motor::Config.all.each_with_object({}) do |config, acc|
|
@@ -57,11 +57,16 @@ module Motor
|
|
57
57
|
"$#{index}"
|
58
58
|
end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
attributes =
|
61
|
+
variables.map do |variable_name, default_value|
|
62
|
+
ActiveRecord::Relation::QueryAttribute.new(
|
63
|
+
variable_name,
|
64
|
+
variables_hash[variable_name] || default_value,
|
65
|
+
ActiveRecord::Type::Value.new
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
[format(WITH_STATEMENT_TEMPLATE, sql_body: sql.strip.gsub(/;\z/, ''), limit: limit), 'SQL', attributes]
|
65
70
|
end
|
66
71
|
end
|
67
72
|
end
|
data/lib/motor/version.rb
CHANGED
Binary file
|
Binary file
|
data/ui/dist/manifest.json
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
"fonts/ionicons.ttf?v=3.0.0-alpha.3": "fonts/ionicons.ttf",
|
6
6
|
"fonts/ionicons.woff2?v=3.0.0-alpha.3": "fonts/ionicons.woff2",
|
7
7
|
"fonts/ionicons.woff?v=3.0.0-alpha.3": "fonts/ionicons.woff",
|
8
|
-
"main-
|
9
|
-
"main-
|
10
|
-
"main-
|
11
|
-
"main.css": "main-
|
12
|
-
"main.js": "main-
|
8
|
+
"main-ca8fcc97a95fcfa80394.css.gz": "main-ca8fcc97a95fcfa80394.css.gz",
|
9
|
+
"main-ca8fcc97a95fcfa80394.js.LICENSE.txt": "main-ca8fcc97a95fcfa80394.js.LICENSE.txt",
|
10
|
+
"main-ca8fcc97a95fcfa80394.js.gz": "main-ca8fcc97a95fcfa80394.js.gz",
|
11
|
+
"main.css": "main-ca8fcc97a95fcfa80394.css",
|
12
|
+
"main.js": "main-ca8fcc97a95fcfa80394.js"
|
13
13
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motor-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Matsyburka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord-filter
|
@@ -213,8 +213,8 @@ files:
|
|
213
213
|
- lib/motor/ui_configs.rb
|
214
214
|
- lib/motor/version.rb
|
215
215
|
- ui/dist/fonts/ionicons.woff2
|
216
|
-
- ui/dist/main-
|
217
|
-
- ui/dist/main-
|
216
|
+
- ui/dist/main-ca8fcc97a95fcfa80394.css.gz
|
217
|
+
- ui/dist/main-ca8fcc97a95fcfa80394.js.gz
|
218
218
|
- ui/dist/manifest.json
|
219
219
|
homepage:
|
220
220
|
licenses:
|
Binary file
|