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
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 3d3196690094de459e3e30179f68ce63ee2032e1
         | 
| 4 | 
            +
              data.tar.gz: 827a3e061d72e29cac9b615cbc3fb66c6c1e3462
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: fcf69aceda5afe483dfad4a91d40b21d8472b250c5a9eb719afaa6ccc1252d9488d176c0cc9d41d7a68e18a66f0dd5ff33dbcf4d77b59fd12c673e311cae680c
         | 
| 7 | 
            +
              data.tar.gz: 6a934cabda1f179d9a920cf4974fe8776b68d4a58578ee2c067ae4f1e7fac78a0989268920e711845d78367d66521c297e0cb8215e5d8fe85b336d275ae0d249
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rspec
    ADDED
    
    
    
        data/.travis.yml
    CHANGED
    
    | @@ -1,8 +1,40 @@ | |
| 1 | 
            +
            before_script:
         | 
| 2 | 
            +
              - mysql -e "create database squeel_test_examples;"
         | 
| 3 | 
            +
              - psql -c "create database squeel_test_examples;" -U postgres
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            script:
         | 
| 6 | 
            +
              - bundle exec rake test:$ADAPTER
         | 
| 7 | 
            +
             | 
| 1 8 | 
             
            rvm:
         | 
| 2 9 | 
             
              - 1.9.3
         | 
| 10 | 
            +
              - 2.0.0
         | 
| 11 | 
            +
              - 2.1.1
         | 
| 12 | 
            +
              - 2.1.2
         | 
| 3 13 |  | 
| 4 14 | 
             
            env:
         | 
| 5 | 
            -
               | 
| 6 | 
            -
             | 
| 7 | 
            -
               | 
| 8 | 
            -
             | 
| 15 | 
            +
              global:
         | 
| 16 | 
            +
                - SQ_CONFIG_FILE=$TRAVIS_BUILD_DIR/spec/config.travis.yml
         | 
| 17 | 
            +
              matrix:
         | 
| 18 | 
            +
                - RAILS=master AREL=master ADAPTER=sqlite3
         | 
| 19 | 
            +
                - RAILS=4-1-stable AREL=5-0-stable ADAPTER=sqlite3
         | 
| 20 | 
            +
                - RAILS=4-0-stable AREL=4-0-stable ADAPTER=sqlite3
         | 
| 21 | 
            +
                - RAILS=3-2-stable AREL=3-0-stable ADAPTER=sqlite3
         | 
| 22 | 
            +
                - RAILS=3-1-stable AREL=2-2-stable ADAPTER=sqlite3
         | 
| 23 | 
            +
                - RAILS=3-0-stable AREL=2-0-stable ADAPTER=sqlite3
         | 
| 24 | 
            +
                - RAILS=master AREL=master ADAPTER=mysql
         | 
| 25 | 
            +
                - RAILS=4-1-stable AREL=5-0-stable ADAPTER=mysql
         | 
| 26 | 
            +
                - RAILS=4-0-stable AREL=4-0-stable ADAPTER=mysql
         | 
| 27 | 
            +
                - RAILS=3-2-stable AREL=3-0-stable ADAPTER=mysql
         | 
| 28 | 
            +
                - RAILS=3-1-stable AREL=2-2-stable ADAPTER=mysql
         | 
| 29 | 
            +
                - RAILS=3-0-stable AREL=2-0-stable ADAPTER=mysql
         | 
| 30 | 
            +
                - RAILS=master AREL=master ADAPTER=mysql2
         | 
| 31 | 
            +
                - RAILS=4-1-stable AREL=5-0-stable ADAPTER=mysql2
         | 
| 32 | 
            +
                - RAILS=4-0-stable AREL=4-0-stable ADAPTER=mysql2
         | 
| 33 | 
            +
                - RAILS=3-2-stable AREL=3-0-stable ADAPTER=mysql2
         | 
| 34 | 
            +
                - RAILS=3-1-stable AREL=2-2-stable ADAPTER=mysql2
         | 
| 35 | 
            +
                - RAILS=master AREL=master ADAPTER=postgresql
         | 
| 36 | 
            +
                - RAILS=4-1-stable AREL=5-0-stable ADAPTER=postgresql
         | 
