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
-
.db_schemas ⇒ Object
Returns the value of attribute db_schemas.
-
.default_schema ⇒ Object
Returns the value of attribute default_schema.
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
-
.config {|@config| ... } ⇒ Object
(also: configure)
private
Returns Brick’s global configuration object, a singleton.
- .default_route_fallback=(resource_name) ⇒ 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.
- .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
-
.non_tenanted_models ⇒ Object
If multitenancy is enabled, a list of non-tenanted “global” models.
- .not_nullables=(value) ⇒ Object
- .order=(value) ⇒ Object
-
.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) ⇒ 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
.db_schemas ⇒ Object
Returns the value of attribute db_schemas.
126 127 128 |
# File 'lib/brick.rb', line 126 def db_schemas @db_schemas end |
.default_schema ⇒ Object
Returns the value of attribute default_schema.
126 127 128 |
# File 'lib/brick.rb', line 126 def default_schema @default_schema end |
Class Method Details
._add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false) ⇒ Object
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 |
# File 'lib/brick/extensions.rb', line 1593 def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false) bt_assoc_name = fk[2] unless is_polymorphic bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id') bt_assoc_name[0..-4] 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] apartment = Object.const_defined?('Apartment') && Apartment fk[0] = Apartment.default_schema if apartment && apartment.excluded_models.include?(for_tbl.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_base = cnstr_name = "(brick) #{for_tbl}_#{pri_tbl}" cnstr_added_num = 1 cnstr_name = "#{cnstr_base}_#{cnstr_added_num += 1}" while bts&.key?(cnstr_name) || hms&.key?(cnstr_name) 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 assoc_hm[:inverse] = assoc_bt 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: for_tbl.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] = {} } hm_counts[fk[1]] = hm_counts.fetch(fk[1]) { 0 } + 1 end assoc_bt[:inverse] = assoc_hm end |
._class_pk(dotted_name, multitenant) ⇒ Object
1833 1834 1835 |
# File 'lib/brick/extensions.rb', line 1833 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)
286 287 288 289 290 291 292 293 |
# File 'lib/brick.rb', line 286 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
142 143 144 145 146 147 |
# File 'lib/brick.rb', line 142 def apartment_multitenant if @apartment_multitenant.nil? @apartment_multitenant = ::Brick.config.schema_behavior[:multitenant] && Object.const_defined?('Apartment') end @apartment_multitenant 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.
450 451 452 453 454 |
# File 'lib/brick.rb', line 450 def config @config ||= Brick::Config.instance yield @config if block_given? @config end |
.default_route_fallback=(resource_name) ⇒ Object
366 367 368 |
# File 'lib/brick.rb', line 366 def default_route_fallback=(resource_name) Brick.config.default_route_fallback = resource_name end |
.enable_controllers=(value) ⇒ Object
Switches Brick auto-controllers on or off, for all threads
217 218 219 |
# File 'lib/brick.rb', line 217 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.
224 225 226 |
# File 'lib/brick.rb', line 224 def enable_controllers? !!Brick.config.enable_controllers end |
.enable_models=(value) ⇒ Object
Switches Brick auto-models on or off, for all threads
204 205 206 |
# File 'lib/brick.rb', line 204 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.
211 212 213 |
# File 'lib/brick.rb', line 211 def enable_models? !!Brick.config.enable_models end |
.enable_routes=(value) ⇒ Object
Switches Brick auto-routes on or off, for all threads
243 244 245 |
# File 'lib/brick.rb', line 243 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.
250 251 252 |
# File 'lib/brick.rb', line 250 def enable_routes? !!Brick.config.enable_routes end |
.enable_views=(value) ⇒ Object
Switches Brick auto-views on or off, for all threads
230 231 232 |
# File 'lib/brick.rb', line 230 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.
237 238 239 |
# File 'lib/brick.rb', line 237 def enable_views? !!Brick.config.enable_views end |
.exclude_column(table, col) ⇒ Object
193 194 195 196 |
# File 'lib/brick.rb', line 193 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)
303 304 305 306 307 308 309 310 |
# File 'lib/brick.rb', line 303 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
260 261 262 |
# File 'lib/brick.rb', line 260 def exclude_tables=(value) Brick.config.exclude_tables = value end |
.existing_stis ⇒ Object
121 122 123 |
# File 'lib/brick.rb', line 121 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
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 |
# File 'lib/brick/extensions.rb', line 1771 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`.
431 432 433 |
# File 'lib/brick.rb', line 431 def gem_version ::Gem::Version.new(VERSION::STRING) end |
.get_bts_and_hms(model) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/brick.rb', line 154 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 s.first[a.foreign_key] = if a.polymorphic? primary_tables = relations[model.table_name][:fks].find { |_k, fk| fk[:assoc_name] == a.name.to_s }&.last&.fetch(:inverse_table, []) models = primary_tables&.map { |table| table.singularize.camelize.constantize } [a.name, models, true] else [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
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 |
# File 'lib/brick/extensions.rb', line 1720 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 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_activerecord_bases = ActiveRecord::Base.descendants.select { |ar| ar.abstract_class? }.map(&:name) # abstract_activerecord_bases << ActiveRecord::Base 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 { |v| [(r = v.pluralize), (model = models[r])&.last&.table_name || v, migrations&.fetch(r, nil), model&.first] } end |
.has_ones=(hos) ⇒ Object
Associations to treat as a has_one
320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/brick.rb', line 320 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
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/brick.rb', line 373 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&.include?(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
275 276 277 |
# File 'lib/brick.rb', line 275 def (value) Brick.config. = value end |
.model_descrips=(descrips) ⇒ Object
DSL templates for individual models to provide prettier descriptions of objects
341 342 343 |
# File 'lib/brick.rb', line 341 def model_descrips=(descrips) Brick.config.model_descrips = descrips end |
.models_inherit_from=(value) ⇒ Object
265 266 267 |
# File 'lib/brick.rb', line 265 def models_inherit_from=(value) Brick.config.models_inherit_from = value end |
.non_tenanted_models ⇒ Object
If multitenancy is enabled, a list of non-tenanted “global” models
150 151 152 |
# File 'lib/brick.rb', line 150 def non_tenanted_models @pending_models ||= {} end |
.not_nullables=(value) ⇒ Object
280 281 282 |
# File 'lib/brick.rb', line 280 def not_nullables=(value) Brick.config.not_nullables = value end |
.order=(value) ⇒ Object
296 297 298 |
# File 'lib/brick.rb', line 296 def order=(value) Brick.config.order = value end |
.polymorphics=(polys) ⇒ Object
Polymorphic associations
334 335 336 337 |
# File 'lib/brick.rb', line 334 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)
137 138 139 140 |
# File 'lib/brick.rb', line 137 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.
354 355 356 |
# File 'lib/brick.rb', line 354 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
358 359 360 |
# File 'lib/brick.rb', line 358 def schema_behaviour=(behavior) Brick.schema_behavior = behavior end |
.serializer ⇒ Object
Get the Brick serializer used by all threads.
443 444 445 |
# File 'lib/brick.rb', line 443 def serializer Brick.config.serializer end |
.serializer=(value) ⇒ Object
Set the Brick serializer. This setting affects all threads.
437 438 439 |
# File 'lib/brick.rb', line 437 def serializer=(value) Brick.config.serializer = value end |
.set_db_schema(params) ⇒ Object
128 129 130 131 132 133 134 |
# File 'lib/brick.rb', line 128 def set_db_schema(params) schema = params['_brick_schema'] || 'public' if schema && ::Brick.db_schemas&.include?(schema) ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema) schema end end |
.skip_database_views=(value) ⇒ Object
255 256 257 |
# File 'lib/brick.rb', line 255 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
314 315 316 |
# File 'lib/brick.rb', line 314 def skip_index_hms=(value) Brick.config.skip_index_hms = value end |
.sti_models ⇒ Object
117 118 119 |
# File 'lib/brick.rb', line 117 def self.sti_models @sti_models ||= {} end |
.sti_namespace_prefixes=(snp) ⇒ Object
Module prefixes to build out and associate with specific base STI models
347 348 349 |
# File 'lib/brick.rb', line 347 def sti_namespace_prefixes=(snp) Brick.config.sti_namespace_prefixes = snp end |
.sti_type_column=(type_col) ⇒ Object
362 363 364 |
# File 'lib/brick.rb', line 362 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
270 271 272 |
# File 'lib/brick.rb', line 270 def table_name_prefixes=(value) Brick.config.table_name_prefixes = value end |
.unexclude_column(table, col) ⇒ Object
197 198 199 200 |
# File 'lib/brick.rb', line 197 def unexclude_column(table, col) puts "Unexcluding #{table}.#{col}" true end |
.version ⇒ Object
457 458 459 |
# File 'lib/brick.rb', line 457 def version VERSION::STRING end |