squeel 1.1.1 → 1.2.1
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 +7 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.travis.yml +36 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/README.md +47 -6
- data/Rakefile +14 -2
- data/lib/squeel.rb +9 -1
- data/lib/squeel/adapters/active_record.rb +0 -1
- data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
- data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
- data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
- data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
- data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
- data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
- data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
- data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
- data/lib/squeel/adapters/active_record/context.rb +7 -7
- data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
- data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
- data/lib/squeel/core_ext/symbol.rb +3 -3
- data/lib/squeel/dsl.rb +1 -1
- data/lib/squeel/nodes.rb +1 -0
- data/lib/squeel/nodes/as.rb +12 -0
- data/lib/squeel/nodes/join.rb +8 -4
- data/lib/squeel/nodes/key_path.rb +10 -1
- data/lib/squeel/nodes/node.rb +21 -0
- data/lib/squeel/nodes/stub.rb +8 -4
- data/lib/squeel/nodes/subquery_join.rb +44 -0
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors.rb +2 -0
- data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
- data/lib/squeel/visitors/order_visitor.rb +9 -2
- data/lib/squeel/visitors/predicate_visitor.rb +11 -0
- data/lib/squeel/visitors/preload_visitor.rb +12 -0
- data/lib/squeel/visitors/visitor.rb +89 -13
- data/spec/config.travis.yml +13 -0
- data/spec/config.yml +12 -0
- data/spec/console.rb +3 -12
- data/spec/core_ext/symbol_spec.rb +3 -3
- data/spec/helpers/squeel_helper.rb +8 -5
- data/spec/spec_helper.rb +4 -16
- data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
- data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
- data/spec/squeel/core_ext/symbol_spec.rb +3 -3
- data/spec/squeel/nodes/join_spec.rb +4 -4
- data/spec/squeel/nodes/stub_spec.rb +3 -3
- data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
- data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
- data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
- data/spec/squeel/visitors/visitor_spec.rb +7 -6
- data/spec/support/models.rb +99 -15
- data/spec/support/schema.rb +109 -4
- data/squeel.gemspec +8 -6
- metadata +89 -107
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/spec/blueprints/articles.rb +0 -5
- data/spec/blueprints/comments.rb +0 -5
- data/spec/blueprints/notes.rb +0 -3
- data/spec/blueprints/people.rb +0 -4
- data/spec/blueprints/tags.rb +0 -3
| @@ -3,6 +3,12 @@ require 'squeel/context' | |
| 3 3 | 
             
            module Squeel
         | 
| 4 4 | 
             
              module Adapters
         | 
| 5 5 | 
             
                module ActiveRecord
         | 
| 6 | 
            +
                  JoinPart = if defined?(::ActiveRecord::Associations::JoinDependency::JoinPart)
         | 
| 7 | 
            +
                      ::ActiveRecord::Associations::JoinDependency::JoinPart
         | 
| 8 | 
            +
                    elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase)
         | 
| 9 | 
            +
                      ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 6 12 | 
             
                  class Context < ::Squeel::Context
         | 
| 7 13 |  | 
| 8 14 | 
             
                    def initialize(object)
         | 
| @@ -23,7 +29,7 @@ module Squeel | |
| 23 29 | 
             
                          }
         | 
| 24 30 | 
             
                        when Nodes::Join
         | 
| 25 31 | 
             
                          @object.join_associations.detect { |j|
         | 
| 26 | 
            -
                            j.reflection.name == object. | 
| 32 | 
            +
                            j.reflection.name == object._name && j.parent == parent &&
         | 
| 27 33 | 
             
                            (object.polymorphic? ? j.reflection.klass == object._klass : true)
         | 
| 28 34 | 
             
                          }
         | 
| 29 35 | 
             
                        else
         | 
| @@ -73,12 +79,6 @@ module Squeel | |
| 73 79 | 
             
                    end
         | 
| 74 80 |  | 
| 75 81 | 
             
                  end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                  if defined?(::ActiveRecord::Associations::JoinDependency::JoinPart)
         | 
| 78 | 
            -
                    JoinPart = ::ActiveRecord::Associations::JoinDependency::JoinPart
         | 
| 79 | 
            -
                  elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase)
         | 
| 80 | 
            -
                    JoinPart = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
         | 
| 81 | 
            -
                  end
         | 
| 82 82 | 
             
                end
         | 
| 83 83 | 
             
              end
         | 