| 37 | 
            +
                - RAILS=4-0-stable AREL=4-0-stable ADAPTER=postgresql
         | 
| 38 | 
            +
                - RAILS=3-2-stable AREL=3-0-stable ADAPTER=postgresql
         | 
| 39 | 
            +
                - RAILS=3-1-stable AREL=2-2-stable ADAPTER=postgresql
         | 
| 40 | 
            +
                - RAILS=3-0-stable AREL=2-0-stable ADAPTER=postgresql
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,18 @@ | |
| 1 | 
            +
            ## 1.2.1 (Unreleased)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * Run all specs against sqlite, mysql and postgresql!
         | 
| 4 | 
            +
            * Genereta table names correctly when joining through an association. Fixes#302.
         | 
| 5 | 
            +
            * Enable Arel nodes in Squeel with "|" operator. Fixes #314.
         | 
| 6 | 
            +
            * Properly append binds from a relation used in a subquery. Fixes #272.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            ## 1.2.0 (2014-07-16)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            * Add compatibility to Ruby 2.0+ with Rails 4.1 and 4.2.0.alpha.
         | 
| 11 | 
            +
              Fixes #301, #305, #307
         | 
| 12 | 
            +
            * Enable using a relation as a subquery. Fixes #309
         | 
| 13 | 
            +
            * Bind params correctly in subquery using associations. Fixes #312
         | 
| 14 | 
            +
            * Use the correct attribute name when finding a Join node. Fixes #273.
         | 
| 15 | 
            +
             | 
| 1 16 | 
             
            ## 1.1.1 (2013-09-03)
         | 
| 2 17 |  | 
| 3 18 | 
             
            * Update relation extensions to support new count behavior in Active Record
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # Squeel [](http://travis-ci.org/activerecord-hackery/squeel) [](http://coderwall.com/ernie)
         | 
| 2 2 |  | 
| 3 3 | 
             
            Squeel lets you write your Active Record queries with fewer strings, and more Ruby,
         | 
| 4 4 | 
             
            by making the Arel awesomeness that lies beneath Active Record more accessible.
         | 
| @@ -23,8 +23,10 @@ just a simple example -- Squeel's capable of a whole lot more. Keep reading. | |
| 23 23 | 
             
            In your Gemfile:
         | 
| 24 24 |  | 
| 25 25 | 
             
            ```ruby
         | 
| 26 | 
            +
            # Make sure you are using the latest version of polyamorous
         | 
| 27 | 
            +
            gem "polyamorous", :git => "git://github.com/activerecord-hackery/polyamorous.git"
         | 
| 26 28 | 
             
            gem "squeel"  # Last officially released gem
         | 
| 27 | 
            -
            # gem "squeel", :git => "git://github.com/ | 
| 29 | 
            +
            # gem "squeel", :git => "git://github.com/activerecord-hackery/squeel.git" # Track git repo
         | 
| 28 30 | 
             
            ```
         | 
| 29 31 |  | 
| 30 32 | 
             
            Then bundle as usual.
         | 
| @@ -86,6 +88,24 @@ A Squeel keypath is essentially a more concise and readable alternative to a | |
| 86 88 | 
             
            deeply nested hash. For instance, in standard Active Record, you might join several
         | 
| 87 89 | 
             
            associations like this to perform a query:
         | 
| 88 90 |  | 
| 91 | 
            +
            #### Rails 4+
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            ```ruby
         | 
| 94 | 
            +
            Person.joins(:articles => {:comments => :person}).references(:all)
         | 
| 95 | 
            +
            # => SELECT "people".* FROM "people"
         | 
| 96 | 
            +
            #    LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id"
         | 
| 97 | 
            +
            #    LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id"
         | 
| 98 | 
            +
            #    LEFT OUTER JOIN "people" "people_comments" ON "people_comments"."id" = "comments"."person_id"
         | 
| 99 | 
            +
            ```
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            With a keypath, this would look like:
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            ```ruby
         | 
| 104 | 
            +
            Person.joins{articles.comments.person}.references(:all)
         | 
| 105 | 
            +
            ```
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            #### Rails 3.x
         | 
| 108 | 
            +
             | 
