five-two-nw-olivander 0.2.0.49 → 0.2.0.51
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/assets/javascripts/controllers/datatable_index_charts_controller.js +5 -2
- data/app/components/olivander/components/menu_item_component.rb +0 -1
- data/app/components/olivander/components/resource_form_component.html.haml +1 -1
- data/app/components/olivander/components/resource_form_component.rb +0 -1
- data/app/components/olivander/components/resource_show_component.html.haml +1 -1
- data/app/components/olivander/components/resource_show_component.rb +0 -1
- data/app/components/olivander/components/tabs_component.rb +1 -1
- data/app/controllers/concerns/olivander/resources/auto_form_attributes.rb +109 -38
- data/app/controllers/concerns/olivander/resources/route_builder.rb +49 -35
- data/app/views/application/index.html.haml +4 -1
- data/lib/generators/olivander/active_record/model/templates/model.rb.tt +32 -0
- data/lib/generators/olivander/active_record/model/templates/module.rb.tt +7 -0
- data/lib/generators/olivander/active_record_generator.rb +21 -0
- data/lib/generators/olivander/controller/templates/controller.rb.tt +8 -0
- data/lib/generators/olivander/controller_generator.rb +7 -0
- data/lib/generators/olivander/datatable_generator.rb +13 -0
- data/lib/generators/olivander/infrastructure_generator.rb +76 -0
- data/lib/generators/olivander/migration/templates/create_table_migration.rb.tt +27 -0
- data/lib/generators/olivander/model_generator.rb +7 -0
- data/lib/generators/olivander/scaffold_generator.rb +117 -0
- data/lib/generators/olivander/templates/datatable/datatable.rb.tt +29 -0
- data/lib/generators/olivander/templates/infrastructure/context_builder.rb.tt +16 -0
- data/lib/generators/olivander/templates/infrastructure/hello_olivander_controller.rb.tt +11 -0
- data/lib/generators/olivander/templates/infrastructure/manifest.js +5 -0
- data/lib/generators/olivander/templates/infrastructure/menu_builder.rb.tt +32 -0
- data/lib/generators/olivander/templates/infrastructure/route_builder.rb.tt +8 -0
- data/lib/olivander/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e178485d7c7d6e52a13df286378873fadb049edcbc84a88de71763db0f29dc02
|
4
|
+
data.tar.gz: 8e60799ed84eb44f378741a5df3d95d48159837ce752fda30a5b5deac7604cc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1597949c45595dca40b42e52a6434f7c4317b7d8cead7694b0167552140d591207700341562951ad0c526099ba1d6a466cfb111205c4adcc595289a48b83ffa3
|
7
|
+
data.tar.gz: c256983a41fc1c851e311fd6675ddd4c89db14f4ea36b4591ef1670f6f91c838d001f8372cd7e694380771ac0dd206f3b757c88f229e5ce44d2a4b0a726232db
|
@@ -20,8 +20,11 @@ export default class extends Controller {
|
|
20
20
|
transformData(chart) {
|
21
21
|
var self = this
|
22
22
|
if (chart.as == 'LineChart') {
|
23
|
-
|
24
|
-
|
23
|
+
var transformed = chart.data.map(([name, obj]) => ({
|
24
|
+
name,
|
25
|
+
data: Object.entries(obj || {})
|
26
|
+
}));
|
27
|
+
return transformed
|
25
28
|
} else {
|
26
29
|
return chart.data
|
27
30
|
}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
%h3= resource_field_group_label(@resource.class, rfg.key) unless rfg.key == :default
|
4
4
|
- rfg.sections.each do |section|
|
5
5
|
.row
|
6
|
-
- section.
|
6
|
+
- section.form_fields.each do |field|
|
7
7
|
- label = field_label_for(@resource.class, field.sym)
|
8
8
|
%div{ class: section.column_class }
|
9
9
|
- if association?(field)
|
@@ -7,7 +7,7 @@
|
|
7
7
|
%td{ style: 'width: 8.33%; height: 0px' }
|
8
8
|
%tbody
|
9
9
|
- rfg.sections.each do |section|
|
10
|
-
- section.
|
10
|
+
- section.show_fields.each_slice(section.columns) do |slice|
|
11
11
|
- colspan = (12 - slice.size) / section.columns
|
12
12
|
%tr
|
13
13
|
- slice.each do |f|
|
@@ -14,7 +14,7 @@ module Olivander
|
|
14
14
|
options = args.extract_options!
|
15
15
|
@id = options[:id] || "tabs-#{SecureRandom.hex(4)}"
|
16
16
|
@card = options.key?(:card) ? !!options[:card] : true
|
17
|
-
@card_class = options.key?(:card_class) ?
|
17
|
+
@card_class = options.key?(:card_class) ? options[:card_class] : 'card-primary'
|
18
18
|
@tab_strip_id = "tab-strip-#{SecureRandom.hex(4)}"
|
19
19
|
@tab_content_id = "tab-strip-#{SecureRandom.hex(4)}"
|
20
20
|
end
|
@@ -5,7 +5,7 @@ module Olivander
|
|
5
5
|
|
6
6
|
included do
|
7
7
|
def auto_form_attributes
|
8
|
-
attributes.keys - [
|
8
|
+
attributes.keys - %w[updated_at created_at deleted_at]
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.method_missing(m, *args, **kwargs, &block)
|
@@ -16,6 +16,57 @@ module Olivander
|
|
16
16
|
super
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.tracked_attrs
|
21
|
+
@tracked_attrs ||= { readers: [], writers: [], accessors: [] }
|
22
|
+
end
|
23
|
+
|
24
|
+
# when subclassing, make sure the subclass gets a copy of parent's tracked attrs
|
25
|
+
def self.inherited(subclass)
|
26
|
+
super if defined?(super)
|
27
|
+
|
28
|
+
# copy parent's lists into subclass so they are independent arrays
|
29
|
+
tracked_attrs.each do |key, arr|
|
30
|
+
subclass.tracked_attrs[key].concat(arr.dup)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# override attr_* to capture names, then delegate to the original behavior
|
35
|
+
def self.attr_reader(*names)
|
36
|
+
tracked_attrs[:readers].concat(names.map(&:to_sym))
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.attr_writer(*names)
|
41
|
+
tracked_attrs[:writers].concat(names.map(&:to_sym))
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.attr_accessor(*names)
|
46
|
+
tracked_attrs[:accessors].concat(names.map(&:to_sym))
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
# accessors for the tracked data (unique and preserved order)
|
51
|
+
def self.tracked_attrs
|
52
|
+
@tracked_attrs ||= { readers: [], writers: [], accessors: [] }
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.readers
|
56
|
+
tracked_attrs[:readers].uniq
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.writers
|
60
|
+
tracked_attrs[:writers].uniq
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.accessors
|
64
|
+
tracked_attrs[:accessors].uniq
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.all_tracked_attributes
|
68
|
+
(readers + writers + accessors).uniq
|
69
|
+
end
|
19
70
|
end
|
20
71
|
end
|
21
72
|
|
@@ -29,54 +80,60 @@ module Olivander
|
|
29
80
|
cattr_accessor :resource_field_group_collection
|
30
81
|
|
31
82
|
def self.resource_field_groups
|
32
|
-
auto_resource_fields if
|
33
|
-
|
83
|
+
auto_resource_fields if resource_field_group_collection.nil?
|
84
|
+
resource_field_group_collection
|
34
85
|
end
|
35
86
|
|
36
|
-
def self.auto_resource_fields(columns: 2, only: [], except: [], editable: true)
|
87
|
+
def self.auto_resource_fields(columns: 2, only: [], except: [], editable: true, on_show: true, on_form: true)
|
37
88
|
return unless ActiveRecord::Base.connection.table_exists?(table_name)
|
38
89
|
|
39
90
|
if current_resource_field_group.nil?
|
40
|
-
resource_field_group do
|
41
|
-
auto_resource_fields(columns: columns, only: only, except: except, editable: editable
|
91
|
+
resource_field_group(editable: editable, on_show: on_show, on_form: on_form) do
|
92
|
+
auto_resource_fields(columns: columns, only: only, except: except, editable: editable, on_show: on_show,
|
93
|
+
on_form: on_form)
|
42
94
|
end
|
43
95
|
elsif current_resource_field_group.forced_section.nil?
|
44
96
|
resource_field_section(columns) do
|
45
|
-
auto_resource_fields(columns: columns, only: only, except: except, editable: editable
|
97
|
+
auto_resource_fields(columns: columns, only: only, except: except, editable: editable, on_show: on_show,
|
98
|
+
on_form: on_form)
|
46
99
|
end
|
47
100
|
else
|
48
101
|
if only.size.zero?
|
49
102
|
only = [
|
50
|
-
self.columns.collect{ |x| x.name.to_sym },
|
51
|
-
reflections.map{ |r| r[1].name }
|
103
|
+
self.columns.collect { |x| x.name.to_sym },
|
104
|
+
reflections.map { |r| r[1].name }
|
52
105
|
]
|
53
|
-
only << attachment_definitions.select{ |x| x[0] } if respond_to?(:attachment_definitions)
|
106
|
+
only << attachment_definitions.select { |x| x[0] } if respond_to?(:attachment_definitions)
|
54
107
|
only = only.flatten - SKIPPED_ATTRIBUTES
|
55
108
|
end
|
56
|
-
only
|
109
|
+
only -= except
|
57
110
|
only.each do |inc|
|
111
|
+
all_tracked_attributes.each do |sym|
|
112
|
+
next unless inc == sym
|
113
|
+
|
114
|
+
resource_field sym, :string, editable: false, on_show: true, on_form: false
|
115
|
+
end
|
116
|
+
|
58
117
|
self.columns.each do |att|
|
59
118
|
sym = att.name.to_sym
|
60
119
|
type = att.type
|
61
120
|
next unless inc == sym
|
62
121
|
|
63
|
-
resource_field sym, type, editable: editable
|
122
|
+
resource_field sym, type, editable: editable, on_show: on_show, on_form: on_form
|
64
123
|
end
|
65
124
|
|
66
|
-
reflections.map{ |x| x[1] }
|
67
|
-
.filter{ |x| x.foreign_key == inc || x.name == inc }
|
125
|
+
reflections.map { |x| x[1] }
|
126
|
+
.filter { |x| x.foreign_key == inc || x.name == inc }
|
68
127
|
.each do |r|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
resource_field(r.name, :association, editable: editable && !uneditable_association?(r, type))
|
74
|
-
end
|
128
|
+
type = r.association_class.name.demodulize.underscore.to_sym
|
129
|
+
resource_field(r.name, type, editable: editable && !uneditable_association?(r, type))
|
130
|
+
rescue NotImplementedError
|
131
|
+
resource_field(r.name, :association, editable: editable && !uneditable_association?(r, type))
|
75
132
|
end
|
76
133
|
|
77
134
|
next unless respond_to?(:attachment_definitions)
|
78
135
|
|
79
|
-
attachment_definitions.filter{ |x| x == inc }.each do |ad|
|
136
|
+
attachment_definitions.filter { |x| x == inc }.each do |ad|
|
80
137
|
resource_field ad[0], :file, editable: editable
|
81
138
|
end
|
82
139
|
end
|
@@ -90,42 +147,46 @@ module Olivander
|
|
90
147
|
%i[has_one_through_association].include?(type)
|
91
148
|
end
|
92
149
|
|
93
|
-
def self.resource_field_group(key = :default, editable: true,
|
150
|
+
def self.resource_field_group(key = :default, editable: true, on_show: true, on_form: true)
|
94
151
|
self.resource_field_group_collection ||= []
|
95
|
-
self.current_resource_field_group = resource_field_group_collection.select{ |x| x.key == key}.first
|
152
|
+
self.current_resource_field_group = resource_field_group_collection.select { |x| x.key == key }.first
|
96
153
|
unless current_resource_field_group.present?
|
97
|
-
self.current_resource_field_group = ResourceFieldGroup.new(key, editable)
|
98
|
-
self.resource_field_group_collection <<
|
154
|
+
self.current_resource_field_group = ResourceFieldGroup.new(key, editable, on_show, on_form)
|
155
|
+
self.resource_field_group_collection << current_resource_field_group
|
99
156
|
end
|
100
157
|
yield
|
101
158
|
self.current_resource_field_group = nil
|
102
159
|
end
|
103
160
|
|
104
|
-
def self.resource_field_section(columns = nil
|
105
|
-
|
161
|
+
def self.resource_field_section(columns = nil)
|
162
|
+
current_resource_field_group.forced_section = current_resource_field_group.next_section(columns)
|
106
163
|
yield
|
107
|
-
|
164
|
+
current_resource_field_group.forced_section = nil
|
108
165
|
end
|
109
166
|
|
110
|
-
def self.resource_field(sym, type = :string, editable: nil)
|
111
|
-
|
167
|
+
def self.resource_field(sym, type = :string, editable: nil, on_show: true, on_form: true)
|
168
|
+
current_resource_field_group.add_field(sym, type, editable, on_show, on_form)
|
112
169
|
end
|
113
170
|
end
|
114
171
|
|
115
172
|
class ResourceFieldGroup
|
116
|
-
attr_accessor :fields, :key, :editable, :forced_section, :sections
|
173
|
+
attr_accessor :fields, :key, :editable, :forced_section, :sections, :on_show, :on_form
|
117
174
|
|
118
|
-
def initialize(key, editable)
|
175
|
+
def initialize(key, editable, on_show, on_form)
|
119
176
|
self.key = key
|
120
177
|
self.editable = editable
|
178
|
+
self.on_show = on_show
|
179
|
+
self.on_form = on_form
|
121
180
|
self.fields = []
|
122
181
|
self.sections = []
|
123
182
|
end
|
124
183
|
|
125
|
-
def add_field(sym, type, editable)
|
184
|
+
def add_field(sym, type, editable, on_show, on_form)
|
126
185
|
e = editable.nil? ? self.editable : editable
|
186
|
+
s = on_show
|
187
|
+
f = on_form
|
127
188
|
section = forced_section || next_section
|
128
|
-
field = ResourceField.new(sym, type, e, self)
|
189
|
+
field = ResourceField.new(sym, type, e, s, f, self)
|
129
190
|
section.fields << field
|
130
191
|
fields << field
|
131
192
|
end
|
@@ -137,7 +198,7 @@ module Olivander
|
|
137
198
|
end
|
138
199
|
|
139
200
|
def max_section_columns
|
140
|
-
sections.collect{ |x| x.columns }.max
|
201
|
+
sections.collect { |x| x.columns }.max
|
141
202
|
end
|
142
203
|
end
|
143
204
|
|
@@ -150,16 +211,26 @@ module Olivander
|
|
150
211
|
end
|
151
212
|
|
152
213
|
def column_class
|
153
|
-
"col-md-#{12/columns}"
|
214
|
+
"col-md-#{12 / columns}"
|
215
|
+
end
|
216
|
+
|
217
|
+
def show_fields
|
218
|
+
fields.select { |f| f.on_show }
|
219
|
+
end
|
220
|
+
|
221
|
+
def form_fields
|
222
|
+
fields.select { |f| f.on_form }
|
154
223
|
end
|
155
224
|
end
|
156
225
|
|
157
226
|
class ResourceField
|
158
|
-
attr_accessor :sym, :type, :editable
|
227
|
+
attr_accessor :sym, :type, :editable, :on_show, :on_form
|
159
228
|
|
160
|
-
def initialize(sym, type, editable,
|
229
|
+
def initialize(sym, type, editable, on_show, on_form, _group)
|
161
230
|
self.sym = sym
|
162
231
|
self.type = type
|
232
|
+
self.on_show = on_show
|
233
|
+
self.on_form = on_form
|
163
234
|
self.editable = editable
|
164
235
|
end
|
165
236
|
end
|
@@ -63,10 +63,11 @@ module Olivander
|
|
63
63
|
end
|
64
64
|
|
65
65
|
class RoutedResource
|
66
|
-
attr_accessor :model, :namespaces, :actions
|
66
|
+
attr_accessor :model, :nested_models, :namespaces, :actions
|
67
67
|
|
68
68
|
def initialize(model, namespaces, crud_actions)
|
69
69
|
self.model = model
|
70
|
+
self.nested_models = []
|
70
71
|
self.namespaces = namespaces
|
71
72
|
self.actions = []
|
72
73
|
%i[index new create edit show update destroy].each do |ca|
|
@@ -134,10 +135,15 @@ module Olivander
|
|
134
135
|
|
135
136
|
class_methods do
|
136
137
|
def resource(model, only: DEFAULT_CRUD_ACTIONS, except: [], namespaces: [])
|
138
|
+
parent_resource = self.current_resource
|
137
139
|
self.current_resource = RoutedResource.new(model, namespaces, DEFAULT_CRUD_ACTIONS & (only - except))
|
140
|
+
if parent_resource
|
141
|
+
parent_resource.nested_models << self.current_resource
|
142
|
+
else
|
143
|
+
resources[model] = current_resource
|
144
|
+
end
|
138
145
|
yield if block_given?
|
139
|
-
|
140
|
-
self.current_resource = nil
|
146
|
+
self.current_resource = parent_resource
|
141
147
|
end
|
142
148
|
|
143
149
|
def action(sym, **kwargs)
|
@@ -171,44 +177,52 @@ module Olivander
|
|
171
177
|
build_resource_route(mapper, r, r.namespaces.last(r.namespaces.size - 1))
|
172
178
|
end
|
173
179
|
else
|
174
|
-
mapper
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
180
|
+
map_it(mapper, r)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def map_it(mapper, r)
|
185
|
+
mapper.resources r.model, only: [] do
|
186
|
+
mapper.collection do
|
187
|
+
r.collection_actions.each do |ba|
|
188
|
+
next if ba.no_route
|
189
|
+
next if ba.action == :new
|
190
|
+
|
191
|
+
if ba.confirm
|
192
|
+
mapper.get ba.action, action: "confirm_#{ba.action}"
|
193
|
+
set_controller_and_helper(ba)
|
194
|
+
mapper.post ba.action
|
195
|
+
else
|
196
|
+
mapper.send(ba.verb, ba.action)
|
197
|
+
set_controller_and_helper(ba)
|
188
198
|
end
|
189
199
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
200
|
+
end
|
201
|
+
|
202
|
+
if r.collection_actions.select { |x| x.action == :new }.size.positive?
|
203
|
+
mapper.new do
|
204
|
+
mapper.get :new
|
195
205
|
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
206
|
+
end
|
207
|
+
|
208
|
+
mapper.member do
|
209
|
+
r.member_actions.each do |ma|
|
210
|
+
next if ma.no_route
|
211
|
+
|
212
|
+
if ma.confirm
|
213
|
+
mapper.get ma.action, action: "confirm_#{ma.action}"
|
214
|
+
set_controller_and_helper(ma)
|
215
|
+
mapper.post ma.action
|
216
|
+
else
|
217
|
+
mapper.send(ma.verb, ma.action)
|
218
|
+
set_controller_and_helper(ma)
|
209
219
|
end
|
210
220
|
end
|
211
221
|
end
|
222
|
+
|
223
|
+
r.nested_models.each do |nm|
|
224
|
+
map_it(mapper, nm)
|
225
|
+
end
|
212
226
|
end
|
213
227
|
end
|
214
228
|
end
|
@@ -23,7 +23,10 @@
|
|
23
23
|
%button.btn.btn-tool{ type: :button, 'data-card-widget': :maximize }
|
24
24
|
%i.fas.fa-expand
|
25
25
|
.card-body{ style: 'height: 220px' }
|
26
|
-
|
26
|
+
- if chart[:as] == 'LineChart'
|
27
|
+
= send(chart[:as].underscore, @datatable.to_json[:charts][k][:data].map{ |x| {name: x[0], data: x[1]}}, id: chart[:name], height: '90%')
|
28
|
+
- else
|
29
|
+
= send(chart[:as].underscore, @datatable.to_json[:charts][k][:data], id: chart[:name], height: '90%', adapter: 'google')
|
27
30
|
|
28
31
|
= content_for :datatable_charts
|
29
32
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Model class for <%= plural_table_name %> table
|
5
|
+
class <%= class_name %> < <%= parent_class_name.classify %>
|
6
|
+
include HagridChassis::MultiTenant
|
7
|
+
include HagridChassis::Audited
|
8
|
+
|
9
|
+
<% attributes.select(&:reference?).each do |attribute| -%>
|
10
|
+
belongs_to :<%= attribute.name %><%= ", polymorphic: true" if attribute.polymorphic? %>
|
11
|
+
<% end -%>
|
12
|
+
<% attributes.select(&:rich_text?).each do |attribute| -%>
|
13
|
+
has_rich_text :<%= attribute.name %>
|
14
|
+
<% end -%>
|
15
|
+
<% attributes.select(&:attachment?).each do |attribute| -%>
|
16
|
+
has_one_attached :<%= attribute.name %>
|
17
|
+
<% end -%>
|
18
|
+
<% attributes.select(&:attachments?).each do |attribute| -%>
|
19
|
+
has_many_attached :<%= attribute.name %>
|
20
|
+
<% end -%>
|
21
|
+
<% attributes.select(&:token?).each do |attribute| -%>
|
22
|
+
has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %>
|
23
|
+
<% end -%>
|
24
|
+
<% if attributes.any?(&:password_digest?) -%>
|
25
|
+
has_secure_password
|
26
|
+
<% end -%>
|
27
|
+
|
28
|
+
effective_resource do
|
29
|
+
<% attributes.each do |attribute| -%><%= " #{attribute.name} :#{attribute.type}\n" unless attribute.type == :references %><% end -%>
|
30
|
+
end
|
31
|
+
end
|
32
|
+
<% end -%>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rails/generators/active_record/model/model_generator"
|
2
|
+
|
3
|
+
module Olivander
|
4
|
+
class ActiveRecordGenerator < ActiveRecord::Generators::ModelGenerator
|
5
|
+
desc "This will generate ActiveRecord files using Olivander templates"
|
6
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
7
|
+
|
8
|
+
# redefine the source path so we can isolate our templates
|
9
|
+
# note that ActiveRecords's generator defines a path like:
|
10
|
+
# ../../migration/templates/create_table_migration.rb
|
11
|
+
# so we have to put two dummy path positions to counter-act
|
12
|
+
# the ../.. and of course append .tt for thor to treat the file
|
13
|
+
# as a template
|
14
|
+
source_root File.expand_path('active_record/model/templates', __dir__)
|
15
|
+
|
16
|
+
def create_migration_file
|
17
|
+
self.attributes = shell.base.attributes
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Olivander
|
2
|
+
class DatatableGenerator < Rails::Generators::NamedBase
|
3
|
+
include Rails::Generators::ResourceHelpers
|
4
|
+
|
5
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
6
|
+
|
7
|
+
source_root File.expand_path('templates/datatable', __dir__)
|
8
|
+
|
9
|
+
def create_datatable
|
10
|
+
template "datatable.rb", File.join("app/datatables", controller_file_path, "../#{file_name}_datatable.rb")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Olivander
|
2
|
+
class InfrastructureGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('templates/infrastructure', __dir__)
|
4
|
+
|
5
|
+
# boolean option — use --no-application-controller to disable
|
6
|
+
class_option :application_controller,
|
7
|
+
type: :boolean,
|
8
|
+
default: true,
|
9
|
+
desc: "Modify ApplicationController to include infrastructure hooks"
|
10
|
+
|
11
|
+
def create_infrastructure
|
12
|
+
create_builders
|
13
|
+
setup_asset_pipeline
|
14
|
+
modify_application_controller
|
15
|
+
create_hello_olivander
|
16
|
+
route 'root "hello_olivander#index"'
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_builders
|
22
|
+
template "route_builder.rb", File.join("app/services", "route_builder.rb")
|
23
|
+
template "context_builder.rb", File.join("app/services", "context_builder.rb")
|
24
|
+
template "menu_builder.rb", File.join("app/services", "menu_builder.rb")
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup_asset_pipeline
|
28
|
+
template "manifest.js", File.join("app/assets/config", "manifest.js")
|
29
|
+
pin_all = 'pin_all_from "#{Olivander.root}/../app/assets/javascripts/controllers", under: "controllers"'
|
30
|
+
inject_into_file 'config/importmap.rb', pin_all, verbose: true, force: false
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_hello_olivander
|
34
|
+
template "hello_olivander_controller.rb", File.join("app/controllers", "hello_olivander_controller.rb")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Insert a safe snippet into ApplicationController
|
38
|
+
def modify_application_controller
|
39
|
+
unless options[:application_controller]
|
40
|
+
say_status :skip, "ApplicationController modification skipped", :yellow
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
target = File.join("app", "controllers", "application_controller.rb")
|
45
|
+
snippet = <<~RUBY
|
46
|
+
|
47
|
+
# --- Olivander infrastructure additions (generated) ---
|
48
|
+
# If you want to remove this, delete the lines between the markers.
|
49
|
+
before_action :build_context
|
50
|
+
|
51
|
+
def build_context
|
52
|
+
# can?(:build, :context)
|
53
|
+
ContextBuilder.build_context
|
54
|
+
end
|
55
|
+
|
56
|
+
def authorize!(*args)
|
57
|
+
# implement authorization logic
|
58
|
+
end
|
59
|
+
# ------------------------------------------------------
|
60
|
+
RUBY
|
61
|
+
|
62
|
+
if File.exist?(target)
|
63
|
+
inject_into_class target, "ApplicationController", snippet.indent(2)
|
64
|
+
say_status :insert, "added infrastructure hooks to #{target}", :green
|
65
|
+
else
|
66
|
+
# create a minimal ApplicationController if none exists
|
67
|
+
create_file target, <<~RUBY
|
68
|
+
class ApplicationController < ActionController::Base
|
69
|
+
#{snippet.indent(2)}
|
70
|
+
end
|
71
|
+
RUBY
|
72
|
+
say_status :create, "#{target} (new)", :green
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
2
|
+
def change
|
3
|
+
create_table :<%= table_name %><%= primary_key_type %> do |t|
|
4
|
+
<% attributes.each do |attribute| -%>
|
5
|
+
<% foreign_key_type ||= nil -%>
|
6
|
+
<% if attribute.password_digest? -%>
|
7
|
+
t.string :password_digest<%= attribute.inject_options %>
|
8
|
+
<% elsif attribute.token? -%>
|
9
|
+
t.string :<%= attribute.name %><%= attribute.inject_options %>
|
10
|
+
<% elsif attribute.reference? -%>
|
11
|
+
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
|
12
|
+
<% elsif !attribute.virtual? -%>
|
13
|
+
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
|
14
|
+
<% end -%>
|
15
|
+
<% end -%>
|
16
|
+
<% if options[:timestamps] %>
|
17
|
+
t.audits
|
18
|
+
<% end -%>
|
19
|
+
end
|
20
|
+
<% attributes.select(&:token?).each do |attribute| -%>
|
21
|
+
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
|
22
|
+
<% end -%>
|
23
|
+
<% attributes_with_index.each do |attribute| -%>
|
24
|
+
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
|
25
|
+
<% end -%>
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Olivander
|
2
|
+
class ScaffoldGenerator < Rails::Generators::NamedBase
|
3
|
+
include Rails::Generators::ResourceHelpers
|
4
|
+
|
5
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
6
|
+
class_option :menu,
|
7
|
+
type: :boolean,
|
8
|
+
default: true,
|
9
|
+
desc: "Modify MenuBuilder to include menu item"
|
10
|
+
|
11
|
+
source_root File.expand_path('templates/scaffold', __dir__)
|
12
|
+
|
13
|
+
hook_for :orm, as: :model, required: true do |instance, controller|
|
14
|
+
instance.invoke controller, [ instance.name ], instance.options.merge({ test_framework: false })
|
15
|
+
end
|
16
|
+
|
17
|
+
hook_for :datatable, type: :boolean, default: true
|
18
|
+
|
19
|
+
hook_for :resource_controller do |instance, controller|
|
20
|
+
instance.invoke controller, [ instance.name.pluralize ], instance.options.merge({ helper: false, test_framework: false, assets: false })
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_views
|
24
|
+
# template "views/_form.html.haml", File.join("app/views", controller_file_path, '_form.html.haml')
|
25
|
+
# template "views/_model.html.haml", File.join("app/views", controller_file_path, "_#{file_name}.html.haml")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make an entry in \Rails routing file <tt>config/routes.rb</tt>
|
29
|
+
#
|
30
|
+
# route "root 'welcome#index'"
|
31
|
+
# route "root 'admin#index'", namespace: :admin
|
32
|
+
def handle_route
|
33
|
+
return if options[:actions].present?
|
34
|
+
|
35
|
+
use_route_builder = File.exist?(Rails.root.join("app/services/route_builder.rb"))
|
36
|
+
if use_route_builder
|
37
|
+
route_for_route_builder
|
38
|
+
else
|
39
|
+
route "resources :#{file_name.pluralize}", namespace: regular_class_path
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_menu
|
44
|
+
say_status :menu, "Handling menu option", :yellow
|
45
|
+
return unless options[:menu]
|
46
|
+
|
47
|
+
target = 'app/services/menu_builder.rb'
|
48
|
+
unless File.exist?(target)
|
49
|
+
say_status :error, "#{target} not found, skipping menu injection", :red
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
# The exact line we want to insert (note trailing comma)
|
54
|
+
new_line = <<~RUBY.indent(6)
|
55
|
+
builder.build_menu_item(key: "#{file_name.pluralize}", url: builder.#{file_name.pluralize}_path),
|
56
|
+
RUBY
|
57
|
+
|
58
|
+
file_contents = File.read(target)
|
59
|
+
# avoid inserting duplicates
|
60
|
+
if file_contents.include?(new_line.strip)
|
61
|
+
say_status :skip, "menu item already present in #{target}", :yellow
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
# Insert before the closing bracket of the array (a line that only contains optional whitespace and `]`)
|
66
|
+
inject_into_file target,
|
67
|
+
new_line,
|
68
|
+
before: /^\s*\]\s*$/m,
|
69
|
+
verbose: true,
|
70
|
+
force: false
|
71
|
+
|
72
|
+
say_status :done, "Inserted menu item into #{target}", :green
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def route_for_route_builder
|
78
|
+
route 'RouteBuilder.build_routes(self)'
|
79
|
+
namespace = regular_class_path
|
80
|
+
namespace = Array(namespace)
|
81
|
+
routing_code = "resource :#{file_name.pluralize}, namespaces:[#{namespace.map{ |n| ":#{n}" }.join(', ')}]"
|
82
|
+
namespace_pattern = namespace.each_with_index.reverse_each.reduce(nil) do |pattern, (name, i)|
|
83
|
+
cummulative_margin = "\\#{i + 1}[ ]{2}"
|
84
|
+
blank_or_indented_line = "^[ ]*\n|^#{cummulative_margin}.*\n"
|
85
|
+
"(?:(?:#{blank_or_indented_line})*?^(#{cummulative_margin})namespace :#{name} do\n#{pattern})?"
|
86
|
+
end.then do |pattern|
|
87
|
+
/^([ ]*).+include Olivander::Resources::RouteBuilder*\n#{pattern}/
|
88
|
+
end
|
89
|
+
# routing_code = namespace.reverse.reduce(routing_code) do |code, name|
|
90
|
+
# "namespace :#{name} do\n#{rebase_indentation(code, 2)}end"
|
91
|
+
# end
|
92
|
+
|
93
|
+
log :route, routing_code
|
94
|
+
|
95
|
+
target_file_name = "app/services/route_builder.rb"
|
96
|
+
in_root do
|
97
|
+
if namespace_match = match_file(target_file_name, namespace_pattern)
|
98
|
+
base_indent, *, existing_block_indent = namespace_match.captures.compact.map(&:length)
|
99
|
+
existing_line_pattern = /^[ ]{,#{existing_block_indent}}\S.+\n?/
|
100
|
+
routing_code = rebase_indentation(routing_code, base_indent + 1).gsub(existing_line_pattern, "")
|
101
|
+
namespace_pattern = /#{Regexp.escape namespace_match.to_s}/
|
102
|
+
end
|
103
|
+
|
104
|
+
inject_into_file target_file_name, routing_code, after: namespace_pattern, verbose: true, force: false
|
105
|
+
|
106
|
+
if behavior == :revoke && namespace.any? && namespace_match
|
107
|
+
empty_block_pattern = /(#{namespace_pattern})((?:\s*end\n){1,#{namespace.size}})/
|
108
|
+
gsub_file target_file_name, empty_block_pattern, verbose: false, force: true do |matched|
|
109
|
+
beginning, ending = empty_block_pattern.match(matched).captures
|
110
|
+
ending.sub!(/\A\s*end\n/, "") while !ending.empty? && beginning.sub!(/^[ ]*namespace .+ do\n\s*\z/, "")
|
111
|
+
beginning + ending
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Datatable class for <%= plural_table_name %> table
|
5
|
+
class <%= class_name %>Datatable < ::Olivander::Datatable
|
6
|
+
auto_datatable <%= class_name %>
|
7
|
+
# only: %w[],
|
8
|
+
# hide: %w[],
|
9
|
+
# show: %w[],
|
10
|
+
# scopes: %i[],
|
11
|
+
# collection: <%= class_name %>.all
|
12
|
+
|
13
|
+
# filters do
|
14
|
+
# scope :all, default: true
|
15
|
+
# end
|
16
|
+
|
17
|
+
# datatable do
|
18
|
+
# order :id, :desc
|
19
|
+
<% attributes.each do |attribute| -%>
|
20
|
+
# col :<%= attribute.name %>
|
21
|
+
<% end -%>
|
22
|
+
# actions_col
|
23
|
+
# end
|
24
|
+
|
25
|
+
# collection do
|
26
|
+
# <%= class_name %>.all
|
27
|
+
# end
|
28
|
+
end
|
29
|
+
<% end -%>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class ContextBuilder
|
2
|
+
include Rails.application.routes.url_helpers
|
3
|
+
|
4
|
+
def self.build_context
|
5
|
+
builder = ContextBuilder.new
|
6
|
+
Olivander::CurrentContext.build do |_ctx, app_ctx, _user, _ability|
|
7
|
+
app_ctx.name = 'Set Your System Name'
|
8
|
+
app_ctx.logo.url = '/images/logo.png'
|
9
|
+
app_ctx.logo.alt = 'Set Your Logo Alt Text'
|
10
|
+
app_ctx.company.name = 'Set Your Company Name'
|
11
|
+
app_ctx.menu_items = MenuBuilder.build_menu_items
|
12
|
+
app_ctx.route_builder = RouteBuilder
|
13
|
+
app_ctx.sign_out_path = builder.root_path
|
14
|
+
end.application_context
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# a default landing page for Olivander
|
4
|
+
class HelloOlivanderController < ApplicationController
|
5
|
+
layout 'olivander/adminlte/main'
|
6
|
+
|
7
|
+
def index
|
8
|
+
@page_title = 'Welcome to Olivander'
|
9
|
+
render({html: "This is only the beginning...".html_safe, layout: true})
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class MenuBuilder
|
2
|
+
include Rails.application.routes.url_helpers
|
3
|
+
|
4
|
+
def self.build_menu_items
|
5
|
+
builder = MenuBuilder.new
|
6
|
+
[
|
7
|
+
builder.build_menu_item(key: 'root', url: builder.root_path),
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
def condition_proc(verb, noun)
|
12
|
+
proc { Olivander::CurrentContext.ability.can? verb, noun }
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_menu_item(key: nil, controller: nil, action: nil, url: nil, is_module: false, condition: nil, items: nil)
|
16
|
+
url ||= url_for(controller:, action:, only_path: true)
|
17
|
+
key ||= url.gsub('.', '_').gsub(' ', '_')
|
18
|
+
|
19
|
+
condition = proc { true } if !condition && is_module
|
20
|
+
|
21
|
+
unless items
|
22
|
+
submenu_key = "#{key.gsub('.', '_')}_submenu"
|
23
|
+
items = proc { send(submenu_key) } if respond_to?(submenu_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
key = key.gsub('help.', '') if !I18n.exists?("menus.#{key}") && key.starts_with?('help.')
|
27
|
+
mi = Olivander::Menus::MenuItem.new(key, url, nil, is_module:)
|
28
|
+
mi.with_condition { condition.is_a?(Proc) ? condition.call : condition } if condition.present?
|
29
|
+
mi.with_submenu_items { items.call } if items.present?
|
30
|
+
mi
|
31
|
+
end
|
32
|
+
end
|
data/lib/olivander/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: five-two-nw-olivander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0.
|
4
|
+
version: 0.2.0.51
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chartkick
|
@@ -310,6 +310,22 @@ files:
|
|
310
310
|
- config/locales/simple_form.en.yml
|
311
311
|
- config/routes.rb
|
312
312
|
- lib/five-two-nw-olivander.rb
|
313
|
+
- lib/generators/olivander/active_record/model/templates/model.rb.tt
|
314
|
+
- lib/generators/olivander/active_record/model/templates/module.rb.tt
|
315
|
+
- lib/generators/olivander/active_record_generator.rb
|
316
|
+
- lib/generators/olivander/controller/templates/controller.rb.tt
|
317
|
+
- lib/generators/olivander/controller_generator.rb
|
318
|
+
- lib/generators/olivander/datatable_generator.rb
|
319
|
+
- lib/generators/olivander/infrastructure_generator.rb
|
320
|
+
- lib/generators/olivander/migration/templates/create_table_migration.rb.tt
|
321
|
+
- lib/generators/olivander/model_generator.rb
|
322
|
+
- lib/generators/olivander/scaffold_generator.rb
|
323
|
+
- lib/generators/olivander/templates/datatable/datatable.rb.tt
|
324
|
+
- lib/generators/olivander/templates/infrastructure/context_builder.rb.tt
|
325
|
+
- lib/generators/olivander/templates/infrastructure/hello_olivander_controller.rb.tt
|
326
|
+
- lib/generators/olivander/templates/infrastructure/manifest.js
|
327
|
+
- lib/generators/olivander/templates/infrastructure/menu_builder.rb.tt
|
328
|
+
- lib/generators/olivander/templates/infrastructure/route_builder.rb.tt
|
313
329
|
- lib/o18n.rb
|
314
330
|
- lib/olivander.rb
|
315
331
|
- lib/olivander/application_context.rb
|