| 84 84 | 
             
            end
         | 
| @@ -1,17 +1,22 @@ | |
| 1 | 
            -
            require 'polyamorous'
         | 
| 2 | 
            -
             | 
| 3 1 | 
             
            module Squeel
         | 
| 4 2 | 
             
              module Adapters
         | 
| 5 3 | 
             
                module ActiveRecord
         | 
| 6 | 
            -
                   | 
| 4 | 
            +
                  if defined?(::ActiveRecord::Associations::JoinDependency)
         | 
| 5 | 
            +
                    JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
         | 
| 6 | 
            +
                    JoinDependency = ::ActiveRecord::Associations::JoinDependency
         | 
| 7 | 
            +
                  elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency)
         | 
| 8 | 
            +
                    JoinAssociation = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
         | 
| 9 | 
            +
                    JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
         | 
| 10 | 
            +
                  end
         | 
| 7 11 |  | 
| 12 | 
            +
                  module JoinDependencyExtensions
         | 
| 8 13 | 
             
                    def self.included(base)
         | 
| 9 14 | 
             
                      base.class_eval do
         | 
| 10 15 | 
             
                        alias_method_chain :build, :squeel
         | 
| 11 16 | 
             
                      end
         | 
| 12 17 | 
             
                    end
         | 
| 13 18 |  | 
| 14 | 
            -
                    def build_with_squeel(associations, parent = nil, join_type =  | 
| 19 | 
            +
                    def build_with_squeel(associations, parent = nil, join_type = InnerJoin)
         | 
| 15 20 | 
             
                      case associations
         | 
| 16 21 | 
             
                      when Nodes::Stub
         | 
| 17 22 | 
             
                        associations = associations.symbol
         | 
| @@ -29,15 +34,6 @@ module Squeel | |
| 29 34 | 
             
                        build_without_squeel(associations, parent, join_type)
         | 
| 30 35 | 
             
                      end
         | 
| 31 36 | 
             
                    end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                  if defined?(::ActiveRecord::Associations::JoinDependency)
         | 
| 36 | 
            -
                    JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
         | 
| 37 | 
            -
                    JoinDependency = ::ActiveRecord::Associations::JoinDependency
         | 
| 38 | 
            -
                  elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency)
         | 
| 39 | 
            -
                    JoinAssociation = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
         | 
| 40 | 
            -
                    JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
         | 
| 41 37 | 
             
                  end
         | 
| 42 38 |  | 
| 43 39 | 
             
                  JoinDependency.send :include, Adapters::ActiveRecord::JoinDependencyExtensions
         | 
| @@ -45,7 +45,6 @@ module Squeel | |
| 45 45 | 
             
                          merge_resolving_duplicate_squeel_equalities(r)
         | 
| 46 46 | 
             
                        end
         | 
| 47 47 | 
             
                      else
         | 
| 48 | 
            -
                        puts r.inspect if r.is_a?(Proc)
         | 
| 49 48 | 
             
                        super(r)
         | 
| 50 49 | 
             
                      end
         | 
| 51 50 | 
             
                    end
         | 
| @@ -115,6 +114,8 @@ module Squeel | |
| 115 114 | 
             
                          :stashed_join
         | 
| 116 115 | 
             
                        when Arel::Nodes::Join
         | 
| 117 116 | 
             
                          :join_node
         | 
| 117 | 
            +
                        when Nodes::SubqueryJoin
         | 
| 118 | 
            +
                          :subquery_join
         | 
| 118 119 | 
             
                        else
         | 
| 119 120 | 
             
                          raise 'unknown class: %s' % join.class.name
         | 
| 120 121 | 
             
                        end
         | 
| @@ -123,6 +124,7 @@ module Squeel | |
| 123 124 | 
             
                      association_joins         = buckets[:association_join] || []
         | 
| 124 125 | 
             
                      stashed_association_joins = buckets[:stashed_join] || []
         | 
| 125 126 | 
             
                      join_nodes                = (buckets[:join_node] || []).uniq
         | 
| 127 | 
            +
                      subquery_joins            = (buckets[:subquery_join] || []).uniq
         | 
| 126 128 | 
             
                      string_joins              = (buckets[:string_join] || []).map { |x|
         | 
| 127 129 | 
             
                        x.strip
         | 
| 128 130 | 
             
                      }.uniq
         | 