| 89 109 | 
             
            ```ruby
         | 
| 90 110 | 
             
            Person.joins(:articles => {:comments => :person})
         | 
| 91 111 | 
             
            # => SELECT "people".* FROM "people"
         | 
| @@ -413,6 +433,27 @@ Person.joins{children.parent.children}. | |
| 413 433 |  | 
| 414 434 | 
             
            Keypaths were used here for clarity, but nested hashes would work just as well.
         | 
| 415 435 |  | 
| 436 | 
            +
            You can also use a subquery in a join.
         | 
| 437 | 
            +
             | 
| 438 | 
            +
            Notice:
         | 
| 439 | 
            +
            1. Squeel can only accept an ActiveRecord::Relation class of subqueries in a join.
         | 
| 440 | 
            +
            2. Use the chain with caution. You should call `as` first to get a Nodes::As, then call `on` to get a join node.
         | 
| 441 | 
            +
             | 
| 442 | 
            +
            ```ruby
         | 
| 443 | 
            +
            subquery = OrderItem.group(:orderable_id).select { [orderable_id, sum(quantity * unit_price).as(amount)] }
         | 
| 444 | 
            +
            Seat.joins { [payment.outer, subquery.as('seat_order_items').on { id == seat_order_items.orderable_id}.outer] }.
         | 
| 445 | 
            +
                          select { [seat_order_items.amount, "seats.*"] }
         | 
| 446 | 
            +
            # => SELECT "seat_order_items"."amount", seats.*
         | 
| 447 | 
            +
            #    FROM "seats"
         | 
| 448 | 
            +
            #    LEFT OUTER JOIN "payments" ON "payments"."id" = "seats"."payment_id"
         | 
| 449 | 
            +
            #    LEFT OUTER JOIN (
         | 
| 450 | 
            +
            #      SELECT "order_items"."orderable_id",
         | 
| 451 | 
            +
            #             sum("order_items"."quantity" * "order_items"."unit_price") AS amount
         | 
| 452 | 
            +
            #      FROM "order_items"
         | 
| 453 | 
            +
            #      GROUP BY "order_items"."orderable_id"
         | 
| 454 | 
            +
            #    ) seat_order_items ON "seats"."id" = "seat_order_items"."orderable_id"
         | 
| 455 | 
            +
            ```
         | 
| 456 | 
            +
             | 
| 416 457 | 
             
            ### Functions
         | 
| 417 458 |  | 
| 418 459 | 
             
            You can call SQL functions just like you would call a method in Ruby...
         | 
| @@ -470,7 +511,7 @@ As you can see, just like functions, these operations can be given aliases. | |
| 470 511 | 
             
            To select more than one attribute (or calculated attribute) simply put them into an array:
         | 
| 471 512 |  | 
| 472 513 | 
             
            ```ruby
         | 
