brick 1.0.190 → 1.0.192
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +175 -48
- data/lib/brick/frameworks/rails/engine.rb +34 -14
- data/lib/brick/frameworks/rails/form_builder.rb +2 -1
- data/lib/brick/frameworks/rails/form_tags.rb +13 -4
- data/lib/brick/frameworks/rails.rb +6 -0
- data/lib/brick/route_mapper.rb +348 -0
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +10 -335
- data/lib/generators/brick/controllers_generator.rb +91 -0
- data/lib/generators/brick/migration_builder.rb +224 -160
- data/lib/generators/brick/migrations_generator.rb +1 -0
- data/lib/generators/brick/models_generator.rb +2 -2
- data/lib/generators/brick/salesforce_migrations_generator.rb +101 -0
- data/lib/generators/brick/salesforce_schema.rb +105 -0
- metadata +6 -2
data/lib/brick.rb
CHANGED
@@ -107,7 +107,7 @@ module Brick
|
|
107
107
|
end
|
108
108
|
|
109
109
|
attr_accessor :default_schema, :db_schemas, :test_schema,
|
110
|
-
:
|
110
|
+
:established_drf,
|
111
111
|
:is_oracle, :is_eager_loading, :auto_models, :initializer_loaded,
|
112
112
|
:table_name_lookup
|
113
113
|
::Brick.auto_models = []
|
@@ -619,6 +619,10 @@ module Brick
|
|
619
619
|
Brick.config.license = key
|
620
620
|
end
|
621
621
|
|
622
|
+
def omit_empty_tables_in_dropdown=(setting)
|
623
|
+
Brick.config.omit_empty_tables_in_dropdown = setting
|
624
|
+
end
|
625
|
+
|
622
626
|
def always_load_fields=(field_set)
|
623
627
|
Brick.config.always_load_fields = field_set
|
624
628
|
end
|
@@ -877,339 +881,6 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
877
881
|
end
|
878
882
|
end
|
879
883
|
|
880
|
-
module RouteMapper
|
881
|
-
def add_brick_routes
|
882
|
-
routeset_to_use = ::Rails.application.routes
|
883
|
-
path_prefix = ::Brick.config.path_prefix
|
884
|
-
existing_controllers = routeset_to_use.routes.each_with_object({}) do |r, s|
|
885
|
-
if r.verb == 'GET' && (c = r.defaults[:controller])
|
886
|
-
path = r.path.ast.to_s
|
887
|
-
path = path[0..((path.index('(') || 0) - 1)]
|
888
|
-
# Skip adding this if it's the default_route_fallback set from the initializers/brick.rb file
|
889
|
-
next if "#{path}##{r.defaults[:action]}" == ::Brick.established_drf
|
890
|
-
|
891
|
-
# next unless [:index, :show, :new, :edit].incude?(a = r.defaults[:action])
|
892
|
-
|
893
|
-
# c_parts = c.split('/')
|
894
|
-
# while c_parts.length > 0
|
895
|
-
# c_dotted = c_parts.join('.')
|
896
|
-
# if (relation = ::Brick.relations[c_dotted]) # Does it match up with an existing Brick table / resource name?
|
897
|
-
# puts path
|
898
|
-
# puts " #{c_dotted}##{r.defaults[:action]}"
|
899
|
-
# s[c_dotted.tr('.', '/')] = nil
|
900
|
-
# break
|
901
|
-
# end
|
902
|
-
# c_parts.shift
|
903
|
-
# end
|
904
|
-
s[c] = nil
|
905
|
-
end
|
906
|
-
end
|
907
|
-
|
908
|
-
tables = []
|
909
|
-
views = []
|
910
|
-
table_class_length = 38 # Length of "Classes that can be built from tables:"
|
911
|
-
view_class_length = 37 # Length of "Classes that can be built from views:"
|
912
|
-
|
913
|
-
brick_namespace_create = lambda do |path_names, res_name, options|
|
914
|
-
if path_names&.present?
|
915
|
-
if (path_name = path_names.pop).is_a?(Array)
|
916
|
-
module_name = path_name[1]
|
917
|
-
path_name = path_name.first
|
918
|
-
end
|
919
|
-
send(:scope, { module: module_name || path_name, path: path_name, as: path_name }) do
|
920
|
-
brick_namespace_create.call(path_names, res_name, options)
|
921
|
-
end
|
922
|
-
else
|
923
|
-
send(:resources, res_name.to_sym, **options)
|
924
|
-
end
|
925
|
-
end
|
926
|
-
|
927
|
-
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
928
|
-
# If auto-controllers and auto-models are both enabled then this makes sense:
|
929
|
-
controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
|
930
|
-
sti_subclasses = ::Brick.config.sti_namespace_prefixes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
|
931
|
-
# Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
|
932
|
-
s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
|
933
|
-
end
|
934
|
-
versioned_views = {} # Track which views have already been done for each api_root
|
935
|
-
::Brick.relations.each do |k, v|
|
936
|
-
next if k.is_a?(Symbol)
|
937
|
-
|
938
|
-
if (schema_name = v.fetch(:schema, nil))
|
939
|
-
schema_prefix = "#{schema_name}."
|
940
|
-
end
|
941
|
-
|
942
|
-
resource_name = v.fetch(:resource, nil)
|
943
|
-
next if !resource_name ||
|
944
|
-
existing_controllers.key?(
|
945
|
-
controller_prefix + (resource_name = "#{schema_prefix&.tr('.', '/')}#{resource_name}".pluralize)
|
946
|
-
)
|
947
|
-
|
948
|
-
object_name = k.split('.').last # Take off any first schema part
|
949
|
-
|
950
|
-
full_schema_prefix = if (full_aps = aps = v.fetch(:auto_prefixed_schema, nil))
|
951
|
-
aps = aps[0..-2] if aps[-1] == '_'
|
952
|
-
(schema_prefix&.dup || +'') << "#{aps}."
|
953
|
-
else
|
954
|
-
schema_prefix
|
955
|
-
end
|
956
|
-
|
957
|
-
# Track routes being built
|
958
|
-
if (class_name = v.fetch(:class_name, nil))
|
959
|
-
if v.key?(:isView)
|
960
|
-
view_class_length = class_name.length if class_name.length > view_class_length
|
961
|
-
views
|
962
|
-
else
|
963
|
-
table_class_length = class_name.length if class_name.length > table_class_length
|
964
|
-
tables
|
965
|
-
end << [class_name, aps, k.tr('.', '/')[full_aps&.length || 0 .. -1]]
|
966
|
-
end
|
967
|
-
|
968
|
-
options = {}
|
969
|
-
options[:only] = [:index, :show] if v.key?(:isView)
|
970
|
-
|
971
|
-
# First do the normal routes
|
972
|
-
prefixes = []
|
973
|
-
prefixes << [aps, v[:class_name]&.split('::')[-2]&.underscore] if aps
|
974
|
-
prefixes << schema_name if schema_name
|
975
|
-
prefixes << path_prefix if path_prefix
|
976
|
-
brick_namespace_create.call(prefixes, v[:resource], options)
|
977
|
-
sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
|
978
|
-
brick_namespace_create.call(prefixes, sc.underscore.tr('/', '_').pluralize, options)
|
979
|
-
end
|
980
|
-
|
981
|
-
# Now the API routes if necessary
|
982
|
-
full_resource = nil
|
983
|
-
::Brick.api_roots&.each do |api_root|
|
984
|
-
api_done_views = (versioned_views[api_root] ||= {})
|
985
|
-
found = nil
|
986
|
-
test_ver_num = nil
|
987
|
-
view_relation = nil
|
988
|
-
# If it's a view then see if there's a versioned one available by searching for resource names
|
989
|
-
# versioned with the closest number (equal to or less than) compared with our API version number.
|
990
|
-
if v.key?(:isView)
|
991
|
-
if (ver = object_name.match(/^v([\d_]*)/)&.captures&.first) && ver[-1] == '_'
|
992
|
-
core_object_name = object_name[ver.length + 1..-1]
|
993
|
-
next if api_done_views.key?(unversioned = "#{schema_prefix}v_#{core_object_name}")
|
994
|
-
|
995
|
-
# Expect that the last item in the path generally holds versioning information
|
996
|
-
api_ver = api_root.split('/')[-1]&.gsub('_', '.')
|
997
|
-
vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
|
998
|
-
# Was: .to_d
|
999
|
-
test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
|
1000
|
-
# puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
|
1001
|
-
|
1002
|
-
next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
|
1003
|
-
|
1004
|
-
test_ver_num -= 1 until test_ver_num.zero? ||
|
1005
|
-
(view_relation = ::Brick.relations.fetch(
|
1006
|
-
found = "#{schema_prefix}v#{test_ver_num}_#{core_object_name}", nil
|
1007
|
-
))
|
1008
|
-
api_done_views[unversioned] = nil # Mark that for this API version this view is done
|
1009
|
-
|
1010
|
-
# puts "Found #{found}" if view_relation
|
1011
|
-
# If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
|
1012
|
-
# fall back to simply looking for "v_view_name", and then finally "view_name".
|
1013
|
-
no_v_prefix_name = "#{schema_prefix}#{core_object_name}"
|
1014
|
-
standard_prefix = 'v_'
|
1015
|
-
else
|
1016
|
-
core_object_name = object_name
|
1017
|
-
end
|
1018
|
-
if (rvp = ::Brick.config.api_remove_view_prefix) && core_object_name.start_with?(rvp)
|
1019
|
-
core_object_name.slice!(0, rvp.length)
|
1020
|
-
end
|
1021
|
-
no_prefix_name = "#{schema_prefix}#{core_object_name}"
|
1022
|
-
unversioned = "#{schema_prefix}#{standard_prefix}#{::Brick.config.api_add_view_prefix}#{core_object_name}"
|
1023
|
-
else
|
1024
|
-
unversioned = k
|
1025
|
-
end
|
1026
|
-
|
1027
|
-
view_relation ||= ::Brick.relations.fetch(found = unversioned, nil) ||
|
1028
|
-
(no_v_prefix_name && ::Brick.relations.fetch(found = no_v_prefix_name, nil)) ||
|
1029
|
-
(no_prefix_name && ::Brick.relations.fetch(found = no_prefix_name, nil))
|
1030
|
-
if view_relation
|
1031
|
-
actions = view_relation.key?(:isView) ? [:index, :show] : ::Brick::ALL_API_ACTIONS # By default all actions are allowed
|
1032
|
-
# Call proc that limits which endpoints get surfaced based on version, table or view name, method (get list / get one / post / patch / delete)
|
1033
|
-
# Returning nil makes it do nothing, false makes it skip creating this endpoint, and an array of up to
|
1034
|
-
# these 3 things controls and changes the nature of the endpoint that gets built:
|
1035
|
-
# (updated api_name, name of different relation to route to, allowed actions such as :index, :show, :create, etc)
|
1036
|
-
proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
|
1037
|
-
begin
|
1038
|
-
num_args = filter.arity.negative? ? 6 : filter.arity
|
1039
|
-
filter.call(*[unversioned, k, view_relation, actions, api_ver_num, found, test_ver_num][0...num_args])
|
1040
|
-
rescue StandardError => e
|
1041
|
-
puts "::Brick.api_filter Proc error: #{e.message}"
|
1042
|
-
end
|
1043
|
-
end
|
1044
|
-
# proc_result expects to receive back: [updated_api_name, to_other_relation, allowed_actions]
|
1045
|
-
|
1046
|
-
case proc_result
|
1047
|
-
when NilClass
|
1048
|
-
# Do nothing differently than what normal behaviour would be
|
1049
|
-
when FalseClass # Skip implementing this endpoint
|
1050
|
-
view_relation[:api][api_ver_num] = nil
|
1051
|
-
next
|
1052
|
-
when Array # Did they give back an array of actions?
|
1053
|
-
unless proc_result.any? { |pr| ::Brick::ALL_API_ACTIONS.exclude?(pr) }
|
1054
|
-
proc_result = [unversioned, to_relation, proc_result]
|
1055
|
-
end
|
1056
|
-
# Otherwise don't change this array because it's probably legit
|
1057
|
-
when String
|
1058
|
-
proc_result = [proc_result] # Treat this as the surfaced api_name (path) they want to use for this endpoint
|
1059
|
-
else
|
1060
|
-
puts "::Brick.api_filter Proc warning: Unable to parse this result returned: \n #{proc_result.inspect}"
|
1061
|
-
proc_result = nil # Couldn't understand what in the world was returned
|
1062
|
-
end
|
1063
|
-
|
1064
|
-
if proc_result&.present?
|
1065
|
-
if proc_result[1] # to_other_relation
|
1066
|
-
if (new_view_relation = ::Brick.relations.fetch(proc_result[1], nil))
|
1067
|
-
k = proc_result[1] # Route this call over to this different relation
|
1068
|
-
view_relation = new_view_relation
|
1069
|
-
else
|
1070
|
-
puts "::Brick.api_filter Proc warning: Unable to find new suggested relation with name #{proc_result[1]} -- sticking with #{k} instead."
|
1071
|
-
end
|
1072
|
-
end
|
1073
|
-
if proc_result.first&.!=(k) # updated_api_name -- a different name than this relation would normally have
|
1074
|
-
found = proc_result.first
|
1075
|
-
end
|
1076
|
-
actions &= proc_result[2] if proc_result[2] # allowed_actions
|
1077
|
-
end
|
1078
|
-
(view_relation[:api][api_ver_num] ||= {})[unversioned] = actions # Add to the list of API paths this resource responds to
|
1079
|
-
|
1080
|
-
# view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
|
1081
|
-
# first_part[1..-1].gsub('_', '.').to_i
|
1082
|
-
# end
|
1083
|
-
controller_name = if (last = view_relation.fetch(:resource, nil)&.pluralize)
|
1084
|
-
"#{full_schema_prefix}#{last}"
|
1085
|
-
else
|
1086
|
-
found
|
1087
|
-
end.tr('.', '/')
|
1088
|
-
|
1089
|
-
{ :index => 'get', :create => 'post' }.each do |action, method|
|
1090
|
-
if actions.include?(action)
|
1091
|
-
# Normally goes to something like: /api/v1/employees
|
1092
|
-
send(method, "#{api_root}#{unversioned.tr('.', '/')}", { to: "#{controller_prefix}#{controller_name}##{action}" })
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
# %%% We do not yet surface the #show action
|
1096
|
-
if (id_col = view_relation[:pk]&.first) # ID-dependent stuff
|
1097
|
-
{ :update => ['put', 'patch'], :destroy => ['delete'] }.each do |action, methods|
|
1098
|
-
if actions.include?(action)
|
1099
|
-
methods.each do |method|
|
1100
|
-
send(method, "#{api_root}#{unversioned.tr('.', '/')}/:#{id_col}", { to: "#{controller_prefix}#{controller_name}##{action}" })
|
1101
|
-
end
|
1102
|
-
end
|
1103
|
-
end
|
1104
|
-
end
|
1105
|
-
end
|
1106
|
-
end
|
1107
|
-
|
1108
|
-
# Trestle compatibility
|
1109
|
-
if Object.const_defined?('Trestle') && ::Trestle.config.options&.key?(:site_title) &&
|
1110
|
-
!Object.const_defined?("#{(res_name = resource_name.tr('/', '_')).camelize}Admin")
|
1111
|
-
begin
|
1112
|
-
::Trestle.resource(res_sym = res_name.to_sym, model: class_name&.constantize) do
|
1113
|
-
menu { item res_sym, icon: "fa fa-star" }
|
1114
|
-
end
|
1115
|
-
rescue
|
1116
|
-
end
|
1117
|
-
end
|
1118
|
-
end
|
1119
|
-
|
1120
|
-
if (named_routes = instance_variable_get(:@set).named_routes).respond_to?(:find)
|
1121
|
-
if ::Brick.config.add_status && (status_as = "#{controller_prefix.tr('/', '_')}brick_status".to_sym)
|
1122
|
-
(
|
1123
|
-
!(status_route = instance_variable_get(:@set).named_routes.find { |route| route.first == status_as }&.last) ||
|
1124
|
-
!status_route.ast.to_s.include?("/#{controller_prefix}brick_status/")
|
1125
|
-
)
|
1126
|
-
get("/#{controller_prefix}brick_status", to: 'brick_gem#status', as: status_as.to_s)
|
1127
|
-
end
|
1128
|
-
|
1129
|
-
# ::Brick.config.add_schema &&
|
1130
|
-
if (schema_as = "#{controller_prefix.tr('/', '_')}brick_schema".to_sym)
|
1131
|
-
(
|
1132
|
-
!(schema_route = instance_variable_get(:@set).named_routes.find { |route| route.first == schema_as }&.last) ||
|
1133
|
-
!schema_route.ast.to_s.include?("/#{controller_prefix}brick_schema/")
|
1134
|
-
)
|
1135
|
-
post("/#{controller_prefix}brick_schema", to: 'brick_gem#schema_create', as: schema_as.to_s)
|
1136
|
-
end
|
1137
|
-
|
1138
|
-
if ::Brick.config.add_orphans && (orphans_as = "#{controller_prefix.tr('/', '_')}brick_orphans".to_sym)
|
1139
|
-
(
|
1140
|
-
!(orphans_route = instance_variable_get(:@set).named_routes.find { |route| route.first == orphans_as }&.last) ||
|
1141
|
-
!orphans_route.ast.to_s.include?("/#{controller_prefix}brick_orphans/")
|
1142
|
-
)
|
1143
|
-
get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
|
1144
|
-
end
|
1145
|
-
end
|
1146
|
-
|
1147
|
-
if instance_variable_get(:@set).named_routes.names.exclude?(:brick_crosstab)
|
1148
|
-
get("/#{controller_prefix}brick_crosstab", to: 'brick_gem#crosstab', as: 'brick_crosstab')
|
1149
|
-
get("/#{controller_prefix}brick_crosstab/data", to: 'brick_gem#crosstab_data')
|
1150
|
-
end
|
1151
|
-
|
1152
|
-
if ((rswag_ui_present = Object.const_defined?('Rswag::Ui')) &&
|
1153
|
-
(rswag_path = routeset_to_use.routes.find { |r| r.app.app == ::Rswag::Ui::Engine }
|
1154
|
-
&.instance_variable_get(:@path_formatter)
|
1155
|
-
&.instance_variable_get(:@parts)&.join) &&
|
1156
|
-
(doc_endpoints = ::Rswag::Ui.config.config_object[:urls])) ||
|
1157
|
-
(doc_endpoints = ::Brick.instance_variable_get(:@swagger_endpoints))
|
1158
|
-
last_endpoint_parts = nil
|
1159
|
-
doc_endpoints.each do |doc_endpoint|
|
1160
|
-
puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}" unless ::Brick.routes_done
|
1161
|
-
send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
|
1162
|
-
endpoint_parts = doc_endpoint[:url]&.split('/')
|
1163
|
-
last_endpoint_parts = endpoint_parts
|
1164
|
-
end
|
1165
|
-
end
|
1166
|
-
return if ::Brick.routes_done
|
1167
|
-
|
1168
|
-
if doc_endpoints.present?
|
1169
|
-
if rswag_ui_present
|
1170
|
-
if rswag_path
|
1171
|
-
puts "API documentation now available when navigating to: /#{last_endpoint_parts&.find(&:present?)}/index.html"
|
1172
|
-
else
|
1173
|
-
puts "In order to make documentation available you can put this into your routes.rb:"
|
1174
|
-
puts " mount Rswag::Ui::Engine => '/#{last_endpoint_parts&.find(&:present?) || 'api-docs'}'"
|
1175
|
-
end
|
1176
|
-
else
|
1177
|
-
puts "Having this exposed, one easy way to leverage this to create HTML-based API documentation is to use Scalar.
|
1178
|
-
It will jump to life when you put these two lines into a view template or other HTML resource:
|
1179
|
-
<script id=\"api-reference\" data-url=\"#{last_endpoint_parts.join('/')}\"></script>
|
1180
|
-
<script src=\"https://cdn.jsdelivr.net/@scalar/api-reference\"></script>
|
1181
|
-
Alternatively you can add the rswag-ui gem."
|
1182
|
-
end
|
1183
|
-
elsif rswag_ui_present
|
1184
|
-
sample_path = rswag_path || '/api-docs'
|
1185
|
-
puts
|
1186
|
-
puts "Brick: rswag-ui gem detected -- to make OpenAPI 3.0 documentation available from a path such as '#{sample_path}/v1/swagger.json',"
|
1187
|
-
puts ' put code such as this in an initializer:'
|
1188
|
-
puts ' Rswag::Ui.configure do |config|'
|
1189
|
-
puts " config.swagger_endpoint '#{sample_path}/v1/swagger.json', 'API V1 Docs'"
|
1190
|
-
puts ' end'
|
1191
|
-
unless rswag_path
|
1192
|
-
puts
|
1193
|
-
puts ' and put this into your routes.rb:'
|
1194
|
-
puts " mount Rswag::Ui::Engine => '/api-docs'"
|
1195
|
-
end
|
1196
|
-
end
|
1197
|
-
|
1198
|
-
puts "\n" if tables.present? || views.present?
|
1199
|
-
if tables.present?
|
1200
|
-
puts "Classes that can be built from tables:#{' ' * (table_class_length - 38)} Path:"
|
1201
|
-
puts "======================================#{' ' * (table_class_length - 38)} ====="
|
1202
|
-
::Brick.display_classes(controller_prefix, tables, table_class_length)
|
1203
|
-
end
|
1204
|
-
if views.present?
|
1205
|
-
puts "Classes that can be built from views:#{' ' * (view_class_length - 37)} Path:"
|
1206
|
-
puts "=====================================#{' ' * (view_class_length - 37)} ====="
|
1207
|
-
::Brick.display_classes(controller_prefix, views, view_class_length)
|
1208
|
-
end
|
1209
|
-
::Brick.routes_done = true
|
1210
|
-
end
|
1211
|
-
end
|
1212
|
-
|
1213
884
|
end
|
1214
885
|
|
1215
886
|
require 'brick/version_number'
|
@@ -2004,7 +1675,11 @@ end
|
|
2004
1675
|
|
2005
1676
|
# Now the Ransack Polyamorous version of #build
|
2006
1677
|
if Gem::Dependency.new('ransack').matching_specs.present?
|
2007
|
-
|
1678
|
+
begin # First try the new way of requiring Polyamorous
|
1679
|
+
require 'polyamorous/activerecord/join_dependency'
|
1680
|
+
rescue LoadError => e
|
1681
|
+
require "polyamorous/activerecord_#{::ActiveRecord::VERSION::STRING[0, 3]}_ruby_2/join_dependency"
|
1682
|
+
end
|
2008
1683
|
module Polyamorous::JoinDependencyExtensions
|
2009
1684
|
def build(associations, base_klass, root = nil, path = '')
|
2010
1685
|
root ||= associations
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'brick'
|
4
|
+
require 'rails/generators'
|
5
|
+
require 'rails/generators/active_record'
|
6
|
+
require 'fancy_gets'
|
7
|
+
|
8
|
+
module Brick
|
9
|
+
# Auto-generates controllers
|
10
|
+
class ControllersGenerator < ::Rails::Generators::Base
|
11
|
+
include FancyGets
|
12
|
+
# include ::Rails::Generators::Migration
|
13
|
+
|
14
|
+
desc 'Auto-generates controllers'
|
15
|
+
|
16
|
+
def brick_controllers
|
17
|
+
# %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
|
18
|
+
|
19
|
+
::Brick.mode = :on
|
20
|
+
ActiveRecord::Base.establish_connection
|
21
|
+
|
22
|
+
# Load all models and controllers
|
23
|
+
::Brick.eager_load_classes
|
24
|
+
|
25
|
+
# Generate a list of viable controllers that can be chosen
|
26
|
+
longest_length = 0
|
27
|
+
model_info = Hash.new { |h, k| h[k] = {} }
|
28
|
+
tableless = Hash.new { |h, k| h[k] = [] }
|
29
|
+
existing_controllers = ActionController::Base.descendants.reject do |c|
|
30
|
+
c.name.start_with?('Turbo::Native::')
|
31
|
+
end.map(&:name)
|
32
|
+
controllers = ::Brick.relations.each_with_object([]) do |rel, s|
|
33
|
+
next if rel.first.is_a?(Symbol)
|
34
|
+
|
35
|
+
tbl_parts = rel.first.split('.')
|
36
|
+
tbl_parts.shift if [::Brick.default_schema, 'public'].include?(tbl_parts.first)
|
37
|
+
tbl_parts[-1] = tbl_parts[-1].pluralize
|
38
|
+
begin
|
39
|
+
s << ControllerOption.new(tbl_parts.join('/').camelize, rel.last[:class_name].constantize)
|
40
|
+
rescue
|
41
|
+
end
|
42
|
+
end.reject { |c| existing_controllers.include?(c.to_s) }
|
43
|
+
controllers.sort! do |a, b| # Sort first to separate namespaced stuff from the rest, then alphabetically
|
44
|
+
is_a_namespaced = a.to_s.include?('::')
|
45
|
+
is_b_namespaced = b.to_s.include?('::')
|
46
|
+
if is_a_namespaced && !is_b_namespaced
|
47
|
+
1
|
48
|
+
elsif !is_a_namespaced && is_b_namespaced
|
49
|
+
-1
|
50
|
+
else
|
51
|
+
a.to_s <=> b.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
controllers.each do |m| # Find longest name in the list for future use to show lists on the right side of the screen
|
55
|
+
if longest_length < (len = m.to_s.length)
|
56
|
+
longest_length = len
|
57
|
+
end
|
58
|
+
end
|
59
|
+
chosen = gets_list(list: controllers, chosen: controllers.dup)
|
60
|
+
relations = ::Brick.relations
|
61
|
+
chosen.each do |controller_option|
|
62
|
+
if (controller_parts = controller_option.to_s.split('::')).length > 1
|
63
|
+
namespace = controller_parts.first.constantize
|
64
|
+
end
|
65
|
+
_built_controller, code = Object.send(:build_controller, namespace, controller_parts.last, controller_parts.last.pluralize, controller_option.model, relations)
|
66
|
+
path = ['controllers']
|
67
|
+
path.concat(controller_parts.map(&:underscore))
|
68
|
+
dir = +"#{::Rails.root}/app"
|
69
|
+
path[0..-2].each do |path_part|
|
70
|
+
dir << "/#{path_part}"
|
71
|
+
Dir.mkdir(dir) unless Dir.exist?(dir)
|
72
|
+
end
|
73
|
+
File.open("#{dir}/#{path.last}.rb", 'w') { |f| f.write code } unless code.blank?
|
74
|
+
end
|
75
|
+
puts "\n*** Created #{chosen.length} controller files under app/controllers ***"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ControllerOption
|
81
|
+
attr_accessor :name, :model
|
82
|
+
|
83
|
+
def initialize(name, model)
|
84
|
+
self.name = name
|
85
|
+
self.model = model
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
name
|
90
|
+
end
|
91
|
+
end
|