brick 1.0.228 → 1.0.230
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/extensions.rb +123 -55
- data/lib/brick/rails/engine.rb +41 -19
- data/lib/brick/reflect_tables.rb +54 -22
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +5 -1
- data/lib/generators/brick/airtable_api_caller.rb +171 -0
- data/lib/generators/brick/airtable_migrations_generator.rb +24 -0
- data/lib/generators/brick/airtable_seeds_generator.rb +19 -0
- data/lib/generators/brick/install_generator.rb +18 -6
- data/lib/generators/brick/{migration_builder.rb → migrations_builder.rb} +7 -3
- data/lib/generators/brick/migrations_generator.rb +4 -4
- data/lib/generators/brick/salesforce_migrations_generator.rb +3 -3
- data/lib/generators/brick/salesforce_schema.rb +1 -1
- data/lib/generators/brick/seeds_builder.rb +329 -0
- data/lib/generators/brick/seeds_generator.rb +2 -242
- metadata +7 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 5230864d4450f23406da3efcab54e2c337ae9029c2b9fea3d1b8df1bb34583dd
         | 
| 4 | 
            +
              data.tar.gz: b16f195b58d0df45611f8a2251e005ae6cc841970fb1c6eea17119af678d62a8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 55e9c658e0c258be349866a82f7c5a62234779aa27e7a5e04517979dbccdf9475948fecb2de8f78604daf773094e9a19f8a3c32b9d64ef5c8948cbd3fa4dffe2
         | 
| 7 | 
            +
              data.tar.gz: a00b991cd2d4125e10baada1d070440d4b25a99994e462e543acafdcdcf4cf5a4703e7b0fc60dd2e6989e933828a9eb0355610f6901b9b2ca9d5a047a61260a3
         | 
    
        data/lib/brick/extensions.rb
    CHANGED
    
    | @@ -979,7 +979,8 @@ module ActiveRecord | |
| 979 979 | 
             
                      next
         | 
| 980 980 | 
             
                    end
         | 
| 981 981 |  | 
| 982 | 
            -
                    tbl_alias =  | 
| 982 | 
            +
                    tbl_alias = "b_r_#{hm.name}"
         | 
| 983 | 
            +
                    tbl_alias = unique63(tbl_alias, previous) if is_postgres
         | 
| 983 984 | 
             
                    on_clause = []
         | 
| 984 985 | 
             
                    hm_selects = if !pri_key.is_a?(Array) # Probable standard key?
         | 
| 985 986 | 
             
                                   if fk_col.is_a?(Array) # Foreign is composite but not Primary?  OK, or choose the first part of the foreign key if nothing else
         | 
| @@ -1031,19 +1032,28 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}# | |
| 1031 1032 | 
             
                  unless wheres.empty?
         | 
| 1032 1033 | 
             
                    # Rewrite the wheres to reference table and correlation names built out by AREL
         | 
| 1033 1034 | 
             
                    where_nots = {}
         | 
| 1035 | 
            +
                    where_comparisons = []
         | 
| 1034 1036 | 
             
                    wheres2 = wheres.each_with_object({}) do |v, s|
         | 
| 1035 1037 | 
             
                      is_not = if v.first[-1] == '!'
         | 
| 1036 1038 | 
             
                                 v[0] = v[0][0..-2] # Take off ending ! from column name
         | 
| 1037 1039 | 
             
                               end
         | 
| 1038 | 
            -
                      if (v_parts = v.first.split('.')).length == 1
         | 
| 1039 | 
            -
             | 
| 1040 | 
            +
                      tbl_and_col_name = if (v_parts = v.first.split('.')).length == 1
         | 
| 1041 | 
            +
                                           []
         | 
| 1042 | 
            +
                                         else
         | 
| 1043 | 
            +
                                           [brick_links[v_parts[0..-2].join('.')].split('.').last]
         | 
| 1044 | 
            +
                                         end
         | 
| 1045 | 
            +
                      tbl_and_col_name << v_parts.last
         | 
| 1046 | 
            +
                      if ['>', '<'].include?(first_char = v.last.first[0]) # Greater than or less than?
         | 
| 1047 | 
            +
                        col_name = v.last.first[1..-1]
         | 
| 1048 | 
            +
                        col_name = "'#{col_name}'" unless [:integer, :boolean, :decimal, :float].include?(klass.columns_hash[v.first].type)
         | 
| 1049 | 
            +
                        where_comparisons << "#{tbl_and_col_name.join('.')} #{first_char} #{col_name}"
         | 
| 1040 1050 | 
             
                      else
         | 
| 1041 | 
            -
                         | 
| 1042 | 
            -
                        (is_not ? where_nots : s)["#{tbl_name}.#{v_parts.last}"] = v.last
         | 
| 1051 | 
            +
                        (is_not ? where_nots : s)[tbl_and_col_name.join('.')] = v.last
         | 
| 1043 1052 | 
             
                      end
         | 
| 1044 1053 | 
             
                    end
         | 
| 1045 1054 | 
             
                    if respond_to?(:where!)
         | 
| 1046 1055 | 
             
                      where!(wheres2) if wheres2.present?
         | 
| 1056 | 
            +
                      where_comparisons.each { |wc| where!(wc) }
         | 