| 473 | 
            -
            p = Person.select{[ name.op('||', '-diddly').as(flanderized_name), | 
| 514 | 
            +
            p = Person.select{[ name.op('||', '-diddly').as(flanderized_name),
         | 
| 474 515 | 
             
                                coalesce(name, '<no name given>').as(name_with_default) ]}.first
         | 
| 475 516 | 
             
            p.flanderized_name
         | 
| 476 517 | 
             
            # => "Aric Smith-diddly"
         | 
| @@ -537,9 +578,9 @@ For further information, see | |
| 537 578 | 
             
            [this post to the Rails list](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-core/NQJJzZ7R7S0),
         | 
| 538 579 | 
             
            [this commit](https://github.com/lifo/docrails/commit/50c5005bafe7e43f81a141cd2c512379aec74325) to
         | 
| 539 580 | 
             
            the [Active Record guides](http://edgeguides.rubyonrails.org/active_record_querying.html#hash-conditions),
         | 
| 540 | 
            -
            [#67](https://github.com/ | 
| 541 | 
            -
            [#75](https://github.com/ | 
| 542 | 
            -
            [#171](https://github.com/ | 
| 581 | 
            +
            [#67](https://github.com/activerecord-hackery/squeel/issues/67),
         | 
| 582 | 
            +
            [#75](https://github.com/activerecord-hackery/squeel/issues/75), and
         | 
| 583 | 
            +
            [#171](https://github.com/activerecord-hackery/squeel/issues/171).
         | 
| 543 584 |  | 
| 544 585 | 
             
            ## Compatibility with MetaWhere
         | 
| 545 586 |  | 
    
        data/Rakefile
    CHANGED
    
    | @@ -4,10 +4,22 @@ require 'rspec/core/rake_task' | |
| 4 4 | 
             
            Bundler::GemHelper.install_tasks
         | 
| 5 5 |  | 
| 6 6 | 
             
            RSpec::Core::RakeTask.new(:spec) do |rspec|
         | 
| 7 | 
            -
              rspec.rspec_opts = ['--backtrace']
         | 
| 7 | 
            +
              rspec.rspec_opts = ['--backtrace', '--color', '--format documentation']
         | 
| 8 8 | 
             
            end
         | 
| 9 9 |  | 
| 10 | 
            -
            task :default =>  | 
| 10 | 
            +
            task :default => 'test_sqlite3'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            %w(sqlite3 mysql mysql2 postgresql).each do |adapter|
         | 
| 13 | 
            +
              namespace :test do
         | 
| 14 | 
            +
                task(adapter => ["#{adapter}:env", "spec"])
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              namespace adapter do
         | 
| 18 | 
            +
                task(:env) { ENV['SQ_DB'] = adapter }
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
         | 
| 22 | 
            +
            end
         | 
| 11 23 |  | 
| 12 24 | 
             
            desc "Open an irb session with Squeel and the sample data used in specs"
         | 
| 13 25 | 
             
            task :console do
         | 
    
        data/lib/squeel.rb
    CHANGED
    
    | @@ -1,7 +1,16 @@ | |
| 1 1 | 
             
            require 'squeel/configuration'
         | 
| 2 | 
            +
            require 'polyamorous'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Squeel
         | 
| 4 5 |  | 
| 6 | 
            +
              if defined?(Arel::InnerJoin)
         | 
| 7 | 
            +
                InnerJoin = Arel::InnerJoin
         | 
| 8 | 
            +
                OuterJoin = Arel::OuterJoin
         | 
| 9 | 
            +
              else
         | 
| 10 | 
            +
                InnerJoin = Arel::Nodes::InnerJoin
         | 
| 11 | 
            +
                OuterJoin = Arel::Nodes::OuterJoin
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 5 14 | 
             
              extend Configuration
         | 
| 6 15 |  | 
| 7 16 | 
             
              # Prevent warnings on the console when doing things some might describe as "evil"
         | 
| @@ -30,7 +39,6 @@ module Squeel | |
| 30 39 | 
             
                  alias_predicate aliaz, original
         | 
| 31 40 | 
             
                end
         | 
| 32 41 | 
             
              end
         | 
| 33 | 
            -
             | 
| 34 42 | 
             
            end
         | 
| 35 43 |  | 
| 36 44 | 
             
            require 'squeel/dsl'
         | 
| @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            case ActiveRecord::VERSION::MAJOR
         | 
| 2 2 | 
             
            when 3, 4
         | 
| 3 3 | 
             
              ActiveRecord::Relation.send :include, Squeel::Nodes::Aliasing
         | 
| 4 | 
            -
              require 'squeel/adapters/active_record/join_dependency_extensions'
         | 
| 5 4 | 
             
              require 'squeel/adapters/active_record/base_extensions'
         | 
| 6 5 |  | 
| 7 6 | 
             
              adapter_directory = "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/join_dependency_extensions'
         | 
| @@ -54,8 +54,9 @@ module Squeel | |
| 54 54 | 
             
                      end
         | 
| 55 55 |  | 
| 56 56 | 
             
                      stashed_association_joins = joins.grep(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
         | 
| 57 | 
            +
                      subquery_joins = joins.grep(Nodes::SubqueryJoin)
         | 
| 57 58 |  | 
| 58 | 
            -
                      non_association_joins = (joins - association_joins - stashed_association_joins)
         | 
| 59 | 
            +
                      non_association_joins = (joins - association_joins - stashed_association_joins - subquery_joins)
         | 
| 59 60 | 
             
                      custom_joins = custom_join_sql(*non_association_joins)
         | 
| 60 61 |  | 
| 61 62 | 
             
                      self.join_dependency = JoinDependency.new(@klass, association_joins, custom_joins)
         | 
| @@ -79,6 +80,16 @@ module Squeel | |
| 79 80 | 
             
                        relation = relation.join(left, join_type).on(*right)
         | 
| 80 81 | 
             
                      end
         | 
| 81 82 |  | 
| 83 | 
            +
                      subquery_joins.each do |join|
         | 
| 84 | 
            +
                        relation = relation.
         | 
| 85 | 
            +
                          join(
         | 
| 86 | 
            +
                            Arel::Nodes::TableAlias.new(
         | 
| 87 | 
            +
                              join.subquery.right,
         | 
| 88 | 
            +
                              Arel::Nodes::Grouping.new(join.subquery.left.arel.ast)),
         | 
| 89 | 
            +
                            join.type).
         | 
| 90 | 
            +
                          on(*where_visit(join.constraints))
         | 
| 91 | 
            +
                      end
         | 
| 92 | 
            +
             | 
| 82 93 | 
             
                      relation = relation.join(custom_joins)
         | 
| 83 94 | 
             
                    end
         | 
| 84 95 |  | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/join_dependency_extensions'
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/join_dependency_extensions'
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/join_dependency_extensions'
         | 
| @@ -30,6 +30,24 @@ module Squeel | |
| 30 30 | 
             
                      end
         | 
| 31 31 | 
             
                    end
         | 
| 32 32 |  | 
| 33 | 
            +
                    def where_unscoping(target_value)
         | 
| 34 | 
            +
                      target_value = target_value.to_s
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      where_values.reject! do |rel|
         | 
| 37 | 
            +
                        case rel
         | 
| 38 | 
            +
                        when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
         | 
| 39 | 
            +
                          subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
         | 
| 40 | 
            +
                          subrelation.name == target_value
         | 
| 41 | 
            +
                        when Hash
         | 
| 42 | 
            +
                          rel.stringify_keys.has_key?(target_value)
         | 
| 43 | 
            +
                        when Squeel::Nodes::Predicate
         | 
| 44 | 
            +
                          rel.expr.symbol.to_s == target_value
         | 
| 45 | 
            +
                        end
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                      bind_values.reject! { |col,_| col.name == target_value }
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 33 51 | 
             
                    def build_arel
         | 
| 34 52 | 
             
                      arel = Arel::SelectManager.new(table.engine, table)
         | 
| 35 53 |  | 
| @@ -52,9 +70,49 @@ module Squeel | |
| 52 70 | 
             
                      arel.from(build_from) if from_value
         | 
| 53 71 | 
             
                      arel.lock(lock_value) if lock_value
         | 
| 54 72 |  | 
| 73 | 
            +
                      # Reorder bind indexes when joins or subqueries include more bindings.
         | 
| 74 | 
            +
                      # Special for PostgreSQL
         | 
| 75 | 
            +
                      if bind_values.size > 1
         | 
| 76 | 
            +
                        bvs = bind_values
         | 
| 77 | 
            +
                        arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
         | 
| 78 | 
            +
                          column = bvs[i].first
         | 
| 79 | 
            +
                          bp.replace connection.substitute_at(column, i)
         | 
| 80 | 
            +
                        end
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
             | 
| 55 83 | 
             
                      arel
         | 
| 56 84 | 
             
                    end
         | 
| 57 85 |  | 
| 86 | 
            +
                    def build_where(opts, other = [])
         | 
| 87 | 
            +
                      case opts
         | 
| 88 | 
            +
                      when String, Array
         | 
| 89 | 
            +
                        super
         | 
| 90 | 
            +
                      else  # Let's prevent PredicateBuilder from doing its thing
         | 
| 91 | 
            +
                        [opts, *other].map do |arg|
         | 
| 92 | 
            +
                          case arg
         | 
| 93 | 
            +
                          when Array  # Just in case there's an array in there somewhere
         | 
| 94 | 
            +
                            @klass.send(:sanitize_sql, arg)
         | 
| 95 | 
            +
                          when Hash
         | 
| 96 | 
            +
                            attributes = @klass.send(:expand_hash_conditions_for_aggregates, arg)
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                            attributes.values.grep(::ActiveRecord::Relation) do |rel|
         | 
| 99 | 
            +
                              self.bind_values += rel.bind_values
         | 
| 100 | 
            +
                            end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                            preprocess_attrs_with_ar(attributes)
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                          when Squeel::Nodes::Node
         | 
| 105 | 
            +
                            arg.grep(::ActiveRecord::Relation) do |rel|
         | 
| 106 | 
            +
                              self.bind_values += rel.bind_values
         | 
| 107 | 
            +
                            end
         | 
| 108 | 
            +
                            arg
         | 
| 109 | 
            +
                          else
         | 
| 110 | 
            +
                            arg
         | 
| 111 | 
            +
                          end
         | 
| 112 | 
            +
                        end
         | 
| 113 | 
            +
                      end
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
             | 
| 58 116 | 
             
                    def build_from
         | 
| 59 117 | 
             
                      opts, name = from_visit(from_value)
         | 
| 60 118 | 
             
                      case opts
         | 
| @@ -109,6 +167,40 @@ module Squeel | |
| 109 167 | 
             
                      self
         | 
| 110 168 | 
             
                    end
         | 
| 111 169 |  | 
| 170 | 
            +
                    def where_values_hash_with_squeel(relation_table_name = table_name)
         | 
| 171 | 
            +
                      equalities = find_equality_predicates(where_visit(with_default_scope.where_values), relation_table_name)
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                      binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                      Hash[equalities.map { |where|
         | 
| 176 | 
            +
                        name = where.left.name
         | 
| 177 | 
            +
                        [name, binds.fetch(name.to_s) { where.right }]
         | 
| 178 | 
            +
                      }]
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                    def to_sql_with_binding_params
         | 
| 182 | 
            +
                      @to_sql ||= begin
         | 
| 183 | 
            +
                        relation   = self
         | 
| 184 | 
            +
                        connection = klass.connection
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                        if eager_loading?
         | 
| 187 | 
            +
                          find_with_associations { |rel| relation = rel }
         | 
| 188 | 
            +
                        end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                        ast   = relation.arel.ast
         | 
| 191 | 
            +
                        binds = relation.bind_values.dup
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                        visitor = connection.visitor.clone
         | 
| 194 | 
            +
                        visitor.class_eval do
         | 
| 195 | 
            +
                          include ::Arel::Visitors::BindVisitor
         | 
| 196 | 
            +
                        end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                        visitor.accept(ast) do
         | 
| 199 | 
            +
                          connection.quote(*binds.shift.reverse)
         | 
| 200 | 
            +
                        end
         | 
| 201 | 
            +
                      end
         | 
| 202 | 
            +
                    end
         | 
| 203 | 
            +
             | 
| 112 204 | 
             
                    private
         | 
| 113 205 |  | 
| 114 206 | 
             
                    def dehashified_order_values
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/compat'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveRecord
         | 
| 4 | 
            +
              module Associations
         | 
| 5 | 
            +
                class AssociationScope
         | 
| 6 | 
            +
                  def eval_scope(klass, scope, owner)
         | 
| 7 | 
            +
                    if scope.is_a?(Relation)
         | 
| 8 | 
            +
                      scope
         | 
| 9 | 
            +
                    else
         | 
| 10 | 
            +
                      klass.unscoped.instance_exec(owner, &scope).visited
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            require 'squeel/adapters/active_record/context'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Squeel
         | 
| 4 | 
            +
              module Adapters
         | 
| 5 | 
            +
                module ActiveRecord
         | 
| 6 | 
            +
                  class Context < ::Squeel::Context
         | 
| 7 | 
            +
                    class NoParentFoundError < RuntimeError; end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize(object)
         | 
| 10 | 
            +
                      super
         | 
| 11 | 
            +
                      @base = object.join_root
         | 
| 12 | 
            +
                      @engine = @base.base_klass.arel_engine
         | 
| 13 | 
            +
                      @arel_visitor = get_arel_visitor
         | 
| 14 | 
            +
                      @default_table = Arel::Table.new(@base.table_name, :as => @base.aliased_table_name, :engine => @engine)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def find(object, parent = @base)
         | 
| 18 | 
            +
                      if ::ActiveRecord::Associations::JoinDependency::JoinPart === parent
         | 
| 19 | 
            +
                        case object
         | 
| 20 | 
            +
                        when String, Symbol, Nodes::Stub
         | 
| 21 | 
            +
                          assoc_name = object.to_s
         | 
| 22 | 
            +
                          find_string_symbol_stub_association(@base.children, @base, assoc_name, parent)
         | 
| 23 | 
            +
                        when Nodes::Join
         | 
| 24 | 
            +
                          find_node_join_association(@base.children, @base, object, parent)
         | 
| 25 | 
            +
                        else
         | 
| 26 | 
            +
                          find_other_association(@base.children, @base, object, parent)
         | 
| 27 | 
            +
                        end
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    def find!(object, parent = @base)
         | 
| 32 | 
            +
                      if ::ActiveRecord::Associations::JoinDependency::JoinPart === parent
         | 
| 33 | 
            +
                        result =
         | 
| 34 | 
            +
                          case object
         | 
| 35 | 
            +
                            when String, Symbol, Nodes::Stub
         | 
| 36 | 
            +
                              assoc_name = object.to_s
         | 
| 37 | 
            +
                              find_string_symbol_stub_association(@base.children, @base, assoc_name, parent)
         | 
| 38 | 
            +
                            when Nodes::Join
         | 
| 39 | 
            +
                              find_node_join_association(@base.children, @base, object, parent)
         | 
| 40 | 
            +
                            else
         | 
| 41 | 
            +
                              find_other_association(@base.children, @base, object, parent)
         | 
| 42 | 
            +
                            end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        result || raise(NoParentFoundError, "can't find #{object} in #{parent}")
         | 
| 45 | 
            +
                      else
         | 
| 46 | 
            +
                        raise NoParentFoundError, "can't find #{object} in #{parent}"
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    def traverse!(keypath, parent = @base, include_endpoint = false)
         | 
| 51 | 
            +
                      parent = @base if keypath.absolute?
         | 
| 52 | 
            +
                      keypath.path_without_endpoint.each do |key|
         | 
| 53 | 
            +
                        parent = find!(key, parent)
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
                      parent = find!(keypath.endpoint, parent) if include_endpoint
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      parent
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    private
         | 
| 61 | 
            +
                      def find_string_symbol_stub_association(join_associations, current_parent, assoc_name, target_parent)
         | 
| 62 | 
            +
                        join_associations.each do |assoc|
         | 
| 63 | 
            +
                          return assoc if assoc.reflection.name.to_s == assoc_name && current_parent.equal?(target_parent)
         | 
| 64 | 
            +
                          child_assoc = find_string_symbol_stub_association(assoc.children, assoc, assoc_name, target_parent)
         | 
| 65 | 
            +
                          return child_assoc if child_assoc
         | 
| 66 | 
            +
                        end && false
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      def find_node_join_association(join_associations, current_parent, object, target_parent)
         | 
| 70 | 
            +
                        join_associations.each do |assoc|
         | 
| 71 | 
            +
                          return assoc if assoc.reflection.name == object._name && current_parent.equal?(target_parent) &&
         | 
| 72 | 
            +
                            (object.polymorphic? ? assoc.reflection.klass == object._klass : true)
         | 
| 73 | 
            +
                          child_assoc = find_node_join_association(assoc.children, assoc, object, target_parent)
         | 
| 74 | 
            +
                          return child_assoc if child_assoc
         | 
| 75 | 
            +
                        end && false
         | 
| 76 | 
            +
                      end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                      def find_other_association(join_associations, current_parent, object, target_parent)
         | 
| 79 | 
            +
                        join_associations.each do |assoc|
         | 
| 80 | 
            +
                          return assoc if assoc.reflection == object && current_parent.equal?(target_parent)
         | 
| 81 | 
            +
                          child_assoc = find_other_association(assoc.children, assoc, object, target_parent)
         | 
| 82 | 
            +
                          return child_assoc if child_assoc
         | 
| 83 | 
            +
                        end && false
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         |