motor-admin 0.1.64 → 0.1.70

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.
@@ -22,3 +22,4 @@ require_relative './active_record_utils/active_storage_links_extension'
22
22
  require_relative './active_record_utils/active_storage_blob_patch'
23
23
  require_relative './active_record_utils/active_record_filter_patch'
24
24
  require_relative './active_record_utils/active_record_connection_column_patch'
25
+ require_relative './active_record_utils/action_text_attribute_patch'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActionText::Attribute)
4
+ ActionText::Attribute::ClassMethods.prepend(Module.new do
5
+ # rubocop:disable Naming/PredicateName
6
+ def has_rich_text(*args)
7
+ super
8
+
9
+ name = args.first
10
+
11
+ alias_method :"#{name}_body=", :"#{name}="
12
+
13
+ define_method(:"#{name}_body") do
14
+ send(name).body&.to_trix_html
15
+ end
16
+ end
17
+ # rubocop:enable Naming/PredicateName
18
+ end)
19
+ end
@@ -10,4 +10,5 @@ ActiveRecord::ConnectionAdapters::Column.class_eval do
10
10
  def array
11
11
  false
12
12
  end
13
+ alias_method :array?, :array
13
14
  end
@@ -5,3 +5,14 @@ ActiveRecord::Filter.module_eval do
5
5
  @filters ||= HashWithIndifferentAccess.new
6
6
  end
7
7
  end
8
+
9
+ if Rails::VERSION::MAJOR == 6
10
+ ActiveRecord::Relation::Filter.module_eval do
11
+ def build_arel(aliases = nil)
12
+ arel = super
13
+ my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(connection, table.name, [])
14
+ build_filters(arel, my_alias_tracker)
15
+ arel
16
+ end
17
+ end
18
+ end
@@ -6,10 +6,6 @@ module Motor
6
6
  def path
7
7
  Rails.application.routes.url_helpers.rails_blob_path(self, only_path: true)
8
8
  end
9
-
10
- def url
11
- Rails.application.routes.url_helpers.url_for(self)
12
- end
13
9
  end
14
10
  end
15
11
  end
@@ -22,40 +22,6 @@ module Motor
22
22
 
23
23
  DEFAULT_SCOPE_TYPE = 'default'
24
24
 
25
- DEFAULT_ACTIONS = [
26
- {
27
- name: 'create',
28
- display_name: 'Create',
29
- action_type: 'default',
30
- preferences: {},
31
- visible: true
32
- },
33
- {
34
- name: 'edit',
35
- display_name: 'Edit',
36
- action_type: 'default',
37
- preferences: {},
38
- visible: true
39
- },
40
- {
41
- name: 'remove',
42
- display_name: 'Remove',
43
- action_type: 'default',
44
- preferences: {},
45
- visible: true
46
- }
47
- ].freeze
48
-
49
- DEFAULT_TABS = [
50
- {
51
- name: 'details',
52
- display_name: 'Details',
53
- tab_type: 'default',
54
- preferences: {},
55
- visible: true
56
- }
57
- ].freeze
58
-
59
25
  module_function
60
26
 
61
27
  def call(cache_keys = {}, current_ability = nil)
@@ -78,3 +44,4 @@ require_relative './build_schema/reorder_schema'
78
44
  require_relative './build_schema/merge_schema_configs'
79
45
  require_relative './build_schema/apply_permissions'
80
46
  require_relative './build_schema/utils'
47
+ require_relative './build_schema/defaults'
@@ -2,113 +2,123 @@
2
2
 
3
3
  module Motor
4
4
  module BuildSchema