| 1047 1057 | 
             
                      if where_nots.present?
         | 
| 1048 1058 | 
             
                        self.where_clause += WhereClause.new(predicate_builder.build_from_hash(where_nots)).invert
         | 
| 1049 1059 | 
             
                      end
         | 
| @@ -1106,8 +1116,7 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}# | |
| 1106 1116 |  | 
| 1107 1117 | 
             
                # Build out an AR relation that queries for a list of objects, and include all the appropriate JOINs to later apply DSL using #brick_descrip
         | 
| 1108 1118 | 
             
                def brick_list
         | 
| 1109 | 
            -
                   | 
| 1110 | 
            -
                  selects = pks.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
         | 
| 1119 | 
            +
                  selects = klass._pk_as_array.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
         | 
| 1111 1120 | 
             
                  # Get foreign keys for anything marked to be auto-preloaded, or a self-referencing JOIN
         | 
| 1112 1121 | 
             
                  klass_cols = klass.column_names
         | 
| 1113 1122 | 
             
                  reflect_on_all_associations.each do |a|
         | 
| @@ -1740,52 +1749,6 @@ class Object | |
| 1740 1749 | 
             
                  built_model = Class.new(base_model) do |new_model_class|
         | 
| 1741 1750 | 
             
                    (schema_module || Object).const_set(chosen_name, new_model_class) unless is_generator
         | 
| 1742 1751 | 
             
                    @_brick_relation = relation
         | 
| 1743 | 
            -
                    # Enable Elasticsearch for this one?
         | 
| 1744 | 
            -
                    access = ::Brick.elasticsearch_models&.is_a?(Hash) ? ::Brick.elasticsearch_models[name] : ::Brick.elasticsearch_models
         | 
| 1745 | 
            -
                    @_brick_es_crud ||= case access
         | 
| 1746 | 
            -
                                        when String
         | 
| 1747 | 
            -
                                          access
         | 
| 1748 | 
            -
                                        when :all, :full
         | 
| 1749 | 
            -
                                          'icrud' # Auto-create index, and full CRUD
         | 
| 1750 | 
            -
                                        else
         | 
| 1751 | 
            -
                                          ''
         | 
| 1752 | 
            -
                                        end
         | 
| 1753 | 
            -
                    unless @_brick_es_crud.blank?
         | 
| 1754 | 
            -
                      include ::Elasticsearch::Model
         | 
| 1755 | 
            -
                      code << "  include Elasticsearch::Model\n"
         | 
| 1756 | 
            -
                      if @_brick_es_crud.index('i') # Enable auto-creation of indexes on import?
         | 
| 1757 | 
            -
                        class << self
         | 
| 1758 | 
            -
                          alias _original_import import
         | 
| 1759 | 
            -
                          def import(options={}, &block)
         | 
| 1760 | 
            -
                            self.__elasticsearch__.create_index! unless self.__elasticsearch__.index_exists?
         | 
| 1761 | 
            -
                            _original_import(options={}, &block)
         | 
| 1762 | 
            -
                          end
         | 
| 1763 | 
            -
                        end
         | 
| 1764 | 
            -
                      end
         | 
| 1765 | 
            -
                      if @_brick_es_crud.index('c') || @_brick_es_crud.index('u') || @_brick_es_crud.index('d')
         | 
| 1766 | 
            -
                        include ::Elasticsearch::Model::Callbacks
         | 
| 1767 | 
            -
                        code << "  include Elasticsearch::Model::Callbacks\n"
         | 
| 1768 | 
            -
                      end
         | 
| 1769 | 
            -
                      if @_brick_es_crud.index('r')
         | 
| 1770 | 
            -
                        # Customer.__elasticsearch__.search('taco').to_a
         | 
| 1771 | 
            -
                      end
         | 
| 1772 | 
            -
                      # # Need some mappings for text columns
         | 
| 1773 | 
            -
                      # mappings do
         | 
| 1774 | 
            -
                      #   indexes :company_name, type: 'text'
         | 
| 1775 | 
            -
                      #   indexes :country, type: 'text'
         | 
| 1776 | 
            -
                      # end
         | 
| 1777 | 
            -
                      # def self.search(q)
         | 
| 1778 | 
            -
                      #   s = self.__elasticsearch__.search(q)
         | 
| 1779 | 
            -
                      #   binding.pry
         | 
| 1780 | 
            -
                      #   s.to_a
         | 
| 1781 | 
            -
                      # #   class Elasticsearch::Model::Response::Response
         | 
| 1782 | 
            -
                      # #     def to_a
         | 
| 1783 | 
            -
                      # #   end
         | 
| 1784 | 
            -
                      # # rescue Elastic::Transport::Transport::Errors::NotFound => e
         | 
| 1785 | 
            -
                      # #   self.create_index!
         | 
| 1786 | 
            -
                      # #   self.__elasticsearch__.search(q)
         | 
| 1787 | 
            -
                      # end
         | 
| 1788 | 
            -
                    end
         | 
| 1789 1752 | 
             
                    if inheritable_name
         | 
| 1790 1753 | 
             
                      new_model_class.define_singleton_method :inherited do |subclass|
         | 
| 1791 1754 | 
             
                        super(subclass)
         | 