| @@ -144,11 +146,12 @@ module Squeel | |
| 144 146 | 
             
                        association.join_to(manager)
         | 
| 145 147 | 
             
                      end
         | 
| 146 148 |  | 
| 147 | 
            -
                      manager.join_sources.concat | 
| 149 | 
            +
                      manager.join_sources.concat(join_list)
         | 
| 150 | 
            +
                      manager.join_sources.concat(build_join_from_subquery(subquery_joins))
         | 
| 148 151 |  | 
| 149 152 | 
             
                      manager
         | 
| 150 153 | 
             
                    end
         | 
| 151 | 
            -
                    # For 4. | 
| 154 | 
            +
                    # For 4.0 adapters
         | 
| 152 155 | 
             
                    alias :build_joins :build_join_dependency
         | 
| 153 156 |  | 
| 154 157 | 
             
                    def includes(*args)
         | 
| @@ -279,12 +282,12 @@ module Squeel | |
| 279 282 | 
             
                      end
         | 
| 280 283 | 
             
                    end
         | 
| 281 284 |  | 
| 282 | 
            -
                    def find_equality_predicates(nodes)
         | 
| 285 | 
            +
                    def find_equality_predicates(nodes, relation_table_name = table_name)
         | 
| 283 286 | 
             
                      nodes.map { |node|
         | 
| 284 287 | 
             
                        case node
         | 
| 285 288 | 
             
                        when Arel::Nodes::Equality
         | 
| 286 289 | 
             
                          if node.left.respond_to?(:relation) &&
         | 
| 287 | 
            -
                            node.left.relation.name ==  | 
| 290 | 
            +
                            node.left.relation.name == relation_table_name
         | 
| 288 291 | 
             
                            node
         | 
| 289 292 | 
             
                          end
         | 
| 290 293 | 
             
                        when Arel::Nodes::Grouping
         | 
| @@ -395,9 +398,8 @@ module Squeel | |
| 395 398 | 
             
                    # your model's default scope. We hijack it in order to dig down into
         | 
| 396 399 | 
             
                    # And and Grouping nodes, which are equivalent to seeing top-level
         | 
| 397 400 | 
             
                    # Equality nodes in stock AR terms.
         | 
| 398 | 
            -
                    def where_values_hash_with_squeel
         | 
| 399 | 
            -
                      equalities = find_equality_predicates(where_visit(with_default_scope.where_values))
         | 
| 400 | 
            -
             | 
| 401 | 
            +
                    def where_values_hash_with_squeel(relation_table_name = table_name)
         | 
| 402 | 
            +
                      equalities = find_equality_predicates(where_visit(with_default_scope.where_values), relation_table_name)
         | 
| 401 403 | 
             
                      binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
         | 
| 402 404 |  | 
| 403 405 | 
             
                      Hash[equalities.map { |where|
         | 
| @@ -406,6 +408,34 @@ module Squeel | |
| 406 408 | 
             
                      }]
         | 
| 407 409 | 
             
                    end
         | 
| 408 410 |  | 
| 411 | 
            +
                    def build_join_from_subquery(subquery_joins)
         | 
| 412 | 
            +
                      subquery_joins.map do |join|
         | 
| 413 | 
            +
                        join.type.new(
         | 
| 414 | 
            +
                          Arel::Nodes::TableAlias.new(
         | 
| 415 | 
            +
                            Arel::Nodes::Grouping.new(join.subquery.left.arel.ast),
         | 
| 416 | 
            +
                            join.subquery.right),
         | 
| 417 | 
            +
                          Arel::Nodes::On.new(where_visit(join.constraints))
         | 
| 418 | 
            +
                        )
         | 
| 419 | 
            +
                      end
         | 
| 420 | 
            +
                    end
         | 
| 421 | 
            +
             | 
| 422 | 
            +
                    def preprocess_attrs_with_ar(attributes)
         | 
| 423 | 
            +
                      attributes.map do |key, value|
         | 
| 424 | 
            +
                        case key
         | 
| 425 | 
            +
                          when Squeel::Nodes::Node
         | 
| 426 | 
            +
                            {key => value}
         | 
| 427 | 
            +
                          when Symbol
         | 
| 428 | 
            +
                            if value.is_a?(Hash)
         | 
| 429 | 
            +
                              {key => value}
         | 
| 430 | 
            +
                            else
         | 
| 431 | 
            +
                              ::ActiveRecord::PredicateBuilder.build_from_hash(klass, {key => value}, table)
         | 
| 432 | 
            +
                            end
         | 
| 433 | 
            +
                          else
         | 
| 434 | 
            +
                            ::ActiveRecord::PredicateBuilder.build_from_hash(klass, {key => value}, table)
         | 
| 435 | 
            +
                          end
         | 
| 436 | 
            +
                      end
         | 
| 437 | 
            +
                    end
         | 
| 438 | 
            +
             | 
| 409 439 | 
             
                  end
         | 
| 410 440 | 
             
                end
         | 
| 411 441 | 
             
              end
         | 
| @@ -15,15 +15,15 @@ class Symbol | |
| 15 15 | 
             
              end
         | 
| 16 16 |  | 
| 17 17 | 
             
              def inner
         | 
| 18 | 
            -
                Squeel::Nodes::Join.new(self,  | 
| 18 | 
            +
                Squeel::Nodes::Join.new(self, Squeel::InnerJoin)
         | 
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 21 | 
             
              def outer
         | 
| 22 | 
            -
                Squeel::Nodes::Join.new(self,  | 
| 22 | 
            +
                Squeel::Nodes::Join.new(self, Squeel::OuterJoin)
         | 
| 23 23 | 
             
              end
         | 
| 24 24 |  | 
| 25 25 | 
             
              def of_class(klass)
         | 
| 26 | 
            -
                Squeel::Nodes::Join.new(self,  | 
| 26 | 
            +
                Squeel::Nodes::Join.new(self, Squeel::InnerJoin, klass)
         | 
| 27 27 | 
             
              end
         | 
| 28 28 |  | 
| 29 29 | 
             
            end
         | 
    
        data/lib/squeel/dsl.rb
    CHANGED
    
    | @@ -110,7 +110,7 @@ module Squeel | |
| 110 110 | 
             
                  if args.empty?
         | 
| 111 111 | 
             
                    Nodes::Stub.new method_id
         | 
| 112 112 | 
             
                  elsif (args.size == 1) && (Class === args[0])
         | 
| 113 | 
            -
                    Nodes::Join.new(method_id,  | 
| 113 | 
            +
                    Nodes::Join.new(method_id, InnerJoin, args[0])
         | 
| 114 114 | 
             
                  else
         | 
| 115 115 | 
             
                    Nodes::Function.new method_id, args
         | 
| 116 116 | 
             
                  end
         | 
    
        data/lib/squeel/nodes.rb
    CHANGED
    
    
    
        data/lib/squeel/nodes/as.rb
    CHANGED
    
    | @@ -12,6 +12,18 @@ module Squeel | |
| 12 12 | 
             
                  def initialize(left, right)
         | 
| 13 13 | 
             
                    @left, @right = left, right
         | 
| 14 14 | 
             
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def on(*args)
         | 
| 17 | 
            +
                    raise "only can convert ActiveRecord::Relation to a join node" unless left.is_a?(ActiveRecord::Relation)
         | 
| 18 | 
            +
                    proc =
         | 
| 19 | 
            +
                      if block_given?
         | 
| 20 | 
            +
                        DSL.eval(&Proc.new)
         | 
| 21 | 
            +
                      else
         | 
| 22 | 
            +
                        args
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    SubqueryJoin.new(self, proc)
         | 
| 26 | 
            +
                  end
         | 
| 15 27 | 
             
                end
         | 
| 16 28 | 
             
              end
         | 
| 17 29 | 
             
            end
         | 
    
        data/lib/squeel/nodes/join.rb
    CHANGED
    
    | @@ -14,21 +14,21 @@ module Squeel | |
| 14 14 | 
             
                  # @param [Symbol] name The association name
         | 
| 15 15 | 
             
                  # @param [Arel::InnerJoin, Arel::OuterJoin] type The Arel join class
         | 
| 16 16 | 
             
                  # @param [Class, String, Symbol] klass The polymorphic belongs_to class or class name
         | 
| 17 | 
            -
                  def initialize(name, type =  | 
| 17 | 
            +
                  def initialize(name, type = InnerJoin, klass = nil)
         | 
| 18 18 | 
             
                    @_join = Polyamorous::Join.new(name, type, klass)
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 21 | 
             
                  # Set the join type to an inner join
         | 
| 22 22 | 
             
                  # @return [Join] The join, with an updated join type.
         | 
| 23 23 | 
             
                  def inner
         | 
| 24 | 
            -
                    self._type =  | 
| 24 | 
            +
                    self._type = InnerJoin
         | 
| 25 25 | 
             
                    self
         | 
| 26 26 | 
             
                  end
         | 
| 27 27 |  | 
| 28 28 | 
             
                  # Set the join type to an outer join
         | 
| 29 29 | 
             
                  # @return [Join] The join, with an updated join type.
         | 
| 30 30 | 
             
                  def outer
         | 
| 31 | 
            -
                    self._type =  | 
| 31 | 
            +
                    self._type = OuterJoin
         | 
| 32 32 | 
             
                    self
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 |  | 
| @@ -61,7 +61,7 @@ module Squeel | |
| 61 61 | 
             
                  def method_missing(method_id, *args)
         | 
| 62 62 | 
             
                    super if method_id == :to_ary
         | 
| 63 63 | 
             
                    if (args.size == 1) && (Class === args[0])
         | 
| 64 | 
            -
                      KeyPath.new([self, Join.new(method_id,  | 
| 64 | 
            +
                      KeyPath.new([self, Join.new(method_id, InnerJoin, args[0])])
         | 
| 65 65 | 
             
                    else
         | 
| 66 66 | 
             
                      KeyPath.new([self, method_id])
         | 
| 67 67 | 
             
                    end
         | 
| @@ -83,6 +83,10 @@ module Squeel | |
| 83 83 | 
             
                    nil
         | 
| 84 84 | 
             
                  end
         | 
| 85 85 |  | 
| 86 | 
            +
                  def add_to_tree(hash)
         | 
| 87 | 
            +
                    hash[_join] ||= {}
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 86 90 | 
             
                end
         | 
| 87 91 | 
             
              end
         | 
| 88 92 | 
             
            end
         | 
| @@ -177,6 +177,10 @@ module Squeel | |
| 177 177 | 
             
                  end
         | 
| 178 178 | 
             
                  alias :to_str :to_s
         | 
| 179 179 |  | 
| 180 | 
            +
                  def add_to_tree(hash)
         | 
| 181 | 
            +
                    walk_through_path(path.dup, hash)
         | 
| 182 | 
            +
                  end
         | 
| 183 | 
            +
             | 
| 180 184 | 
             
                  # Appends to the KeyPath or delegates to the endpoint, as appropriate
         | 
| 181 185 | 
             
                  # @return [KeyPath] The updated KeyPath
         | 
| 182 186 | 
             
                  def method_missing(method_id, *args, &block)
         | 
| @@ -194,7 +198,7 @@ module Squeel | |
| 194 198 | 
             
                      if args.empty?
         | 
| 195 199 | 
             
                        @path << Stub.new(method_id)
         | 
| 196 200 | 
             
                      elsif (args.size == 1) && (Class === args[0])
         | 
| 197 | 
            -
                        @path << Join.new(method_id,  | 
| 201 | 
            +
                        @path << Join.new(method_id, InnerJoin, args[0])
         | 
| 198 202 | 
             
                      else
         | 
| 199 203 | 
             
                        @path << Nodes::Function.new(method_id, args)
         | 
| 200 204 | 
             
                      end
         | 
| @@ -213,6 +217,11 @@ module Squeel | |
| 213 217 | 
             
                    @path = @path.dup
         | 
| 214 218 | 
             
                  end
         | 
| 215 219 |  | 
| 220 | 
            +
                  def walk_through_path(path, hash)
         | 
| 221 | 
            +
                    cache = path.shift.add_to_tree(hash)
         | 
| 222 | 
            +
                    path.empty? ? cache : walk_through_path(path, cache)
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
             | 
| 216 225 | 
             
                  # Raises a NoMethodError manually, bypassing #method_missing.
         | 
| 217 226 | 
             
                  # Used by special-case operator overrides.
         | 
| 218 227 | 
             
                  def no_method_error(method_id)
         | 
    
        data/lib/squeel/nodes/node.rb
    CHANGED
    
    | @@ -1,6 +1,27 @@ | |
| 1 | 
            +
            require 'polyamorous/tree_node'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Squeel
         | 
| 2 4 | 
             
              module Nodes
         | 
| 3 5 | 
             
                class Node
         | 
| 6 | 
            +
                  include ::Polyamorous::TreeNode
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def each(&block)
         | 
| 9 | 
            +
                    return enum_for(:each) unless block_given?
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    Visitors::EnumerationVisitor.new(block).accept(self)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # We don't want the full Enumerable method list, because it will mess
         | 
| 15 | 
            +
                  # with stuff like KeyPath
         | 
| 16 | 
            +
                  def grep(object, &block)
         | 
| 17 | 
            +
                    if block_given?
         | 
| 18 | 
            +
                      each { |value| yield value if object === value }
         | 
| 19 | 
            +
                    else
         | 
| 20 | 
            +
                      [].tap do |results|
         | 
| 21 | 
            +
                        each { |value| results << value if object === value }
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 4 25 | 
             
                end
         | 
| 5 26 | 
             
              end
         | 
| 6 27 | 
             
            end
         | 
    
        data/lib/squeel/nodes/stub.rb
    CHANGED
    
    | @@ -79,7 +79,7 @@ module Squeel | |
| 79 79 | 
             
                    if args.empty?
         | 
| 80 80 | 
             
                      KeyPath.new([self, method_id])
         | 
| 81 81 | 
             
                    elsif (args.size == 1) && (Class === args[0])
         | 
| 82 | 
            -
                      KeyPath.new([self, Join.new(method_id,  | 
| 82 | 
            +
                      KeyPath.new([self, Join.new(method_id, InnerJoin, args[0])])
         | 
| 83 83 | 
             
                    else
         | 
| 84 84 | 
             
                      KeyPath.new([self, Nodes::Function.new(method_id, args)])
         | 
| 85 85 | 
             
                    end
         | 
| @@ -102,7 +102,7 @@ module Squeel | |
| 102 102 | 
             
                  # Create an inner Join node for the association named by this Stub
         | 
| 103 103 | 
             
                  # @return [Join] The new inner Join node
         | 
| 104 104 | 
             
                  def inner
         | 
| 105 | 
            -
                    Join.new(self.symbol,  | 
| 105 | 
            +
                    Join.new(self.symbol, InnerJoin)
         | 
| 106 106 | 
             
                  end
         | 
| 107 107 |  | 
| 108 108 | 
             
                  # Create a keypath with a sifter as its endpoint
         | 
| @@ -114,14 +114,18 @@ module Squeel | |
| 114 114 | 
             
                  # Create an outer Join node for the association named by this Stub
         | 
| 115 115 | 
             
                  # @return [Join] The new outer Join node
         | 
| 116 116 | 
             
                  def outer
         | 
| 117 | 
            -
                    Join.new(self.symbol,  | 
| 117 | 
            +
                    Join.new(self.symbol, OuterJoin)
         | 
| 118 118 | 
             
                  end
         | 
| 119 119 |  | 
| 120 120 | 
             
                  # Create a polymorphic Join node for the association named by this Stub,
         | 
| 121 121 | 
             
                  # @param [Class] klass The polymorphic belongs_to class for this Join
         | 
| 122 122 | 
             
                  # @return [Join] The new polymorphic Join node
         | 
| 123 123 | 
             
                  def of_class(klass)
         | 
| 124 | 
            -
                    Join.new(self.symbol,  | 
| 124 | 
            +
                    Join.new(self.symbol, InnerJoin, klass)
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  def add_to_tree(hash)
         | 
| 128 | 
            +
                    hash[symbol] ||= {}
         | 
| 125 129 | 
             
                  end
         | 
| 126 130 |  | 
| 127 131 | 
             
                end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            module Squeel
         | 
| 2 | 
            +
              module Nodes
         | 
| 3 | 
            +
                class SubqueryJoin < Node
         | 
| 4 | 
            +
                  attr_accessor :subquery, :type, :constraints
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(subquery, constraints, type = Squeel::InnerJoin)
         | 
| 7 | 
            +
                    raise ArgumentError,
         | 
| 8 | 
            +
                      "subquery(#{subquery}) isn't an Squeel::Nodes::As" unless subquery.is_a?(As)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    raise ArgumentError,
         | 
| 11 | 
            +
                      "constraints(#{constraints}) isn't a Squeel::Nodes::Node" unless constraints.is_a?(Node)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    self.subquery = subquery
         | 
| 14 | 
            +
                    self.constraints = constraints
         | 
| 15 | 
            +
                    self.type = type
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Implemented for equality testing
         | 
| 19 | 
            +
                  def hash
         | 
| 20 | 
            +
                    [subquery, type, constraints].hash
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def inner
         | 
| 24 | 
            +
                    self.type = Squeel::InnerJoin
         | 
| 25 | 
            +
                    self
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def outer
         | 
| 29 | 
            +
                    self.type = Squeel::OuterJoin
         | 
| 30 | 
            +
                    self
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  # Compare with other objects
         | 
| 34 | 
            +
                  def eql?(other)
         | 
| 35 | 
            +
                    self.class.eql?(other.class) &&
         | 
| 36 | 
            +
                    self.subquery.eql?(other.subquery) &&
         | 
| 37 | 
            +
                    self.type.eql?(other.type) &&
         | 
| 38 | 
            +
                    self.constraints.eql?(other.constraints)
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                  alias :== :eql?
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
    
        data/lib/squeel/version.rb
    CHANGED
    
    
    
        data/lib/squeel/visitors.rb
    CHANGED
    
    
| @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            require 'active_support/core_ext/module'
         | 
| 2 | 
            +
            require 'squeel/nodes'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Squeel
         | 
| 5 | 
            +
              module Visitors
         | 
| 6 | 
            +
                # The Enumeration visitor class, used to implement Node#each
         | 
| 7 | 
            +
                class EnumerationVisitor
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # Create a new EnumerationVisitor.
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # @param [Proc] block The block to execute against each node.
         | 
| 12 | 
            +
                  def initialize(block = Proc.new)
         | 
| 13 | 
            +
                    @block = block
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # Accept an object.
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @param object The object to visit
         | 
| 19 | 
            +
                  # @return The results of the node visitation, which will be the last
         | 
| 20 | 
            +
                  #   call to the @block
         | 
| 21 | 
            +
                  def accept(object)
         | 
| 22 | 
            +
                    visit(object)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  private
         | 
| 26 | 
            +
                  # A hash that caches the method name to use for a visitor for a given
         | 
| 27 | 
            +
                  # class
         | 
| 28 | 
            +
                  DISPATCH = Hash.new do |hash, klass|
         | 
| 29 | 
            +
                    hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  # Visit the object.
         | 
| 33 | 
            +
                  #
         | 
| 34 | 
            +
                  # @param object The object to visit
         | 
| 35 | 
            +
                  def visit(object)
         | 
| 36 | 
            +
                    send(DISPATCH[object.class], object)
         | 
| 37 | 
            +
                    @block.call(object)
         | 
| 38 | 
            +
                  rescue NoMethodError => e
         | 
| 39 | 
            +
                    raise e if respond_to?(DISPATCH[object.class], true)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    superklass = object.class.ancestors.find { |klass|
         | 
| 42 | 
            +
                      respond_to?(DISPATCH[klass], true)
         | 
| 43 | 
            +
                    }
         | 
| 44 | 
            +
                    raise(TypeError, "Cannot visit #{object.class}") unless superklass
         | 
| 45 | 
            +
                    DISPATCH[object.class] = DISPATCH[superklass]
         | 
| 46 | 
            +
                    retry
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def visit_terminal(o)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                  alias :visit_Object :visit_terminal
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def visit_Array(o)
         | 
| 54 | 
            +
                    o.map { |v| visit(v) }
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def visit_Hash(o)
         | 
| 58 | 
            +
                    o.each { |k, v| visit(k); visit(v) }
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def visit_Squeel_Nodes_Nary(o)
         | 
| 62 | 
            +
                    visit(o.children)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def visit_Squeel_Nodes_Binary(o)
         | 
| 66 | 
            +
                    visit(o.left)
         | 
| 67 | 
            +
                    visit(o.right)
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  def visit_Squeel_Nodes_Unary(o)
         | 
| 71 | 
            +
                    visit(o.expr)
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def visit_Squeel_Nodes_Order(o)
         | 
| 75 | 
            +
                    visit(o.expr)
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def visit_Squeel_Nodes_Function(o)
         | 
| 79 | 
            +
                    visit(o.args)
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def visit_Squeel_Nodes_Predicate(o)
         | 
| 83 | 
            +
                    visit(o.expr)
         | 
| 84 | 
            +
                    visit(o.value)
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def visit_Squeel_Nodes_KeyPath(o)
         | 
| 88 | 
            +
                    visit(o.path)
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  def visit_Squeel_Nodes_Join(o)
         | 
| 92 | 
            +
                    visit(o._join)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def visit_Squeel_Nodes_Literal(o)
         | 
| 96 | 
            +
                    visit(o.expr)
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         |