brick 1.0.190 → 1.0.192
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/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
|