Class: MetaSearch::Builder

Inherits:
Object
  • Object
show all
Includes:
ModelCompatibility, Utility
Defined in:
lib/meta_search/builder.rb

Overview

Builder is the workhorse of MetaSearch – it is the class that handles dynamically generating methods based on a supplied model, and is what gets instantiated when you call your model's search method. Builder doesn't generate any methods until they're needed, using method_missing to compare requested method names against your model's attributes, associations, and the configured Where list.

Attributes

  • base - The base model that Builder wraps.

  • search_attributes - Attributes that have been assigned (search terms)

  • relation - The ActiveRecord::Relation representing the current search.

  • join_dependency - The JoinDependency object representing current association join dependencies. It's used internally to avoid joining association tables more than once when constructing search queries.

Constant Summary

Constants included from Utility

Utility::FALSE_VALUES, Utility::TRUE_VALUES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ModelCompatibility

included, #persisted?, #to_key, #to_model, #to_param

Constructor Details

#initialize(base_or_relation, opts = {}) ⇒ Builder

Initialize a new Builder. Requires a base model to wrap, and supports a couple of options for how it will expose this model and its associations to your controllers/views.



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/meta_search/builder.rb', line 31

def initialize(base_or_relation, opts = {})
  opts = opts.dup
  @relation = base_or_relation.scoped
  @base = @relation.klass
  @search_key = (opts.delete(:search_key) || 'search').to_s
  @options = opts  # Let's just hang on to other options for use in authorization blocks
  @join_type = opts[:join_type] ||  Arel::Nodes::OuterJoin
  @join_type = get_join_type(@join_type)
  @join_dependency = build_join_dependency(@relation)
  @search_attributes = {}
  @errors = ActiveModel::Errors.new(self)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *args, &block) ⇒ Object (private)



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/meta_search/builder.rb', line 122

def method_missing(method_id, *args, &block)
  method_name = method_id.to_s
  if method_name =~ /^meta_sort=?$/
    (args.any? || method_name =~ /=$/) ? set_sort(args.first) : get_sort
  elsif match = method_name.match(/^(.*)\(([0-9]+).*\)$/) # Multiparameter reader
    method_name, index = match.captures
    vals = self.send(method_name)
    vals.is_a?(Array) ? vals[index.to_i - 1] : nil
  elsif match = matches_named_method(method_name)
    (args.any? || method_name =~ /=$/) ? set_named_method_value(match, args.first) : get_named_method_value(match)
  elsif match = matches_attribute_method(method_id)
    attribute, predicate = match.captures
     (args.any? || method_name =~ /=$/) ? set_attribute_method_value(attribute, predicate, args.first) : get_attribute_method_value(attribute, predicate)
  else
    super
  end
end

Instance Attribute Details

#baseObject (readonly)

Returns the value of attribute base



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def base
  @base
end

#errorsObject (readonly)

Returns the value of attribute errors



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def errors
  @errors
end

#join_dependencyObject (readonly)

Returns the value of attribute join_dependency



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def join_dependency
  @join_dependency
end

#optionsObject (readonly)

Returns the value of attribute options



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def options
  @options
end

#relationObject (readonly)

Returns the value of attribute relation



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def relation
  @relation
end

#search_attributesObject (readonly)

Returns the value of attribute search_attributes



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def search_attributes
  @search_attributes
end

#search_keyObject (readonly)

Returns the value of attribute search_key



26
27
28
# File 'lib/meta_search/builder.rb', line 26

def search_key
  @search_key
end

Instance Method Details

#build(option_hash) ⇒ Object

Build the search with the given search options. Options are in the form of a hash with keys matching the names creted by the Builder's “wheres” as outlined in MetaSearch::Where



83
84
85
86
87
88
89
90
# File 'lib/meta_search/builder.rb', line 83

def build(option_hash)
  opts = option_hash.dup || {}
  @relation = @base.scoped
  opts.stringify_keys!
  opts = collapse_multiparameter_options(opts)
  assign_attributes(opts)
  self
end

#get_association(assoc, base = @base) ⇒ Object



48
49
50
# File 'lib/meta_search/builder.rb', line 48

def get_association(assoc, base = @base)
  base.reflect_on_association(assoc.to_sym) if base._metasearch_association_authorized?(assoc, self)
end

#get_attribute(name, parent = @join_dependency.join_base) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/meta_search/builder.rb', line 52

def get_attribute(name, parent = @join_dependency.join_base)
  attribute = nil
  if get_column(name, parent.active_record)
    attribute = parent.table[name]
  elsif (segments = name.to_s.split(/_/)).size > 1
    remainder = []
    found_assoc = nil
    while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
      if found_assoc = get_association(segments.join('_'), parent.active_record)
        if found_assoc.options[:polymorphic]
          unless delimiter = remainder.index('type')
            raise PolymorphicAssociationMissingTypeError, "Polymorphic association specified without a type"
          end
          polymorphic_class, attribute_name = remainder[0...delimiter].join('_'),
                                              remainder[delimiter + 1...remainder.size].join('_')
          polymorphic_class = polymorphic_class.classify.constantize
          join = build_or_find_association(found_assoc.name, parent, polymorphic_class)
          attribute = get_attribute(attribute_name, join)
        else
          join = build_or_find_association(found_assoc.name, parent, found_assoc.klass)
          attribute = get_attribute(remainder.join('_'), join)
        end
      end
    end
  end
  attribute
end

#get_column(column, base = @base) ⇒ Object



44
45
46
# File 'lib/meta_search/builder.rb', line 44

def get_column(column, base = @base)
  base.columns_hash[column.to_s] if base._metasearch_attribute_authorized?(column, self)
end

#respond_to?(method_id, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/meta_search/builder.rb', line 92

def respond_to?(method_id, include_private = false)
  return true if super

  method_name = method_id.to_s
  if RELATION_METHODS.map(&:to_s).include?(method_name)
    true
  elsif method_name.match(/^meta_sort=?$/)
    true
  elsif match = method_name.match(/^(.*)\(([0-9]+).*\)$/)
    method_name, index = match.captures
    respond_to?(method_name)
  elsif matches_named_method(method_name) || matches_attribute_method(method_name)
    true
  else
    false
  end
end