5
- ACTIVE_STORAGE_ATTACHMENT_SCHEMA = {
6
- name: 'active_storage/attachment',
7
- slug: 'active_storage__attachments',
8
- class_name: 'ActiveStorage::Attachment',
9
- table_name: 'active_storage_attachments',
10
- primary_key: 'id',
11
- display_name: 'Attachments',
12
- display_column: 'filename',
13
- icon: 'paperclip',
14
- columns: [
15
- {
16
- name: 'id',
17
- display_name: 'ID',
18
- column_type: 'integer',
19
- access_type: 'read_only',
20
- default_value: nil,
21
- validators: [],
22
- format: {},
23
- is_array: false,
24
- reference: nil,
25
- virtual: false
26
- },
27
- {
28
- name: 'path',
29
- display_name: 'Path',
30
- column_type: 'string',
31
- access_type: 'read_only',
32
- default_value: nil,
33
- validators: [],
34
- format: {},
35
- is_array: false,
36
- reference: nil,
37
- virtual: true
38
- },
39
- {
40
- name: 'name',
41
- display_name: 'Name',
42
- column_type: 'string',
43
- access_type: 'read_write',
44
- default_value: nil,
45
- validators: [],
46
- format: {},
47
- is_array: false,
48
- reference: nil,
49
- virtual: false
50
- },
51
- {
52
- name: 'record_type',
53
- display_name: 'Record type',
54
- column_type: 'string',
55
- access_type: 'read_write',
56
- default_value: nil,
57
- validators: [],
58
- format: {},
59
- is_array: false,
60
- reference: nil,
61
- virtual: false
62
- },
63
- {
64
- name: 'record_id',
65
- display_name: 'Record',
66
- column_type: 'integer',
67
- access_type: 'read_write',
68
- default_value: nil,
69
- validators: [],
70
- format: {},
71
- is_array: false,
72
- reference: {
73
- name: 'record',
74
- display_name: 'Record',
75
- model_name: nil,
76
- reference_type: 'belongs_to',
77
- foreign_key: 'record_id',
78
- polymorphic: true
79
- },
80
- virtual: false
81
- },
82
- {
83
- name: 'file',
84
- display_name: 'File',
85
- column_type: 'file',
86
- access_type: 'write_only',
87
- default_value: nil,
88
- validators: [],
89
- format: {},
90
- is_array: false,
91
- reference: nil,
92
- virtual: false
93
- },
5
+ module ActiveStorageAttachmentSchema
6
+ module_function
7
+
8
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
9
+ def call
10
+ model = ActiveStorage::Attachment
11
+
94
12
  {
95
- name: 'created_at',
96
- display_name: 'Created at',
97
- column_type: 'datetime',
98
- access_type: 'read_only',
99
- default_value: nil,
100
- validators: [],
101
- format: {},
102
- is_array: false,
103
- reference: nil,
104
- virtual: false
105
- }
106
- ],
107
- associations: [],
108
- scopes: [],
109
- actions: Motor::BuildSchema::DEFAULT_ACTIONS.reject { |e| e[:name] == 'edit' },
110
- tabs: Motor::BuildSchema::DEFAULT_TABS,
111
- visible: true
112
- }.with_indifferent_access
13
+ name: model.name.underscore,
14
+ slug: Utils.slugify(model),
15
+ class_name: model.class.name,
16
+ table_name: model.table_name,
17
+ primary_key: model.primary_key,
18
+ display_name: model.model_name.human(count: 2, default: 'Attachments'),
19
+ display_column: 'filename',
20
+ icon: 'paperclip',
21
+ columns: [
22
+ {
23
+ name: 'id',
24
+ display_name: 'ID',
25
+ column_type: 'integer',
26
+ access_type: 'read_only',
27
+ default_value: nil,
28
+ validators: [],
29
+ format: {},
30
+ is_array: false,
31
+ reference: nil,
32
+ virtual: false
33
+ },
34
+ {
35
+ name: 'path',
36
+ display_name: model.human_attribute_name(:path),
37
+ column_type: 'string',
38
+ access_type: 'read_only',
39
+ default_value: nil,
40
+ validators: [],
41
+ format: {},
42
+ is_array: false,
43
+ reference: nil,
44
+ virtual: true
45
+ },
46
+ {
47
+ name: 'name',
48
+ display_name: model.human_attribute_name(:name),
49
+ column_type: 'string',
50
+ access_type: 'read_write',
51
+ default_value: nil,
52
+ validators: [],
53
+ format: {},
54
+ is_array: false,
55
+ reference: nil,
56
+ virtual: false
57
+ },
58
+ {
59
+ name: 'record_type',
60
+ display_name: model.human_attribute_name(:record_type),
61
+ column_type: 'string',
62
+ access_type: 'read_write',
63
+ default_value: nil,
64
+ validators: [],
65
+ format: {},
66
+ is_array: false,
67
+ reference: nil,
68
+ virtual: false
69
+ },
70
+ {
71
+ name: 'record_id',
72
+ display_name: model.human_attribute_name(:record),
73
+ column_type: 'integer',
74
+ access_type: 'read_write',
75
+ default_value: nil,
76
+ validators: [],
77
+ format: {},
78
+ is_array: false,
79
+ reference: {
80
+ name: 'record',
81
+ display_name: model.human_attribute_name(:record),
82
+ model_name: nil,
83
+ reference_type: 'belongs_to',
84
+ foreign_key: 'record_id',
85
+ polymorphic: true
86
+ },
87
+ virtual: false
88
+ },
89
+ {
90
+ name: 'file',
91
+ display_name: model.human_attribute_name(:file),
92
+ column_type: 'file',
93
+ access_type: 'write_only',
94
+ default_value: nil,
95
+ validators: [],
96
+ format: {},
97
+ is_array: false,
98
+ reference: nil,
99
+ virtual: false
100
+ },
101
+ {
102
+ name: 'created_at',
103
+ display_name: model.human_attribute_name(:created_at),
104
+ column_type: 'datetime',
105
+ access_type: 'read_only',
106
+ default_value: nil,
107
+ validators: [],
108
+ format: {},
109
+ is_array: false,
110
+ reference: nil,
111
+ virtual: false
112
+ }
113
+ ],
114
+ associations: [],
115
+ scopes: [],
116
+ actions: Motor::BuildSchema::Defaults.actions.reject { |e| e[:name] == 'edit' },
117
+ tabs: Motor::BuildSchema::Defaults.tabs,
118
+ visible: true
119
+ }.with_indifferent_access
120
+ end
121
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
122
+ end
113
123
  end