| @@ -1858,6 +1821,59 @@ class Object | |
| 1858 1821 | 
             
                      end
         | 
| 1859 1822 | 
             
                    end
         | 
| 1860 1823 |  | 
| 1824 | 
            +
                    # Enable Elasticsearch based on the table name?
         | 
| 1825 | 
            +
                    if (@_brick_es_crud = ::Brick.elasticsearch_models&.fetch(matching, nil))
         | 
| 1826 | 
            +
                      include ::Elasticsearch::Model
         | 
| 1827 | 
            +
                      if @_brick_es_crud.index('i') # Enable auto-creation of indexes on import?
         | 
| 1828 | 
            +
                        class << self
         | 
| 1829 | 
            +
                          alias _original_import import
         | 
| 1830 | 
            +
                          def import(options={}, &block)
         | 
| 1831 | 
            +
                            unless self.__elasticsearch__.index_exists?
         | 
| 1832 | 
            +
                              self.__elasticsearch__.create_index!
         | 
| 1833 | 
            +
                              ::Brick.elasticsearch_existings[self.table_name] = self.table_name.tr('.', '-').pluralize
         | 
| 1834 | 
            +
                            end
         | 
| 1835 | 
            +
                            _original_import(options={}, &block)
         | 
| 1836 | 
            +
                          end
         | 
| 1837 | 
            +
                        end
         | 
| 1838 | 
            +
                      end
         | 
| 1839 | 
            +
                      if ::Elasticsearch::Model.const_defined?('Callbacks') &&
         | 
| 1840 | 
            +
                         @_brick_es_crud.index('c') || @_brick_es_crud.index('u') || @_brick_es_crud.index('d')
         | 
| 1841 | 
            +
                        include ::Elasticsearch::Model::Callbacks
         | 
| 1842 | 
            +
                        is_include_es_callbacks = true
         | 
| 1843 | 
            +
                      end
         | 
| 1844 | 
            +
                      # Create mappings for all text columns
         | 
| 1845 | 
            +
                      mappings do
         | 
| 1846 | 
            +
                        has_mappings = nil
         | 
| 1847 | 
            +
                        new_model_class.columns.select { |col| [:string, :text].include?(col.type) }.each do |string_col|
         | 
| 1848 | 
            +
                          unless has_mappings
         | 
| 1849 | 
            +
                            code << "  include Elasticsearch::Model\n"
         | 
| 1850 | 
            +
                            code << "  include Elasticsearch::Model::Callbacks\n" if is_include_es_callbacks
         | 
| 1851 | 
            +
                            code << "  mappings do\n"
         | 
| 1852 | 
            +
                            has_mappings = true
         | 
| 1853 | 
            +
                          end
         | 
| 1854 | 
            +
                          code << "    indexes :#{string_col.name}, type: 'text'\n"
         | 
| 1855 | 
            +
                          indexes string_col.name.to_sym, type: 'text'
         | 
| 1856 | 
            +
                        end
         | 
| 1857 | 
            +
                        code << "  end\n" if has_mappings
         | 
| 1858 | 
            +
                      end
         | 
| 1859 | 
            +
                      if @_brick_es_crud.index('r')
         | 
| 1860 | 
            +
                        class << self
         | 
| 1861 | 
            +
                          # Search and hydrate records using only Elasticsearch data
         | 
| 1862 | 
            +
                          define_method :search do |q|
         | 
| 1863 | 
            +
                            self.__elasticsearch__.search(q).raw_response.body['hits']['hits'].map do |hit|
         | 
| 1864 | 
            +
                              obj = self.new(hit['_source'])
         | 
| 1865 | 
            +
                              obj.instance_variable_set(:@new_record, false) # Don't want to accidentally save a new one
         | 
| 1866 | 
            +
                              obj
         | 
| 1867 | 
            +
                            end
         | 
| 1868 | 
            +
                          rescue Elastic::Transport::Transport::Errors::NotFound => e
         | 
| 1869 | 
            +
                            self.__elasticsearch__.create_index! if @_brick_es_crud.index('i')
         | 
| 1870 | 
            +
                            ::Brick.elasticsearch_existings[self.table_name] = self.table_name.tr('.', '-').pluralize
         | 
| 1871 | 
            +
                            []
         | 
| 1872 | 
            +
                          end
         | 
| 1873 | 
            +
                        end
         | 
| 1874 | 
            +
                      end
         | 
| 1875 | 
            +
                    end
         | 
| 1876 | 
            +
             | 
| 1861 1877 | 
             
                    unless is_sti
         | 
| 1862 1878 | 
             
                      fks = relation[:fks] || {}
         | 
| 1863 1879 | 
             
                      # Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
         | 
| @@ -2272,6 +2288,39 @@ class Object | |
| 2272 2288 | 
             
                        render json: { data: result } # [ver, result]
         | 
| 2273 2289 | 
             
                      end
         | 
| 2274 2290 |  | 
| 2291 | 
            +
                      self.define_method :search do
         | 
| 2292 | 
            +
                        # TODO: Make sure at least one index is actually present which allows for reading before attempting
         | 
| 2293 | 
            +
                        if (@qry = params['qry'] || params['_brick_es']) # Elasticsearch query?
         | 
