Module: Brick
- Defined in:
- lib/brick/util.rb,
lib/brick.rb,
lib/brick/config.rb,
lib/brick/join_array.rb,
lib/brick/version_number.rb,
lib/brick/serializers/json.rb,
lib/brick/serializers/yaml.rb,
lib/brick/frameworks/cucumber.rb,
lib/brick/frameworks/rails/engine.rb,
lib/brick/frameworks/rails/controller.rb,
lib/generators/brick/models_generator.rb,
lib/generators/brick/install_generator.rb,
lib/generators/brick/migrations_generator.rb,
lib/brick/extensions.rb
Overview
:nodoc:
Defined Under Namespace
Modules: Cucumber, Extensions, Rails, RouteSet, Serializers, Util, VERSION Classes: Config, InstallGenerator, JoinArray, JoinHash, MigrationsGenerator, ModelsGenerator
Class Attribute Summary collapse
-
.auto_models ⇒ Object
Returns the value of attribute auto_models.
-
.db_schemas ⇒ Object
Returns the value of attribute db_schemas.
-
.default_schema ⇒ Object
Returns the value of attribute default_schema.
-
.is_eager_loading ⇒ Object
Returns the value of attribute is_eager_loading.
-
.is_oracle ⇒ Object
Returns the value of attribute is_oracle.
-
.routes_done ⇒ Object
Returns the value of attribute routes_done.
Class Method Summary collapse
- ._add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false) ⇒ Object
- ._class_pk(dotted_name, multitenant) ⇒ Object
-
.additional_references=(ars) ⇒ Object
Additional table associations to use (Think of these as virtual foreign keys perhaps).
- .apartment_multitenant ⇒ Object
- .api_root ⇒ Object
- .api_root=(path) ⇒ Object
-
.config {|@config| ... } ⇒ Object
(also: configure)
private
Returns Brick’s global configuration object, a singleton.
-
.custom_columns=(cust_cols) ⇒ Object
Custom columns to add to a table, minimally defined with a name and DSL string.
- .default_route_fallback=(resource_name) ⇒ Object
- .display_classes(prefix, rels, max_length) ⇒ Object
- .eager_load_classes(do_ar_abstract_bases = false) ⇒ Object
- .enable_api ⇒ Object
- .enable_api=(path) ⇒ Object
-
.enable_controllers=(value) ⇒ Object
Switches Brick auto-controllers on or off, for all threads.
-
.enable_controllers? ⇒ Boolean
Returns ‘true` if Brick controllers are on, `false` otherwise.
-
.enable_models=(value) ⇒ Object
Switches Brick auto-models on or off, for all threads.
-
.enable_models? ⇒ Boolean
Returns ‘true` if Brick models are on, `false` otherwise.
-
.enable_routes=(value) ⇒ Object
Switches Brick auto-routes on or off, for all threads.
-
.enable_routes? ⇒ Boolean
Returns ‘true` if Brick routes are on, `false` otherwise.
-
.enable_views=(value) ⇒ Object
Switches Brick auto-views on or off, for all threads.
-
.enable_views? ⇒ Boolean
Returns ‘true` if Brick views are on, `false` otherwise.
- .ensure_unique(name, *sources) ⇒ Object
- .exclude_column(table, col) ⇒ Object
-
.exclude_hms=(skips) ⇒ Object
Skip creating a has_many association for these (Uses the same exact three-part format as would define an additional_reference).
- .exclude_tables=(value) ⇒ Object
- .existing_stis ⇒ Object
-
.find_orphans(multi_schema) ⇒ Object
Locate orphaned records.
-
.gem_version ⇒ Object
Returns Brick’s ‘::Gem::Version`, convenient for comparisons.
- .get_bts_and_hms(model) ⇒ Object
-
.get_status_of_resources ⇒ Object
Identify built out routes, migrations, models, (and also soon controllers and views!) for each resource.
-
.has_ones=(hos) ⇒ Object
Associations to treat as a has_one.
-
.load_additional_references ⇒ Object
Load additional references (virtual foreign keys) This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine’s initialisation %%% Maybe look for differences the second time ‘round and just add new stuff instead of entirely deferring.
- .metadata_columns=(value) ⇒ Object
-
.model_descrips=(descrips) ⇒ Object
DSL templates for individual models to provide prettier descriptions of objects.
- .models_inherit_from=(value) ⇒ Object
-
.namify(name, action = nil) ⇒ Object
Convert spaces to underscores if the second character and onwards is mixed case.
-
.non_tenanted_models ⇒ Object
If multitenancy is enabled, a list of non-tenanted “global” models.
- .not_nullables=(value) ⇒ Object
- .order=(value) ⇒ Object
-
.path_prefix=(path) ⇒ Object
Any path prefixing to apply to all auto-generated Brick routes.
-
.polymorphics=(polys) ⇒ Object
Polymorphic associations.
-
.relations ⇒ Object
All tables and views (what Postgres calls “relations” including column and foreign key info).
-
.schema_behavior=(behavior) ⇒ Object
Database schema to use when analysing existing data, such as deriving a list of polymorphic classes for polymorphics in which it wasn’t originally specified.
-
.schema_behaviour=(behavior) ⇒ Object
For any Brits out there.
-
.serializer ⇒ Object
Get the Brick serializer used by all threads.
-
.serializer=(value) ⇒ Object
Set the Brick serializer.
- .set_db_schema(params = nil) ⇒ Object
- .skip_database_views=(value) ⇒ Object
-
.skip_index_hms=(value) ⇒ Object
Skip showing counts for these specific has_many associations when building auto-generated #index views.
- .sti_models ⇒ Object
-
.sti_namespace_prefixes=(snp) ⇒ Object
Module prefixes to build out and associate with specific base STI models.
- .sti_type_column=(type_col) ⇒ Object
- .table_name_prefixes=(value) ⇒ Object
- .unexclude_column(table, col) ⇒ Object
- .version ⇒ Object
Class Attribute Details
.auto_models ⇒ Object
Returns the value of attribute auto_models.
125 126 127 |
# File 'lib/brick.rb', line 125 def auto_models @auto_models end |
.db_schemas ⇒ Object
Returns the value of attribute db_schemas.
125 126 127 |
# File 'lib/brick.rb', line 125 def db_schemas @db_schemas end |
.default_schema ⇒ Object
Returns the value of attribute default_schema.
125 126 127 |
# File 'lib/brick.rb', line 125 def default_schema @default_schema end |
.is_eager_loading ⇒ Object
Returns the value of attribute is_eager_loading.
125 126 127 |
# File 'lib/brick.rb', line 125 def is_eager_loading @is_eager_loading end |
.is_oracle ⇒ Object
Returns the value of attribute is_oracle.
125 126 127 |
# File 'lib/brick.rb', line 125 def is_oracle @is_oracle end |
.routes_done ⇒ Object
Returns the value of attribute routes_done.
125 126 127 |
# File 'lib/brick.rb', line 125 def routes_done @routes_done end |
Class Method Details
._add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false) ⇒ Object
2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 |
# File 'lib/brick/extensions.rb', line 2071 def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false) bt_assoc_name = ::Brick.namify(fk[2], :downcase) unless is_polymorphic bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id') bt_assoc_name[-3] == '_' ? bt_assoc_name[0..-4] : bt_assoc_name[0..-3] elsif bt_assoc_name.downcase.end_with?('id') && bt_assoc_name.exclude?('_') bt_assoc_name[0..-3] # Make the bold assumption that we can just peel off any final ID part else "#{bt_assoc_name}_bt" end end bt_assoc_name = "#{bt_assoc_name}_" if bt_assoc_name == 'attribute' # %%% Temporary schema patch for_tbl = fk[1] fk_namified = ::Brick.namify(fk[1]) apartment = Object.const_defined?('Apartment') && Apartment fk[0] = Apartment.default_schema if apartment && apartment.excluded_models.include?(fk_namified.singularize.camelize) fk[1] = "#{fk[0]}.#{fk[1]}" if fk[0] # && fk[0] != ::Brick.default_schema bts = (relation = relations.fetch(fk[1], nil))&.fetch(:fks) { relation[:fks] = {} } # %%% Do we miss out on has_many :through or even HM based on constantizing this model early? # Maybe it's already gotten this info because we got as far as to say there was a unique class primary_table = if (is_class = fk[4].is_a?(Hash) && fk[4].key?(:class)) pri_tbl = (primary_class = fk[4][:class].constantize).table_name if (pri_tbl_parts = pri_tbl.split('.')).length > 1 fk[3] = pri_tbl_parts.first end else is_schema = if ::Brick.config.schema_behavior[:multitenant] # If Apartment gem lists the primary table as being associated with a non-tenanted model # then use 'public' schema for the primary table if apartment && apartment&.excluded_models.include?(fk[4].singularize.camelize) fk[3] = Apartment.default_schema true end else fk[3] && fk[3] != ::Brick.default_schema && fk[3] != 'public' end pri_tbl = fk[4] is_schema ? "#{fk[3]}.#{pri_tbl}" : pri_tbl end hms = (relation = relations.fetch(primary_table, nil))&.fetch(:fks) { relation[:fks] = {} } unless is_class unless (cnstr_name = fk[5]) # For any appended references (those that come from config), arrive upon a definitely unique constraint name pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms) missing = [] missing << fk[1] unless relations.key?(fk[1]) missing << primary_table unless is_class || relations.key?(primary_table) unless missing.empty? tables = relations.reject { |_k, v| v.fetch(:isView, nil) }.keys.sort puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)" return end unless (cols = relations[fk[1]][:cols]).key?(fk[2]) || (is_polymorphic && cols.key?("#{fk[2]}_id") && cols.key?("#{fk[2]}_type")) columns = cols.map { |k, v| "#{k} (#{v.first.split(' ').first})" } puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[2]}. (Columns present in #{fk[1]} are #{columns.join(', ')}.)" return end if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[1] && v[:fk] == fk[2] && v[:inverse_table] == primary_table }) if is_class && !redundant.last.key?(:class) redundant.last[:primary_class] = primary_class # Round out this BT so it can find the proper :source for a HMT association that references an STI subclass else puts "Brick: Additional reference #{fk.inspect} is redundant and can be removed. (Already established by #{redundant.first}.)" end return end end return unless bts # Rails 5.0 and older can have bts end up being nil if (assoc_bt = bts[cnstr_name]) if is_polymorphic # Assuming same fk (don't yet support composite keys for polymorphics) assoc_bt[:inverse_table] << fk[4] else # Expect we could have a composite key going if assoc_bt[:fk].is_a?(String) assoc_bt[:fk] = [assoc_bt[:fk], fk[2]] unless fk[2] == assoc_bt[:fk] elsif assoc_bt[:fk].exclude?(fk[2]) assoc_bt[:fk] << fk[2] end assoc_bt[:assoc_name] = "#{assoc_bt[:assoc_name]}_#{fk[2]}" end else inverse_table = [primary_table] if is_polymorphic assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table } assoc_bt[:optional] = true if is_optional assoc_bt[:polymorphic] = true if is_polymorphic end if is_class # For use in finding the proper :source for a HMT association that references an STI subclass assoc_bt[:primary_class] = primary_class # For use in finding the proper :inverse_of for a BT association that references an STI subclass # assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] } end return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[1] == exclusion[0] && fk[2] == exclusion[1] && primary_table == exclusion[2] } || hms.nil? if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil)) if assoc_hm[:fk].is_a?(String) assoc_hm[:fk] = [assoc_hm[:fk], fk[2]] unless fk[2] == assoc_hm[:fk] elsif assoc_hm[:fk].exclude?(fk[2]) assoc_hm[:fk] << fk[2] end assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name else inv_tbl = if ::Brick.config.schema_behavior[:multitenant] && apartment && fk[0] == Apartment.default_schema for_tbl else fk[1] end assoc_hm = hms[hm_cnstr_name] = { is_bt: false, fk: fk[2], assoc_name: fk_namified.pluralize, alternate_name: bt_assoc_name, inverse_table: inv_tbl, inverse: assoc_bt } assoc_hm[:polymorphic] = true if is_polymorphic hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} } this_hm_count = hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1 end assoc_bt[:inverse] = assoc_hm end |
._class_pk(dotted_name, multitenant) ⇒ Object
2329 2330 2331 |
# File 'lib/brick/extensions.rb', line 2329 def _class_pk(dotted_name, multitenant) Object.const_get((multitenant ? [dotted_name.split('.').last] : dotted_name.split('.')).map { |nm| "::#{nm.singularize.camelize}" }.join).primary_key end |
.additional_references=(ars) ⇒ Object
Additional table associations to use (Think of these as virtual foreign keys perhaps)
341 342 343 344 345 346 347 348 |
# File 'lib/brick.rb', line 341 def additional_references=(ars) if ars ars = ars.call if ars.is_a?(Proc) ars = ars.to_a unless ars.is_a?(Array) ars = [ars] unless ars.empty? || ars.first.is_a?(Array) Brick.config.additional_references = ars end end |
.apartment_multitenant ⇒ Object
146 147 148 149 150 151 |
# File 'lib/brick.rb', line 146 def apartment_multitenant if @apartment_multitenant.nil? @apartment_multitenant = ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') end @apartment_multitenant end |
.api_root ⇒ Object
305 306 307 |
# File 'lib/brick.rb', line 305 def api_root Brick.config.api_root end |
.api_root=(path) ⇒ Object
300 301 302 |
# File 'lib/brick.rb', line 300 def api_root=(path) Brick.config.api_root = path end |
.config {|@config| ... } ⇒ Object Also known as: configure
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns Brick’s global configuration object, a singleton. These settings affect all threads.
514 515 516 517 518 |
# File 'lib/brick.rb', line 514 def config @config ||= Brick::Config.instance yield @config if block_given? @config end |
.custom_columns=(cust_cols) ⇒ Object
Custom columns to add to a table, minimally defined with a name and DSL string.
352 353 354 355 356 357 |
# File 'lib/brick.rb', line 352 def custom_columns=(cust_cols) if cust_cols cust_cols = cust_cols.call if cust_cols.is_a?(Proc) Brick.config.custom_columns = cust_cols end end |
.default_route_fallback=(resource_name) ⇒ Object
430 431 432 |
# File 'lib/brick.rb', line 430 def default_route_fallback=(resource_name) Brick.config.default_route_fallback = resource_name end |
.display_classes(prefix, rels, max_length) ⇒ Object
540 541 542 543 544 545 546 |
# File 'lib/brick.rb', line 540 def display_classes(prefix, rels, max_length) rels.sort.each do |rel| (::Brick.auto_models ||= []) << rel.first puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{prefix}#{rel.last}" end puts "\n" end |
.eager_load_classes(do_ar_abstract_bases = false) ⇒ Object
525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'lib/brick.rb', line 525 def eager_load_classes(do_ar_abstract_bases = false) ::Brick.is_eager_loading = true if ::ActiveSupport.version < ::Gem::Version.new('6') || ::Rails.configuration.instance_variable_get(:@autoloader) == :classic ::Rails.configuration.eager_load_namespaces.select { |ns| ns < ::Rails::Application }.each(&:eager_load!) else Zeitwerk::Loader.eager_load_all end abstract_ar_bases = if do_ar_abstract_bases ActiveRecord::Base.descendants.select { |ar| ar.abstract_class? }.map(&:name) end ::Brick.is_eager_loading = false abstract_ar_bases end |
.enable_api ⇒ Object
295 296 297 |
# File 'lib/brick.rb', line 295 def enable_api Brick.config.enable_api end |
.enable_api=(path) ⇒ Object
290 291 292 |
# File 'lib/brick.rb', line 290 def enable_api=(path) Brick.config.enable_api = path end |
.enable_controllers=(value) ⇒ Object
Switches Brick auto-controllers on or off, for all threads
252 253 254 |
# File 'lib/brick.rb', line 252 def enable_controllers=(value) Brick.config.enable_controllers = value end |
.enable_controllers? ⇒ Boolean
Returns ‘true` if Brick controllers are on, `false` otherwise. This affects all threads. Enabled by default.
259 260 261 |
# File 'lib/brick.rb', line 259 def enable_controllers? !!Brick.config.enable_controllers end |
.enable_models=(value) ⇒ Object
Switches Brick auto-models on or off, for all threads
239 240 241 |
# File 'lib/brick.rb', line 239 def enable_models=(value) Brick.config.enable_models = value end |
.enable_models? ⇒ Boolean
Returns ‘true` if Brick models are on, `false` otherwise. This affects all threads. Enabled by default.
246 247 248 |
# File 'lib/brick.rb', line 246 def enable_models? !!Brick.config.enable_models end |
.enable_routes=(value) ⇒ Object
Switches Brick auto-routes on or off, for all threads
278 279 280 |
# File 'lib/brick.rb', line 278 def enable_routes=(value) Brick.config.enable_routes = value end |
.enable_routes? ⇒ Boolean
Returns ‘true` if Brick routes are on, `false` otherwise. This affects all threads. Enabled by default.
285 286 287 |
# File 'lib/brick.rb', line 285 def enable_routes? !!Brick.config.enable_routes end |
.enable_views=(value) ⇒ Object
Switches Brick auto-views on or off, for all threads
265 266 267 |
# File 'lib/brick.rb', line 265 def enable_views=(value) Brick.config.enable_views = value end |
.enable_views? ⇒ Boolean
Returns ‘true` if Brick views are on, `false` otherwise. This affects all threads. Enabled by default.
272 273 274 |
# File 'lib/brick.rb', line 272 def enable_views? !!Brick.config.enable_views end |
.ensure_unique(name, *sources) ⇒ Object
2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 |
# File 'lib/brick/extensions.rb', line 2244 def ensure_unique(name, *sources) base = name if (added_num = name.slice!(/_(\d+)$/)) added_num = added_num[1..-1].to_i else added_num = 1 end while ( name = "#{base}_#{added_num += 1}" sources.each_with_object(nil) do |v, s| s || case v when Hash v.key?(name) when Array v.include?(name) end end ) end name end |
.exclude_column(table, col) ⇒ Object
222 223 224 225 |
# File 'lib/brick.rb', line 222 def exclude_column(table, col) puts "Excluding #{table}.#{col}" true end |
.exclude_hms=(skips) ⇒ Object
Skip creating a has_many association for these (Uses the same exact three-part format as would define an additional_reference)
367 368 369 370 371 372 373 374 |
# File 'lib/brick.rb', line 367 def exclude_hms=(skips) if skips skips = skips.call if skips.is_a?(Proc) skips = skips.to_a unless skips.is_a?(Array) skips = [skips] unless skips.empty? || skips.first.is_a?(Array) Brick.config.exclude_hms = skips end end |
.exclude_tables=(value) ⇒ Object
315 316 317 |
# File 'lib/brick.rb', line 315 def exclude_tables=(value) Brick.config.exclude_tables = value end |
.existing_stis ⇒ Object
120 121 122 |
# File 'lib/brick.rb', line 120 def self.existing_stis @existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') } end |
.find_orphans(multi_schema) ⇒ Object
Locate orphaned records
2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 |
# File 'lib/brick/extensions.rb', line 2267 def find_orphans(multi_schema) is_default_schema = multi_schema&.==(Apartment.default_schema) relations.each_with_object([]) do |v, s| frn_tbl = v.first next if (relation = v.last).key?(:isView) || config.exclude_tables.include?(frn_tbl) || !(for_pk = (relation[:pkey].values.first&.first)) is_default_frn_schema = !is_default_schema && multi_schema && ((frn_parts = frn_tbl.split('.')).length > 1 && frn_parts.first)&.==(Apartment.default_schema) relation[:fks].select { |_k, assoc| assoc[:is_bt] }.each do |_k, bt| begin if bt.key?(:polymorphic) pri_pk = for_pk pri_tables = Brick.config.polymorphics["#{frn_tbl}.#{bt[:fk]}"] .each_with_object(Hash.new { |h, k| h[k] = [] }) do |pri_class, s| s[Object.const_get(pri_class).table_name] << pri_class end fk_id_col = "#{bt[:fk]}_id" fk_type_col = "#{bt[:fk]}_type" selects = [] pri_tables.each do |pri_tbl, pri_types| # Skip if database is multitenant, we're not focused on "public", and the foreign and primary tables # are both in the "public" schema next if is_default_frn_schema && ((pri_parts = pri_tbl&.split('.'))&.length > 1 && pri_parts.first)&.==(Apartment.default_schema) selects << "SELECT '#{pri_tbl}' AS pri_tbl, frn.#{fk_type_col} AS pri_type, frn.#{fk_id_col} AS pri_id, frn.#{for_pk} AS frn_id FROM #{frn_tbl} AS frn LEFT OUTER JOIN #{pri_tbl} AS pri ON pri.#{pri_pk} = frn.#{fk_id_col} WHERE frn.#{fk_type_col} IN (#{ pri_types.map { |pri_type| "'#{pri_type}'" }.join(', ') }) AND frn.#{bt[:fk]}_id IS NOT NULL AND pri.#{pri_pk} IS NULL\n" end ActiveRecord::Base.execute_sql(selects.join("UNION ALL\n")).each do |o| entry = [frn_tbl, o['frn_id'], o['pri_type'], o['pri_id'], fk_id_col] entry << o['pri_tbl'] if (pri_class = Object.const_get(o['pri_type'])) != pri_class.base_class s << entry end else # Skip if database is multitenant, we're not focused on "public", and the foreign and primary tables # are both in the "public" schema pri_tbl = bt.key?(:inverse_table) && bt[:inverse_table] next if is_default_frn_schema && ((pri_parts = pri_tbl&.split('.'))&.length > 1 && pri_parts.first)&.==(Apartment.default_schema) pri_pk = relations[pri_tbl].fetch(:pkey, nil)&.values&.first&.first || _class_pk(pri_tbl, multi_schema) ActiveRecord::Base.execute_sql( "SELECT frn.#{bt[:fk]} AS pri_id, frn.#{for_pk} AS frn_id FROM #{frn_tbl} AS frn LEFT OUTER JOIN #{pri_tbl} AS pri ON pri.#{pri_pk} = frn.#{bt[:fk]} WHERE frn.#{bt[:fk]} IS NOT NULL AND pri.#{pri_pk} IS NULL ORDER BY 1, 2" ).each { |o| s << [frn_tbl, o['frn_id'], pri_tbl, o['pri_id'], bt[:fk]] } end rescue StandardError => err puts "Strange -- #{err.inspect}" end end end end |
.gem_version ⇒ Object
Returns Brick’s ‘::Gem::Version`, convenient for comparisons. This is recommended over `::Brick::VERSION::STRING`.
495 496 497 |
# File 'lib/brick.rb', line 495 def gem_version ::Gem::Version.new(VERSION::STRING) end |
.get_bts_and_hms(model) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/brick.rb', line 175 def get_bts_and_hms(model) bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s| next if !const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name) case a.macro when :belongs_to if a.polymorphic? rel_poly_bt = relations[model.table_name][:fks].find { |_k, fk| fk[:assoc_name] == a.name.to_s } if (primary_tables = rel_poly_bt&.last&.fetch(:inverse_table, [])).is_a?(Array) models = primary_tables&.map { |table| table.singularize.camelize.constantize } s.first[a.foreign_key] = [a.name, models, true] else # This will come up when using Devise invitable when invited_by_class_name is not # specified because in that circumstance it adds a polymorphic :invited_by association, # along with appropriate invited_by_type and invited_by_id columns. puts "Missing any real indication as to which models \"has_many\" this polymorphic BT in model #{a.active_record.name}:" puts " belongs_to :#{a.name}, polymorphic: true" end else s.first[a.foreign_key] = [a.name, a.klass] end when :has_many, :has_one # This gets has_many as well as has_many :through # %%% weed out ones that don't have an available model to reference s.last[a.name] = a end end # Mark has_manys that go to an associative ("join") table so that they are skipped in the UI, # as well as any possible polymorphic associations skip_hms = {} hms.each do |hmt| if (through = hmt.last.[:through]) skip_hms[through] = nil # if hms[through] # binding.pry if !hms[through] # End up with a hash of HMT names pointing to join-table associations # Last part was: hmt.last.name # Changed up because looking for: hms[:issue_issue_duplicates] model._br_associatives[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym] elsif hmt.last.inverse_of.nil? puts "SKIPPING #{hmt.last.name.inspect}" # %%% If we don't do this then below associative.name will find that associative is nil skip_hms[hmt.last.name] = nil end end skip_hms.each { |k, _v| hms.delete(k) } [bts, hms] end |
.get_status_of_resources ⇒ Object
Identify built out routes, migrations, models, (and also soon controllers and views!) for each resource
2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 |
# File 'lib/brick/extensions.rb', line 2196 def get_status_of_resources rails_root = ::Rails.root.to_s migrations = if Dir.exist?(mig_path = ActiveRecord::Migrator.migrations_paths.first || "#{rails_root}/db/migrate") Dir["#{mig_path}/**/*.rb"].each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s| File.read(v).split("\n").each do |line| # For all non-commented lines, look for any that have "create_table", "alter_table", or "drop_table" if !line.lstrip.start_with?('#') && (idx = (line.index('create_table ') || line.index('create_table('))&.+(13)) || (idx = (line.index('alter_table ') || line.index('alter_table('))&.+(12)) || (idx = (line.index('drop_table ') || line.index('drop_table('))&.+(11)) tbl = line[idx..-1].match(/([:'"\w\.]+)/)&.captures&.first if tbl s[tbl.tr(':\'"', '').pluralize] << v break end end end end end abstract_activerecord_bases = ::Brick.eager_load_classes(true) models = if Dir.exist?(model_path = "#{rails_root}/app/models") Dir["#{model_path}/**/*.rb"].each_with_object({}) do |v, s| File.read(v).split("\n").each do |line| # For all non-commented lines, look for any that start with "class " and also "< ApplicationRecord" if line.lstrip.start_with?('class') && (idx = line.index('class')) model = line[idx + 5..-1].match(/[\s:]+([\w:]+)/)&.captures&.first if model && abstract_activerecord_bases.exclude?(model) klass = begin model.constantize rescue end s[model.underscore.tr('/', '.').pluralize] = [ v.start_with?(rails_root) ? v[rails_root.length + 1..-1] : v, klass ] end end end end end ::Brick.relations.keys.map do |v| tbl_parts = v.split('.') tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == Apartment.default_schema res = tbl_parts.join('.') [v, (model = models[res])&.last&.table_name, migrations&.fetch(res, nil), model&.first] end end |
.has_ones=(hos) ⇒ Object
Associations to treat as a has_one
384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/brick.rb', line 384 def has_ones=(hos) if hos hos = hos.call if hos.is_a?(Proc) hos = hos.to_a unless hos.is_a?(Array) hos = [hos] unless hos.empty? || hos.first.is_a?(Array) # Translate to being nested hashes Brick.config.has_ones = hos&.each_with_object(Hash.new { |h, k| h[k] = {} }) do |v, s| s[v.first][v[1]] = v[2] if v[1] s end end end |
.load_additional_references ⇒ Object
Load additional references (virtual foreign keys) This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine’s initialisation %%% Maybe look for differences the second time ‘round and just add new stuff instead of entirely deferring
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/brick.rb', line 437 def load_additional_references return if @_additional_references_loaded relations = ::Brick.relations if (ars = ::Brick.config.additional_references) || ::Brick.config.polymorphics is_optional = ActiveRecord.version >= ::Gem::Version.new('5.0') if ars ars.each do |ar| fk = ar.length < 5 ? [nil, +ar[0], ar[1], nil, +ar[2]] : [ar[0], +ar[1], ar[2], ar[3], +ar[4], ar[5]] ::Brick._add_bt_and_hm(fk, relations, false, is_optional) end end if (polys = ::Brick.config.polymorphics) if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.key?(schema) ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) end missing_stis = {} polys.each do |k, v| table_name, poly = k.split('.') v ||= ActiveRecord::Base.execute_sql("SELECT DISTINCT #{poly}_type AS typ FROM #{table_name}").each_with_object([]) { |result, s| s << result['typ'] if result['typ'] } v.each do |type| if relations.key?(primary_table = type.underscore.pluralize) ::Brick._add_bt_and_hm([nil, table_name, poly, nil, primary_table, "(brick) #{table_name}_#{poly}"], relations, true, is_optional) else missing_stis[primary_table] = type unless ::Brick.existing_stis.key?(type) end end end unless missing_stis.empty? print " You might be missing an STI namespace prefix entry for these tables: #{missing_stis.keys.join(', ')}. In config/initializers/brick.rb appropriate entries would look something like: Brick.sti_namespace_prefixes = {" puts missing_stis.map { |_k, missing_sti| "\n '::#{missing_sti}' => 'SomeParentModel'" }.join(',') puts " } (Just trade out SomeParentModel with some more appropriate one.)" end end @_additional_references_loaded = true end # Find associative tables that can be set up for has_many :through ::Brick.relations.each do |_key, tbl| tbl_cols = tbl[:cols].keys fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = [fk.last[:assoc_name], fk.last[:inverse_table]] if fk.last[:is_bt]; s } # Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has # foreign keys then it can act as an associative table and thus be used with has_many :through. if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config. || []) - (tbl[:pkey].values.first || [])).length.zero? fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last } end end end |
.metadata_columns=(value) ⇒ Object
330 331 332 |
# File 'lib/brick.rb', line 330 def (value) Brick.config. = value end |
.model_descrips=(descrips) ⇒ Object
DSL templates for individual models to provide prettier descriptions of objects
405 406 407 |
# File 'lib/brick.rb', line 405 def model_descrips=(descrips) Brick.config.model_descrips = descrips end |
.models_inherit_from=(value) ⇒ Object
320 321 322 |
# File 'lib/brick.rb', line 320 def models_inherit_from=(value) Brick.config.models_inherit_from = value end |
.namify(name, action = nil) ⇒ Object
Convert spaces to underscores if the second character and onwards is mixed case
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/brick.rb', line 159 def namify(name, action = nil) has_uppers = name =~ /[A-Z]+/ has_lowers = name =~ /[a-z]+/ name.downcase! if has_uppers && action == :downcase if name.include?(' ') # All uppers or all lowers? if !has_uppers || !has_lowers name.titleize.tr(' ', '_') else # Mixed uppers and lowers -- just remove existing spaces name.tr(' ', '') end else action == :underscore ? name.underscore : name end end |
.non_tenanted_models ⇒ Object
If multitenancy is enabled, a list of non-tenanted “global” models
154 155 156 |
# File 'lib/brick.rb', line 154 def non_tenanted_models @pending_models ||= {} end |
.not_nullables=(value) ⇒ Object
335 336 337 |
# File 'lib/brick.rb', line 335 def not_nullables=(value) Brick.config.not_nullables = value end |
.order=(value) ⇒ Object
360 361 362 |
# File 'lib/brick.rb', line 360 def order=(value) Brick.config.order = value end |
.path_prefix=(path) ⇒ Object
Any path prefixing to apply to all auto-generated Brick routes
233 234 235 |
# File 'lib/brick.rb', line 233 def path_prefix=(path) Brick.config.path_prefix = path end |
.polymorphics=(polys) ⇒ Object
Polymorphic associations
398 399 400 401 |
# File 'lib/brick.rb', line 398 def polymorphics=(polys) polys = polys.each_with_object({}) { |poly, s| s[poly] = nil } if polys.is_a?(Array) Brick.config.polymorphics = polys || {} end |
.relations ⇒ Object
All tables and views (what Postgres calls “relations” including column and foreign key info)
141 142 143 144 |
# File 'lib/brick.rb', line 141 def relations # Key our list of relations for this connection off of the connection pool's object_id (@relations ||= {})[ActiveRecord::Base.connection_pool.object_id] ||= Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } } end |
.schema_behavior=(behavior) ⇒ Object
Database schema to use when analysing existing data, such as deriving a list of polymorphic classes for polymorphics in which it wasn’t originally specified.
418 419 420 |
# File 'lib/brick.rb', line 418 def schema_behavior=(behavior) Brick.config.schema_behavior = (behavior.is_a?(Symbol) ? { behavior => nil } : behavior) end |
.schema_behaviour=(behavior) ⇒ Object
For any Brits out there
422 423 424 |
# File 'lib/brick.rb', line 422 def schema_behaviour=(behavior) Brick.schema_behavior = behavior end |
.serializer ⇒ Object
Get the Brick serializer used by all threads.
507 508 509 |
# File 'lib/brick.rb', line 507 def serializer Brick.config.serializer end |
.serializer=(value) ⇒ Object
Set the Brick serializer. This setting affects all threads.
501 502 503 |
# File 'lib/brick.rb', line 501 def serializer=(value) Brick.config.serializer = value end |
.set_db_schema(params = nil) ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/brick.rb', line 127 def set_db_schema(params = nil) schema = (params ? params['_brick_schema'] : ::Brick.default_schema) if schema && ::Brick.db_schemas&.key?(schema) ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) schema elsif ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' # Just return the current schema orig_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',') # ::Brick.apartment_multitenant && tbl_parts.first == Apartment.default_schema (orig_schema - ['pg_catalog']).first end end |
.skip_database_views=(value) ⇒ Object
310 311 312 |
# File 'lib/brick.rb', line 310 def skip_database_views=(value) Brick.config.skip_database_views = value end |
.skip_index_hms=(value) ⇒ Object
Skip showing counts for these specific has_many associations when building auto-generated #index views
378 379 380 |
# File 'lib/brick.rb', line 378 def skip_index_hms=(value) Brick.config.skip_index_hms = value end |
.sti_models ⇒ Object
116 117 118 |
# File 'lib/brick.rb', line 116 def self.sti_models @sti_models ||= {} end |
.sti_namespace_prefixes=(snp) ⇒ Object
Module prefixes to build out and associate with specific base STI models
411 412 413 |
# File 'lib/brick.rb', line 411 def sti_namespace_prefixes=(snp) Brick.config.sti_namespace_prefixes = snp end |
.sti_type_column=(type_col) ⇒ Object
426 427 428 |
# File 'lib/brick.rb', line 426 def sti_type_column=(type_col) Brick.config.sti_type_column = (type_col.is_a?(String) ? { type_col => nil } : type_col) end |
.table_name_prefixes=(value) ⇒ Object
325 326 327 |
# File 'lib/brick.rb', line 325 def table_name_prefixes=(value) Brick.config.table_name_prefixes = value end |
.unexclude_column(table, col) ⇒ Object
226 227 228 229 |
# File 'lib/brick.rb', line 226 def unexclude_column(table, col) puts "Unexcluding #{table}.#{col}" true end |