114
124
  end
@@ -47,7 +47,7 @@ module Motor
47
47
  def add_password_column!(columns)
48
48
  columns << {
49
49
  name: 'password',
50
- display_name: 'Password',
50
+ display_name: I18n.t('motor.password'),
51
51
  column_type: 'string',
52
52
  access_type: 'write_only',
53
53
  default_value: nil,
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module BuildSchema
5
+ module Defaults
6
+ module_function
7
+
8
+ # rubocop:disable Metrics/MethodLength
9
+ def actions
10
+ [
11
+ {
12
+ name: 'create',
13
+ display_name: I18n.t('motor.create'),
14
+ action_type: 'default',
15
+ preferences: {},
16
+ visible: true
17
+ },
18
+ {
19
+ name: 'edit',
20
+ display_name: I18n.t('motor.edit'),
21
+ action_type: 'default',
22
+ preferences: {},
23
+ visible: true
24
+ },
25
+ {
26
+ name: 'remove',
27
+ display_name: I18n.t('motor.remove'),
28
+ action_type: 'default',
29
+ preferences: {},
30
+ visible: true
31
+ }
32
+ ].freeze
33
+ end
34
+ # rubocop:enable Metrics/MethodLength
35
+
36
+ def tabs
37
+ [
38
+ {
39
+ name: 'details',
40
+ display_name: I18n.t('motor.details'),
41
+ tab_type: 'default',
42
+ preferences: {},
43
+ visible: true
44
+ }
45
+ ].freeze
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,6 +6,13 @@ module Motor
6
6
  MUTEX = Mutex.new
7
7
  UNIFIED_TYPES = ActiveRecordUtils::Types::UNIFIED_TYPES
8
8
 
9
+ I18N_SCOPES_KEY = 'activerecord.scopes'
10
+
11
+ ACTION_TEXT_REFLECTION_PREFIX = 'rich_text_'
12
+ ACTION_TEXT_COLUMN_SUFFIX = '_body'
13
+ ACTION_TEXT_SCOPE_PREFIX = 'with_rich_text_'
14
+ ACTIVE_STORAGE_SCOPE_PREFIX = 'with_attached_'
15
+
9
16
  module_function
10
17
 
11
18
  def call
@@ -38,6 +45,7 @@ module Motor
38
45
  models -= [ActiveRecord::SchemaMigration] if defined?(ActiveRecord::SchemaMigration)
39
46
  models -= [ActiveRecord::InternalMetadata] if defined?(ActiveRecord::InternalMetadata)
40
47
  models -= [ActiveStorage::Blob] if defined?(ActiveStorage::Blob)
48
+ models -= [ActionText::RichText] if defined?(ActionText::RichText)
41
49
  models -= [ActiveStorage::VariantRecord] if defined?(ActiveStorage::VariantRecord)
42
50
 
43
51
  models
@@ -46,7 +54,7 @@ module Motor
46
54
  def build_model_schema(model)
47
55
  model_name = model.name
48
56
 
49
- return Motor::BuildSchema::ACTIVE_STORAGE_ATTACHMENT_SCHEMA if model_name == 'ActiveStorage::Attachment'
57
+ return Motor::BuildSchema::ActiveStorageAttachmentSchema.call if model_name == 'ActiveStorage::Attachment'
50
58
 
51
59
  {
52
60
  name: model_name.underscore,
@@ -54,14 +62,14 @@ module Motor
54
62
  table_name: model.table_name,
55
63
  class_name: model.name,
56
64
  primary_key: model.primary_key,
57
- display_name: model_name.titleize.pluralize,
65
+ display_name: model.model_name.human(count: :many, default: model_name.titleize.pluralize),
58
66
  display_column: FindDisplayColumn.call(model),
59
67
  columns: fetch_columns(model),
60
68
  associations: fetch_associations(model),
61
- icon: Motor::FindIcon.call(model.name.titleize),
69
+ icon: Motor::FindIcon.call(model_name),
62
70
  scopes: fetch_scopes(model),
63
- actions: DEFAULT_ACTIONS,
64
- tabs: DEFAULT_TABS,
71
+ actions: BuildSchema::Defaults.actions,
72
+ tabs: BuildSchema::Defaults.tabs,
65
73
  visible: true
66
74
  }.with_indifferent_access
67
75
  end
@@ -70,11 +78,14 @@ module Motor
70
78
  model.defined_scopes.map do |scope_name|
71
79
  scope_name = scope_name.to_s
72
80
 
73
- next if scope_name.starts_with?('with_attached')
81
+ next if scope_name.starts_with?(ACTIVE_STORAGE_SCOPE_PREFIX)
82
+ next if scope_name.starts_with?(ACTION_TEXT_SCOPE_PREFIX)
74
83
 
75
84
  {
76
85
  name: scope_name,
77
- display_name: scope_name.humanize,
86
+ display_name: I18n.t(scope_name,
87
+ scope: [I18N_SCOPES_KEY, model.name.underscore].join('.'),
88
+ default: scope_name.humanize),
78
89
  scope_type: DEFAULT_SCOPE_TYPE,
79
90
  visible: true,
80
91
  preferences: {}
@@ -102,9 +113,9 @@ module Motor
102
113
 
103
114
  {
104
115
  name: column.name,
105
- display_name: Utils.humanize_column_name(column.name),
116
+ display_name: Utils.humanize_column_name(model.human_attribute_name(column.name)),
106
117
  column_type: is_enum ? 'string' : UNIFIED_TYPES[column.type.to_s] || column.type.to_s,
107
- is_array: column.respond_to?(:array?) && column.array?,
118
+ is_array: column.array?,
108
119
  access_type: COLUMN_NAME_ACCESS_TYPES.fetch(column.name, ColumnAccessTypes::READ_WRITE),
109
120
  default_value: default_attrs[column.name],
110
121
  validators: fetch_validators(model, column.name),
@@ -135,27 +146,47 @@ module Motor
135
146
  end
136
147
 
137
148
  def build_reflection_column(name, model, ref, default_attrs)
149
+ if !ref.polymorphic? && ref.klass.name == 'ActionText::RichText'
150
+ return build_action_text_column(name, model, ref)
151
+ end
152
+
138
153
  column_name = ref.belongs_to? ? ref.foreign_key.to_s : name
139
154
  is_attachment = !ref.polymorphic? && ref.klass.name == 'ActiveStorage::Attachment'
140
155
  access_type = ref.belongs_to? || is_attachment ? ColumnAccessTypes::READ_WRITE : ColumnAccessTypes::READ_ONLY
141
156
 
142
157
  {
143
158
  name: column_name,
144
- display_name: column_name.humanize,
159
+ display_name: model.human_attribute_name(name),
145
160
  column_type: is_attachment ? 'file' : 'integer',
146
161
  access_type: access_type,
147
162
  default_value: default_attrs[column_name],
148
163
  validators: fetch_validators(model, column_name, ref),
149
164
  format: {},
150
- reference: build_reference(name, ref),
165
+ reference: build_reference(model, name, ref),
151
166
  virtual: false
152
167
  }
153
168
  end
154
169
 
155
- def build_reference(name, reflection)
170
+ def build_action_text_column(name, model, ref)
171
+ name = name.delete_prefix(ACTION_TEXT_REFLECTION_PREFIX)
172
+
173
+ {
174
+ name: name + ACTION_TEXT_COLUMN_SUFFIX,
175
+ display_name: model.human_attribute_name(name),
176
+ column_type: 'richtext',
177
+ access_type: BuildSchema::ColumnAccessTypes::READ_WRITE,
178
+ default_value: '',
179
+ validators: fetch_validators(model, name, ref),
180
+ format: {},
181
+ reference: nil,
182
+ virtual: true
183
+ }
184
+ end
185
+
186
+ def build_reference(model, name, reflection)
156
187
  {
157
188
  name: name,
158
- display_name: name.humanize,
189
+ display_name: model.human_attribute_name(name),
159
190
  model_name: reflection.polymorphic? ? nil : reflection.klass.name.underscore,
160
191
  reference_type: reflection.belongs_to? ? 'belongs_to' : 'has_one',
161
192
  foreign_key: reflection.foreign_key,
@@ -175,7 +206,7 @@ module Motor
175
206
 
176
207
  {
177
208
  name: name,
178
- display_name: name.humanize,
209
+ display_name: model.human_attribute_name(name),
179
210
  slug: name.underscore,
180
211
  model_name: model_class.name.underscore,
181
212
  foreign_key: ref.foreign_key,