| 2294 | 
            +
                          @indexes = ::Brick.elasticsearch_existings&.keys
         | 
| 2295 | 
            +
                          hits = Elasticsearch::Model.client.search({index: @indexes.join(','), q: @qry, size: 100})
         | 
| 2296 | 
            +
                          model_infos = {}
         | 
| 2297 | 
            +
                          # Number of indexes used:  hits.body['_shards']['total']
         | 
| 2298 | 
            +
                          @count = hits.body['hits']['total']['value']
         | 
| 2299 | 
            +
                          @results = hits.body['hits']['hits'].map do |x|
         | 
| 2300 | 
            +
                            klass = ::Brick.relations[::Brick.elasticsearch_existings[x['_index']]][:class_name].constantize
         | 
| 2301 | 
            +
                            model_info = model_infos[klass] ||= [
         | 
| 2302 | 
            +
                              klass.primary_key,
         | 
| 2303 | 
            +
                              klass.brick_parse_dsl(join_array = ::Brick::JoinArray.new, [], translations = {}, false, nil, true)
         | 
| 2304 | 
            +
                            ]
         | 
| 2305 | 
            +
                            obj = klass.new(x['_source'])
         | 
| 2306 | 
            +
                            [
         | 
| 2307 | 
            +
                              klass.name,
         | 
| 2308 | 
            +
                              obj.send(model_info.first),
         | 
| 2309 | 
            +
                              obj.brick_descrip(
         | 
| 2310 | 
            +
                                model_info.last&.first&.map { |col2| obj.send(col2.last) },
         | 
| 2311 | 
            +
                                obj.send(klass.primary_key)
         | 
| 2312 | 
            +
                              ),
         | 
| 2313 | 
            +
                              send("#{klass._brick_index(:singular)}_path".to_sym, obj),
         | 
| 2314 | 
            +
                              x['_score']
         | 
| 2315 | 
            +
                            ]
         | 
| 2316 | 
            +
                          end
         | 
| 2317 | 
            +
                          respond_to do |format|
         | 
| 2318 | 
            +
                            format.js { render json: { result: @results } }
         | 
| 2319 | 
            +
                            format.any {}
         | 
| 2320 | 
            +
                          end
         | 
| 2321 | 
            +
                        end
         | 
| 2322 | 
            +
                      end
         | 
| 2323 | 
            +
             | 
| 2275 2324 | 
             
                      return [new_controller_class, code + "end # BrickGem controller\n"]
         | 
| 2276 2325 | 
             
                    when 'BrickOpenapi'
         | 
| 2277 2326 | 
             
                      is_openapi = true
         | 
| @@ -2584,7 +2633,26 @@ class Object | |
| 2584 2633 | 
             
                          render json: { result: ::Brick.unexclude_column(table_name, col) }
         | 
| 2585 2634 | 
             
                        elsif is_json && (q = params['_brick_es']) # Elasticsearch
         | 
| 2586 2635 | 
             
                          # Make sure that the index is actually present and we allow reading before attempting
         | 
| 2587 | 
            -
                           | 
| 2636 | 
            +
                          if (es_perms = model.instance_variable_get(:@_brick_es_crud) ||
         | 
| 2637 | 
            +
                                         # If a perms instance variable is missing for this model, get the perms ...
         | 
| 2638 | 
            +
                                         (set = true && ::Brick.elasticsearch_models&.fetch(model.table_name, nil))
         | 
| 2639 | 
            +
                             ) && (
         | 
| 2640 | 
            +
                              # ... then set it on the model
         | 
| 2641 | 
            +
                              (set && model.instance_variable_set(:@_brick_es_crud, es_perms)) || es_perms
         | 
| 2642 | 
            +
                             )&.index('r')
         | 
| 2643 | 
            +
                            es_result = begin
         | 
| 2644 | 
            +
                                          model.__elasticsearch__.search(q)
         | 
| 2645 | 
            +
                                        rescue Elastic::Transport::Transport::Errors::NotFound => e
         | 
| 2646 | 
            +
                                          if @_brick_es_crud.index('i')
         | 
| 2647 | 
            +
                                            self.__elasticsearch__.create_index!
         | 
| 2648 | 
            +
                                            # model.import
         | 
| 2649 | 
            +
                                            ::Brick.elasticsearch_existings[self.table_name] = self.table_name.tr('.', '-').pluralize
         | 
| 2650 | 
            +
                                            model.__elasticsearch__.search(q)
         | 
| 2651 | 
            +
                                          else
         | 
| 2652 | 
            +
                                            []
         | 
| 2653 | 
            +
                                          end
         | 
| 2654 | 
            +
                                        end
         | 
| 2655 | 
            +
                          end
         | 
| 2588 2656 | 
             
                          render json: { result: es_result }
         | 
| 2589 2657 | 
             
                        else
         | 
| 2590 2658 | 
             
                          real_model = model.real_model(params)
         | 
| @@ -3254,7 +3322,7 @@ module Brick | |
| 3254 3322 | 
             
                    # Rails applies an _index suffix to that route when the resource name isn't something plural
         | 
| 3255 3323 | 
             
                    index << '_index' if mode != :singular && !not_path &&
         | 
| 3256 3324 | 
             
                                         index == (
         | 
| 3257 | 
            -
                                                    index2 + [relation[:class_name][(relation&.fetch(:auto_prefixed_class, nil)&.length&.+ 2) || 0..-1 | 
| 3325 | 
            +
                                                    index2 + [relation[:class_name]&.[]((relation&.fetch(:auto_prefixed_class, nil)&.length&.+ 2) || 0..-1)&.underscore&.tr('/', '_') || '_']
         | 
| 3258 3326 | 
             
                                                  ).join(separator)
         | 
| 3259 3327 | 
             
                  end
         | 
| 3260 3328 | 
             
                  index
         | 
    
        data/lib/brick/rails/engine.rb
    CHANGED
    
    | @@ -671,10 +671,16 @@ window.addEventListener(\"popstate\", linkSchemas); | |
| 671 671 | 
             
                        def find_template(*args, **options)
         | 
| 672 672 | 
             
                          find_template_err = nil
         | 
| 673 673 | 
             
                          unless (model_name = @_brick_model&.name) ||
         | 
| 674 | 
            -
                                 ( | 
| 675 | 
            -
             | 
| 676 | 
            -
             | 
| 677 | 
            -
             | 
| 674 | 
            +
                                 (
         | 
| 675 | 
            +
                                  args[1].first == 'brick_gem' &&
         | 
| 676 | 
            +
                                  ((is_search = ::Brick.config.add_search && args[0] == 'search' &&
         | 
| 677 | 
            +
                                                ::Brick.elasticsearch_existings&.length&.positive?
         | 
| 678 | 
            +
                                   ) ||
         | 
| 679 | 
            +
                                   (is_status = ::Brick.config.add_status && args[0] == 'status') ||
         | 
| 680 | 
            +
                                   (is_orphans = ::Brick.config.add_orphans && args[0] == 'orphans') ||
         | 
| 681 | 
            +
                                   (is_crosstab = args[0] == 'crosstab')
         | 
| 682 | 
            +
                                  )
         | 
| 683 | 
            +
                                 )
         | 
| 678 684 | 
             
                            begin
         | 
| 679 685 | 
             
                              if (possible_template = _brick_find_template(*args, **options))
         | 
| 680 686 | 
             
                                return possible_template
         | 
| @@ -792,7 +798,7 @@ window.addEventListener(\"popstate\", linkSchemas); | |
| 792 798 | 
             
                                            b[0] = '' if b[0].is_a?(Symbol)
         | 
| 793 799 | 
             
                                            a.first <=> b.first
         | 
| 794 800 | 
             
                                          end.each_with_object(+'') do |rel, s|
         | 
| 795 | 
            -
                                            next if rel.first.blank? || rel.last[:cols].empty? ||
         | 
| 801 | 
            +
                                            next if rel.first.is_a?(Symbol) || rel.first.blank? || rel.last[:cols].empty? ||
         | 
| 796 802 | 
             
                                                    ::Brick.config.exclude_tables.include?(rel.first)
         | 
| 797 803 |  | 
| 798 804 | 
             
                                            # %%% When table_name_prefixes are use then during rendering empty non-TNP
         | 
| @@ -805,7 +811,7 @@ window.addEventListener(\"popstate\", linkSchemas); | |
| 805 811 | 
             
                                          end.html_safe
         | 
| 806 812 | 
             
                          # Options for special Brick pages
         | 
| 807 813 | 
             
                          prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
         | 
| 808 | 
            -
                          [['Search',  | 
| 814 | 
            +
                          [['Search', is_search],
         | 
| 809 815 | 
             
                           ['Status', ::Brick.config.add_status],
         | 
| 810 816 | 
             
                           ['Orphans', is_orphans],
         | 
| 811 817 | 
             
                           ['Crosstab', is_crosstab]].each do |table_option, show_it|
         | 
| @@ -1084,13 +1090,14 @@ end | |
| 1084 1090 | 
             
            <% end
         | 
| 1085 1091 | 
             
               # SEARCH BOX
         | 
| 1086 1092 | 
             
               if @_brick_es&.index('r') # Must have at least Elasticsearch Read access %>
         | 
| 1087 | 
            -
              <input type=\"text\" id=\"esSearch\" | 
| 1093 | 
            +
              <input type=\"text\" id=\"esSearch\" class=\"dimmed\">
         | 
| 1088 1094 | 
             
              <script>
         | 
| 1089 | 
            -
                var esSearch = document.getElementById(\"esSearch\")
         | 
| 1095 | 
            +
                var esSearch = document.getElementById(\"esSearch\");
         | 
| 1096 | 
            +
                var usedTerms = {};
         | 
| 1090 1097 | 
             
                var isEsFiltered = false;
         | 
| 1091 1098 | 
             
                esSearch.addEventListener(\"input\", function () {
         | 
| 1092 1099 | 
             
                  var gridTrs;
         | 
| 1093 | 
            -
                  if (this.value.length > 2) {  | 
| 1100 | 
            +
                  if (this.value.length > 2 && usedTerms[this.value] !== null) { // At least 3 letters in the search term
         | 
| 1094 1101 | 
             
                    var es = doFetch(\"POST\", {_brick_es: this.value},
         | 
| 1095 1102 | 
             
                      function (p) {p.text().then(function (response) {
         | 
| 1096 1103 | 
             
                        var result = JSON.parse(response).result;
         | 
| @@ -1111,13 +1118,16 @@ end | |
| 1111 1118 | 
             
                            if (!isHit) row.style.display = \"none\";
         | 
| 1112 1119 | 
             
                          }
         | 
| 1113 1120 | 
             
                          isEsFiltered = true;
         | 
| 1121 | 
            +
                          esSearch.className = \"\";
         | 
| 1114 1122 | 
             
                        } else {
         | 
| 1115 1123 | 
             
                          if (isEsFiltered) { // Show all rows and gray the search box
         | 
| 1124 | 
            +
                            usedTerms[this.value] = null; // No results for this term
         | 
| 1116 1125 | 
             
                            gridTrs = [... grid.querySelectorAll(\"tr\")];
         | 
| 1117 1126 | 
             
                            for (var i = 1; i < gridTrs.length; ++i) {
         | 
| 1118 1127 | 
             
                              gridTrs[i].style.display = \"table-row\";
         | 
| 1119 1128 | 
             
                            }
         | 
| 1120 1129 | 
             
                          }
         | 
| 1130 | 
            +
                          esSearch.className = \"dimmed\";
         | 
| 1121 1131 | 
             
                        }
         | 
| 1122 1132 | 
             
                      });}
         | 
| 1123 1133 | 
             
                    );
         | 
| @@ -1127,6 +1137,7 @@ end | |
| 1127 1137 | 
             
                      for (var i = 1; i < gridTrs.length; ++i) {
         | 
| 1128 1138 | 
             
                        gridTrs[i].style.display = \"table-row\";
         | 
| 1129 1139 | 
             
                      }
         | 
| 1140 | 
            +
                      esSearch.className = \"dimmed\";
         | 
| 1130 1141 | 
             
                    }
         | 
| 1131 1142 | 
             
                  }
         | 
| 1132 1143 | 
             
                });
         | 
| @@ -1211,21 +1222,28 @@ end | |
| 1211 1222 | 
             
            +"#{css}
         | 
| 1212 1223 | 
             
            <p class=\"flashNotice\"><%= notice if request.respond_to?(:flash) %></p>#{"
         | 
| 1213 1224 | 
             
            #{schema_options}" if schema_options}
         | 
| 1214 | 
            -
            <select id=\"tbl\">#{table_options}</select>
         | 
| 1215 | 
            -
            < | 
| 1225 | 
            +
            <select id=\"tbl\">#{table_options}</select><br><br>
         | 
| 1226 | 
            +
            <form method=\"get\">
         | 
| 1227 | 
            +
              <input type=\"text\" name=\"qry\"<%= \" value=\\\"#\{@qry}\\\"\".html_safe unless @qry.blank? %>><input type=\"submit\", value=\"Search\">
         | 
| 1228 | 
            +
            </form>
         | 
| 1229 | 
            +
            <% if @results.present? %>
         | 
| 1230 | 
            +
            <div id=\"rowCount\"><b><%= @count %> results from: </b><%= @indexes.sort.join(', ') %></div>
         | 
| 1231 | 
            +
            <% end %>
         | 
| 1216 1232 | 
             
            <table id=\"resourceName\" class=\"shadow\"><thead><tr>
         | 
| 1217 1233 | 
             
              <th>Resource</th>
         | 
| 1218 1234 | 
             
              <th>Description</th>
         | 
| 1219 | 
            -
              <th> | 
| 1235 | 
            +
              <th>Score</th>
         | 
| 1220 1236 | 
             
            </tr></thead>
         | 
| 1221 1237 | 
             
            <tbody>
         | 
| 1222 | 
            -
            <%  | 
| 1238 | 
            +
            <% @results&.each do |r| %>
         | 
| 1223 1239 | 
             
              <tr>
         | 
| 1224 | 
            -
              <td><%=  | 
| 1225 | 
            -
             | 
| 1226 | 
            -
               | 
| 1240 | 
            +
              <td><%= link_to (r[3]) do %><%= r[0] %><br>
         | 
| 1241 | 
            +
                  <%= r[1] %><% end %>
         | 
| 1242 | 
            +
              </td>
         | 
| 1243 | 
            +
              <td><%= r[2] %></td>
         | 
| 1244 | 
            +
              <td><%= '%.3f' % r[4] %></td>
         | 
| 1227 1245 | 
             
              </tr>
         | 
| 1228 | 
            -
            <%  | 
| 1246 | 
            +
            <% end %>
         | 
| 1229 1247 | 
             
            </tbody></table>
         | 
| 1230 1248 | 
             
            #{script}"
         | 
| 1231 1249 | 
             
                                     end
         | 
| @@ -1645,9 +1663,13 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true}); | |
| 1645 1663 | 
             
               # %%% Create a smart javascript routine which can do this client-side %>
         | 
| 1646 1664 | 
             
            [... document.getElementsByTagName(\"TH\")].forEach(function (th) {
         | 
| 1647 1665 | 
             
              th.addEventListener(\"click\", function (e) {
         | 
| 1648 | 
            -
                var xOrder | 
| 1649 | 
            -
             | 
| 1666 | 
            +
                var xOrder,
         | 
| 1667 | 
            +
                    currentOrder;
         | 
| 1668 | 
            +
                if (xOrder = this.getAttribute(\"x-order\")) {
         | 
| 1669 | 
            +
                  if ((currentOrder = changeout(location.href, \"_brick_order\")) === xOrder)
         | 
| 1670 | 
            +
                    xOrder = \"-\" + xOrder;
         | 
| 1650 1671 | 
             
                  location.href = changeout(location.href, \"_brick_order\", xOrder);
         | 
| 1672 | 
            +
                }
         | 
| 1651 1673 | 
             
              });
         | 
| 1652 1674 | 
             
            });
         | 
| 1653 1675 | 
             
            document.querySelectorAll(\"input, select\").forEach(function (inp) {
         | 
    
        data/lib/brick/reflect_tables.rb
    CHANGED
    
    | @@ -28,7 +28,8 @@ module Brick | |
| 28 28 | 
             
                        def method_missing(name, *args, &block)
         | 
| 29 29 | 
             
                          _original_method_missing(name, *args, &block)
         | 
| 30 30 | 
             
                        rescue Elastic::Transport::Transport::Errors::NotFound => e
         | 
| 31 | 
            -
                          if (missing_index = args.last&.fetch(:defined_params, nil)&.fetch(:index, nil))
         | 
| 31 | 
            +
                          if (missing_index = args.last&.fetch(:defined_params, nil)&.fetch(:index, nil)) &&
         | 
| 32 | 
            +
                             ::Brick.elasticsearch_models&.fetch(missing_index, nil)&.include?('i')
         | 
| 32 33 | 
             
                            self.indices.create({ index: missing_index,
         | 
| 33 34 | 
             
                                                  body: { settings: {}, mappings: { properties: {} } } })
         | 
| 34 35 | 
             
                            puts "Auto-creating missing index \"#{missing_index}\""
         | 
| @@ -47,34 +48,19 @@ module Brick | |
| 47 48 | 
             
                      begin
         | 
| 48 49 | 
             
                        cluster_info = client.info.body
         | 
| 49 50 | 
             
                        if (es_ver = cluster_info['version'])
         | 
| 50 | 
            -
                          ::Brick.elasticsearch_models = :all
         | 
| 51 51 | 
             
                          puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
         | 
| 52 | 
            -
                           | 
| 53 | 
            -
             | 
| 54 | 
            -
                           | 
| 55 | 
            -
             | 
| 56 | 
            -
                           | 
| 52 | 
            +
                          if ::Brick.elasticsearch_models.empty?
         | 
| 53 | 
            +
                            puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
         | 
| 54 | 
            +
                          end
         | 
| 55 | 
            +
                        else
         | 
| 56 | 
            +
                          ::Brick.elasticsearch_models = nil
         | 
| 57 57 | 
             
                        end
         | 
| 58 58 | 
             
                      rescue StandardError => e # Errno::ECONNREFUSED
         | 
| 59 | 
            +
                        ::Brick.elasticsearch_models = nil
         | 
| 59 60 | 
             
                        puts "Found Elasticsearch gem, but could not connect to #{'local ' unless es_uri}Elasticsearch/Opensearch server#{" at #{es_uri}" if es_uri}."
         | 
| 60 61 | 
             
                      end
         | 
| 61 | 
            -
                    # require 'net/http'
         | 
| 62 | 
            -
                    #   begin
         | 
| 63 | 
            -
                    #     es_uri = ENV['ELASTICSEARCH_URL']
         | 
| 64 | 
            -
                    #     binding.pry
         | 
| 65 | 
            -
                    #     cluster_info = JSON.parse(Net::HTTP.get(URI.parse(es_uri || 'http://localhost:9200')))
         | 
| 66 | 
            -
                    #     if (es_ver = cluster_info['version'])
         | 
| 67 | 
            -
                    #       ::Brick.elasticsearch_models = :all
         | 
| 68 | 
            -
                    #       puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
         | 
| 69 | 
            -
                    #       puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
         | 
| 70 | 
            -
                    #     end
         | 
| 71 | 
            -
                    #   rescue StandardError => e
         | 
| 72 | 
            -
                    #   end
         | 
| 73 62 | 
             
                    end
         | 
| 74 63 | 
             
                  end
         | 
| 75 | 
            -
                  # client = Elasticsearch::Client.new(host: 'https://my-elasticsearch-host.example')
         | 
| 76 | 
            -
                  # client.ping
         | 
| 77 | 
            -
                  # client.search(q: 'test')
         | 
| 78 64 |  | 
| 79 65 | 
             
                  # Overwrite SQLite's #begin_db_transaction so it opens in IMMEDIATE mode instead of
         | 
| 80 66 | 
             
                  # the default DEFERRED mode.
         | 
| @@ -527,6 +513,52 @@ ORDER BY 1, 2, c.internal_column_id, acc.position" | |
| 527 513 | 
             
                    end
         | 
| 528 514 | 
             
                  end
         | 
| 529 515 |  | 
| 516 | 
            +
                  if ems = ::Brick.elasticsearch_models # ['comments']
         | 
| 517 | 
            +
                    access = case ems
         | 
| 518 | 
            +
                             when Hash, String # Hash is a list of resource names and ES permissions such as 'r' or 'icr'
         | 
| 519 | 
            +
                               ems
         | 
| 520 | 
            +
                             when :all
         | 
| 521 | 
            +
                               'crud' # All CRUD
         | 
| 522 | 
            +
                             when :full
         | 
| 523 | 
            +
                               'icrud' # Also able to auto-create indexes
         | 
| 524 | 
            +
                             else
         | 
| 525 | 
            +
                               ''
         | 
| 526 | 
            +
                             end
         | 
| 527 | 
            +
                    # Rewriting this to have all valid indexes and their perms
         | 
| 528 | 
            +
                    ::Brick.elasticsearch_models = unless access.blank?
         | 
| 529 | 
            +
                      # Find all existing indexes
         | 
| 530 | 
            +
                      client = Elastic::Transport::Client.new
         | 
| 531 | 
            +
                      ::Brick.elasticsearch_existings = client.perform_request('GET', '_aliases').body.each_with_object({}) do |entry, s|
         | 
| 532 | 
            +
                        rel_name = entry.first.tr('-', '.')
         | 
| 533 | 
            +
                        s[entry.first] = rel_name if relations.include?(entry.first)
         | 
| 534 | 
            +
                        s[entry.first] = rel_name.singularize if relations.include?(rel_name.singularize)
         | 
| 535 | 
            +
                        entry.last.fetch('aliases', nil)&.each do |k, _v|
         | 
| 536 | 
            +
                          rel_name = k.tr('-', '.')
         | 
| 537 | 
            +
                          s[k] = rel_name if relations.include?(rel_name)
         | 
| 538 | 
            +
                          s[k] = rel_name.singularize if relations.include?(rel_name.singularize)
         | 
| 539 | 
            +
                        end
         | 
| 540 | 
            +
                      end
         | 
| 541 | 
            +
                      # Add this either if...
         | 
| 542 | 
            +
                      if access.is_a?(String) # ...they have permissions over absolutely anything,
         | 
| 543 | 
            +
                        relations.each_with_object({}) do |rel, s|
         | 
| 544 | 
            +
                          next if rel.first.is_a?(Symbol)
         | 
| 545 | 
            +
             | 
| 546 | 
            +
                          perms = rel.last.fetch(:isView, nil) ? access.tr('cud', '') : access
         | 
| 547 | 
            +
                          s[rel.first] = perms
         | 
| 548 | 
            +
                        end
         | 
| 549 | 
            +
                      else # or there are specific permissions for each resource, so find the matching indexes
         | 
| 550 | 
            +
                        client = Elastic::Transport::Client.new
         | 
| 551 | 
            +
                        ::Brick.elasticsearch_existings.each_with_object({}) do |index, s|
         | 
| 552 | 
            +
                          this_access = access.is_a?(String) ? access : access[index.first] || '' # Look up permissions from above
         | 
| 553 | 
            +
                          next unless (rel = relations.fetch(index.first, nil))
         | 
| 554 | 
            +
             | 
| 555 | 
            +
                          perms = rel&.fetch(:isView, nil) ? this_access.tr('cud', '') : this_access
         | 
| 556 | 
            +
                          s[index.first] = perms unless perms.blank?
         | 
| 557 | 
            +
                        end
         | 
| 558 | 
            +
                      end
         | 
| 559 | 
            +
                    end
         | 
| 560 | 
            +
                  end
         | 
| 561 | 
            +
             | 
| 530 562 | 
             
                  if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first)
         | 
| 531 563 | 
             
                    puts "Now switching back to \"#{orig_schema}\" schema."
         | 
| 532 564 | 
             
                    ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", orig_schema)
         | 
    
        data/lib/brick/version_number.rb
    CHANGED
    
    
    
        data/lib/brick.rb
    CHANGED
    
    | @@ -111,7 +111,7 @@ module Brick | |
| 111 111 | 
             
                              :established_drf,
         | 
| 112 112 | 
             
                              :is_oracle, :is_eager_loading, :auto_models, :initializer_loaded,
         | 
| 113 113 | 
             
                              :table_name_lookup,
         | 
| 114 | 
            -
                              :elasticsearch_models,
         | 
| 114 | 
            +
                              :elasticsearch_models, :elasticsearch_existings,
         | 
| 115 115 | 
             
                              :routes_done
         | 
| 116 116 | 
             
                ::Brick.auto_models = []
         | 
| 117 117 |  | 
| @@ -292,6 +292,10 @@ to instead be this: | |
| 292 292 | 
             
                        s.first[bt_key] = [a.name, a.klass]
         | 
| 293 293 | 
             
                      end
         | 
| 294 294 | 
             
                    else # This gets all forms of has_many and has_one
         | 
| 295 | 
            +
                      if a.scope&.arity&.> 0
         | 
| 296 | 
            +
                        puts "Skipping HM column \"#{a.name}\" because it has a scope which requires #{a.scope.arity} #{'argument'.pluralize(a.scope.arity)}"
         | 
| 297 | 
            +
                        next
         | 
| 298 | 
            +
                      end
         | 
| 295 299 | 
             
                      if through # has_many :through or has_one :through
         | 
| 296 300 | 
             
                        is_invalid_source = nil
         | 
| 297 301 | 
             
                        begin
         |