activerecord 2.3.18 → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +105 -34
- data/examples/performance.rb +3 -39
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +81 -47
- data/lib/active_record/aggregations.rb +1 -3
- data/lib/active_record/association_preload.rb +39 -54
- data/lib/active_record/associations.rb +262 -419
- data/lib/active_record/associations/association_collection.rb +85 -100
- data/lib/active_record/associations/association_proxy.rb +20 -18
- data/lib/active_record/associations/belongs_to_association.rb +8 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
- data/lib/active_record/associations/has_many_association.rb +11 -19
- data/lib/active_record/associations/has_many_through_association.rb +30 -183
- data/lib/active_record/associations/has_one_association.rb +10 -10
- data/lib/active_record/associations/has_one_through_association.rb +13 -11
- data/lib/active_record/associations/through_association_scope.rb +153 -0
- data/lib/active_record/attribute_methods.rb +17 -370
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +87 -0
- data/lib/active_record/attribute_methods/primary_key.rb +44 -0
- data/lib/active_record/attribute_methods/query.rb +37 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +20 -41
- data/lib/active_record/base.rb +357 -1180
- data/lib/active_record/batches.rb +10 -16
- data/lib/active_record/callbacks.rb +66 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
- data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
- data/lib/active_record/fixtures.rb +21 -25
- data/lib/active_record/locale/en.yml +9 -27
- data/lib/active_record/locking/optimistic.rb +16 -48
- data/lib/active_record/migration.rb +59 -46
- data/lib/active_record/named_scope.rb +85 -92
- data/lib/active_record/nested_attributes.rb +18 -24
- data/lib/active_record/observer.rb +18 -94
- data/lib/active_record/railtie.rb +83 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +477 -0
- data/lib/active_record/railties/subscriber.rb +27 -0
- data/lib/active_record/reflection.rb +2 -15
- data/lib/active_record/relation.rb +339 -0
- data/lib/active_record/relation/calculations.rb +259 -0
- data/lib/active_record/relation/finder_methods.rb +315 -0
- data/lib/active_record/relation/predicate_builder.rb +46 -0
- data/lib/active_record/relation/query_methods.rb +218 -0
- data/lib/active_record/relation/spawn_methods.rb +102 -0
- data/lib/active_record/schema_dumper.rb +10 -6
- data/lib/active_record/serialization.rb +31 -74
- data/lib/active_record/serializers/xml_serializer.rb +33 -121
- data/lib/active_record/session_store.rb +1 -9
- data/lib/active_record/test_case.rb +1 -3
- data/lib/active_record/timestamp.rb +7 -5
- data/lib/active_record/transactions.rb +9 -9
- data/lib/active_record/validations.rb +51 -1100
- data/lib/active_record/validations/associated.rb +47 -0
- data/lib/active_record/validations/uniqueness.rb +181 -0
- data/lib/active_record/version.rb +3 -3
- data/lib/generators/active_record.rb +30 -0
- data/lib/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/generators/active_record/migration/templates/migration.rb +11 -0
- data/lib/generators/active_record/model/model_generator.rb +33 -0
- data/lib/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/generators/active_record/model/templates/model.rb +5 -0
- data/lib/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +67 -325
- data/RUNNING_UNIT_TESTS +0 -36
- data/Rakefile +0 -268
- data/install.rb +0 -30
- data/lib/active_record/calculations.rb +0 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
- data/lib/active_record/dirty.rb +0 -183
- data/lib/active_record/serializers/json_serializer.rb +0 -91
- data/lib/activerecord.rb +0 -2
- data/test/assets/example.log +0 -1
- data/test/assets/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +0 -24
- data/test/cases/active_schema_test_mysql.rb +0 -122
- data/test/cases/active_schema_test_postgresql.rb +0 -24
- data/test/cases/adapter_test.rb +0 -144
- data/test/cases/aggregations_test.rb +0 -167
- data/test/cases/ar_schema_test.rb +0 -32
- data/test/cases/associations/belongs_to_associations_test.rb +0 -438
- data/test/cases/associations/callbacks_test.rb +0 -161
- data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
- data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
- data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
- data/test/cases/associations/eager_singularization_test.rb +0 -145
- data/test/cases/associations/eager_test.rb +0 -852
- data/test/cases/associations/extension_test.rb +0 -62
- data/test/cases/associations/habtm_join_table_test.rb +0 -56
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
- data/test/cases/associations/has_many_associations_test.rb +0 -1273
- data/test/cases/associations/has_many_through_associations_test.rb +0 -360
- data/test/cases/associations/has_one_associations_test.rb +0 -330
- data/test/cases/associations/has_one_through_associations_test.rb +0 -209
- data/test/cases/associations/inner_join_association_test.rb +0 -93
- data/test/cases/associations/inverse_associations_test.rb +0 -566
- data/test/cases/associations/join_model_test.rb +0 -712
- data/test/cases/associations_test.rb +0 -282
- data/test/cases/attribute_methods_test.rb +0 -305
- data/test/cases/autosave_association_test.rb +0 -1218
- data/test/cases/base_test.rb +0 -2166
- data/test/cases/batches_test.rb +0 -81
- data/test/cases/binary_test.rb +0 -30
- data/test/cases/calculations_test.rb +0 -360
- data/test/cases/callbacks_observers_test.rb +0 -38
- data/test/cases/callbacks_test.rb +0 -438
- data/test/cases/class_inheritable_attributes_test.rb +0 -32
- data/test/cases/column_alias_test.rb +0 -17
- data/test/cases/column_definition_test.rb +0 -70
- data/test/cases/connection_pool_test.rb +0 -25
- data/test/cases/connection_test_firebird.rb +0 -8
- data/test/cases/connection_test_mysql.rb +0 -65
- data/test/cases/copy_table_test_sqlite.rb +0 -80
- data/test/cases/counter_cache_test.rb +0 -84
- data/test/cases/database_statements_test.rb +0 -12
- data/test/cases/datatype_test_postgresql.rb +0 -204
- data/test/cases/date_time_test.rb +0 -37
- data/test/cases/default_test_firebird.rb +0 -16
- data/test/cases/defaults_test.rb +0 -111
- data/test/cases/deprecated_finder_test.rb +0 -30
- data/test/cases/dirty_test.rb +0 -316
- data/test/cases/finder_respond_to_test.rb +0 -76
- data/test/cases/finder_test.rb +0 -1098
- data/test/cases/fixtures_test.rb +0 -661
- data/test/cases/helper.rb +0 -68
- data/test/cases/i18n_test.rb +0 -46
- data/test/cases/inheritance_test.rb +0 -262
- data/test/cases/invalid_date_test.rb +0 -24
- data/test/cases/json_serialization_test.rb +0 -219
- data/test/cases/lifecycle_test.rb +0 -193
- data/test/cases/locking_test.rb +0 -350
- data/test/cases/method_scoping_test.rb +0 -704
- data/test/cases/migration_test.rb +0 -1649
- data/test/cases/migration_test_firebird.rb +0 -124
- data/test/cases/mixin_test.rb +0 -96
- data/test/cases/modules_test.rb +0 -109
- data/test/cases/multiple_db_test.rb +0 -85
- data/test/cases/named_scope_test.rb +0 -372
- data/test/cases/nested_attributes_test.rb +0 -840
- data/test/cases/pk_test.rb +0 -119
- data/test/cases/pooled_connections_test.rb +0 -103
- data/test/cases/query_cache_test.rb +0 -129
- data/test/cases/readonly_test.rb +0 -107
- data/test/cases/reflection_test.rb +0 -234
- data/test/cases/reload_models_test.rb +0 -22
- data/test/cases/repair_helper.rb +0 -50
- data/test/cases/reserved_word_test_mysql.rb +0 -176
- data/test/cases/sanitize_test.rb +0 -25
- data/test/cases/schema_authorization_test_postgresql.rb +0 -75
- data/test/cases/schema_dumper_test.rb +0 -211
- data/test/cases/schema_test_postgresql.rb +0 -178
- data/test/cases/serialization_test.rb +0 -47
- data/test/cases/sp_test_mysql.rb +0 -16
- data/test/cases/synonym_test_oracle.rb +0 -17
- data/test/cases/timestamp_test.rb +0 -75
- data/test/cases/transactions_test.rb +0 -543
- data/test/cases/unconnected_test.rb +0 -32
- data/test/cases/validations_i18n_test.rb +0 -925
- data/test/cases/validations_test.rb +0 -1684
- data/test/cases/xml_serialization_test.rb +0 -240
- data/test/cases/yaml_serialization_test.rb +0 -11
- data/test/config.rb +0 -5
- data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
- data/test/connections/jdbc_jdbch2/connection.rb +0 -18
- data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
- data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
- data/test/connections/native_db2/connection.rb +0 -25
- data/test/connections/native_firebird/connection.rb +0 -26
- data/test/connections/native_frontbase/connection.rb +0 -27
- data/test/connections/native_mysql/connection.rb +0 -25
- data/test/connections/native_openbase/connection.rb +0 -21
- data/test/connections/native_oracle/connection.rb +0 -27
- data/test/connections/native_postgresql/connection.rb +0 -21
- data/test/connections/native_sqlite/connection.rb +0 -25
- data/test/connections/native_sqlite3/connection.rb +0 -25
- data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
- data/test/connections/native_sybase/connection.rb +0 -23
- data/test/fixtures/accounts.yml +0 -29
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author_addresses.yml +0 -5
- data/test/fixtures/author_favorites.yml +0 -4
- data/test/fixtures/authors.yml +0 -9
- data/test/fixtures/binaries.yml +0 -132
- data/test/fixtures/books.yml +0 -7
- data/test/fixtures/categories.yml +0 -14
- data/test/fixtures/categories/special_categories.yml +0 -9
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
- data/test/fixtures/categories_ordered.yml +0 -7
- data/test/fixtures/categories_posts.yml +0 -23
- data/test/fixtures/categorizations.yml +0 -17
- data/test/fixtures/clubs.yml +0 -6
- data/test/fixtures/comments.yml +0 -59
- data/test/fixtures/companies.yml +0 -56
- data/test/fixtures/computers.yml +0 -4
- data/test/fixtures/courses.yml +0 -7
- data/test/fixtures/customers.yml +0 -26
- data/test/fixtures/developers.yml +0 -21
- data/test/fixtures/developers_projects.yml +0 -17
- data/test/fixtures/edges.yml +0 -6
- data/test/fixtures/entrants.yml +0 -14
- data/test/fixtures/faces.yml +0 -11
- data/test/fixtures/fk_test_has_fk.yml +0 -3
- data/test/fixtures/fk_test_has_pk.yml +0 -2
- data/test/fixtures/funny_jokes.yml +0 -10
- data/test/fixtures/interests.yml +0 -33
- data/test/fixtures/items.yml +0 -4
- data/test/fixtures/jobs.yml +0 -7
- data/test/fixtures/legacy_things.yml +0 -3
- data/test/fixtures/mateys.yml +0 -4
- data/test/fixtures/member_types.yml +0 -6
- data/test/fixtures/members.yml +0 -6
- data/test/fixtures/memberships.yml +0 -20
- data/test/fixtures/men.yml +0 -5
- data/test/fixtures/minimalistics.yml +0 -2
- data/test/fixtures/mixed_case_monkeys.yml +0 -6
- data/test/fixtures/mixins.yml +0 -29
- data/test/fixtures/movies.yml +0 -7
- data/test/fixtures/naked/csv/accounts.csv +0 -1
- data/test/fixtures/naked/yml/accounts.yml +0 -1
- data/test/fixtures/naked/yml/companies.yml +0 -1
- data/test/fixtures/naked/yml/courses.yml +0 -1
- data/test/fixtures/organizations.yml +0 -5
- data/test/fixtures/owners.yml +0 -7
- data/test/fixtures/parrots.yml +0 -27
- data/test/fixtures/parrots_pirates.yml +0 -7
- data/test/fixtures/people.yml +0 -15
- data/test/fixtures/pets.yml +0 -14
- data/test/fixtures/pirates.yml +0 -9
- data/test/fixtures/polymorphic_designs.yml +0 -19
- data/test/fixtures/polymorphic_prices.yml +0 -19
- data/test/fixtures/posts.yml +0 -52
- data/test/fixtures/price_estimates.yml +0 -7
- data/test/fixtures/projects.yml +0 -7
- data/test/fixtures/readers.yml +0 -9
- data/test/fixtures/references.yml +0 -17
- data/test/fixtures/reserved_words/distinct.yml +0 -5
- data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
- data/test/fixtures/reserved_words/group.yml +0 -14
- data/test/fixtures/reserved_words/select.yml +0 -8
- data/test/fixtures/reserved_words/values.yml +0 -7
- data/test/fixtures/ships.yml +0 -5
- data/test/fixtures/sponsors.yml +0 -9
- data/test/fixtures/subscribers.yml +0 -7
- data/test/fixtures/subscriptions.yml +0 -12
- data/test/fixtures/taggings.yml +0 -28
- data/test/fixtures/tags.yml +0 -7
- data/test/fixtures/tasks.yml +0 -7
- data/test/fixtures/tees.yml +0 -4
- data/test/fixtures/ties.yml +0 -4
- data/test/fixtures/topics.yml +0 -42
- data/test/fixtures/toys.yml +0 -4
- data/test/fixtures/treasures.yml +0 -10
- data/test/fixtures/vertices.yml +0 -4
- data/test/fixtures/warehouse-things.yml +0 -3
- data/test/fixtures/zines.yml +0 -5
- data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
- data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
- data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
- data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
- data/test/migrations/duplicate/3_foo.rb +0 -7
- data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
- data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
- data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
- data/test/migrations/missing/1_people_have_last_names.rb +0 -9
- data/test/migrations/missing/3_we_need_reminders.rb +0 -12
- data/test/migrations/missing/4_innocent_jointable.rb +0 -12
- data/test/migrations/valid/1_people_have_last_names.rb +0 -9
- data/test/migrations/valid/2_we_need_reminders.rb +0 -12
- data/test/migrations/valid/3_innocent_jointable.rb +0 -12
- data/test/models/author.rb +0 -151
- data/test/models/auto_id.rb +0 -4
- data/test/models/binary.rb +0 -2
- data/test/models/bird.rb +0 -9
- data/test/models/book.rb +0 -4
- data/test/models/categorization.rb +0 -5
- data/test/models/category.rb +0 -34
- data/test/models/citation.rb +0 -6
- data/test/models/club.rb +0 -13
- data/test/models/column_name.rb +0 -3
- data/test/models/comment.rb +0 -29
- data/test/models/company.rb +0 -173
- data/test/models/company_in_module.rb +0 -78
- data/test/models/computer.rb +0 -3
- data/test/models/contact.rb +0 -16
- data/test/models/contract.rb +0 -5
- data/test/models/course.rb +0 -3
- data/test/models/customer.rb +0 -73
- data/test/models/default.rb +0 -2
- data/test/models/developer.rb +0 -101
- data/test/models/edge.rb +0 -5
- data/test/models/entrant.rb +0 -3
- data/test/models/essay.rb +0 -3
- data/test/models/event.rb +0 -3
- data/test/models/event_author.rb +0 -8
- data/test/models/face.rb +0 -7
- data/test/models/guid.rb +0 -2
- data/test/models/interest.rb +0 -5
- data/test/models/invoice.rb +0 -4
- data/test/models/item.rb +0 -7
- data/test/models/job.rb +0 -5
- data/test/models/joke.rb +0 -3
- data/test/models/keyboard.rb +0 -3
- data/test/models/legacy_thing.rb +0 -3
- data/test/models/line_item.rb +0 -3
- data/test/models/man.rb +0 -9
- data/test/models/matey.rb +0 -4
- data/test/models/member.rb +0 -12
- data/test/models/member_detail.rb +0 -5
- data/test/models/member_type.rb +0 -3
- data/test/models/membership.rb +0 -9
- data/test/models/minimalistic.rb +0 -2
- data/test/models/mixed_case_monkey.rb +0 -3
- data/test/models/movie.rb +0 -5
- data/test/models/order.rb +0 -4
- data/test/models/organization.rb +0 -6
- data/test/models/owner.rb +0 -5
- data/test/models/parrot.rb +0 -22
- data/test/models/person.rb +0 -16
- data/test/models/pet.rb +0 -5
- data/test/models/pirate.rb +0 -80
- data/test/models/polymorphic_design.rb +0 -3
- data/test/models/polymorphic_price.rb +0 -3
- data/test/models/post.rb +0 -102
- data/test/models/price_estimate.rb +0 -3
- data/test/models/project.rb +0 -30
- data/test/models/reader.rb +0 -4
- data/test/models/reference.rb +0 -4
- data/test/models/reply.rb +0 -46
- data/test/models/ship.rb +0 -19
- data/test/models/ship_part.rb +0 -7
- data/test/models/sponsor.rb +0 -4
- data/test/models/subject.rb +0 -4
- data/test/models/subscriber.rb +0 -8
- data/test/models/subscription.rb +0 -4
- data/test/models/tag.rb +0 -7
- data/test/models/tagging.rb +0 -10
- data/test/models/task.rb +0 -3
- data/test/models/tee.rb +0 -4
- data/test/models/tie.rb +0 -4
- data/test/models/topic.rb +0 -80
- data/test/models/toy.rb +0 -6
- data/test/models/treasure.rb +0 -8
- data/test/models/vertex.rb +0 -9
- data/test/models/warehouse_thing.rb +0 -5
- data/test/models/zine.rb +0 -3
- data/test/schema/mysql_specific_schema.rb +0 -31
- data/test/schema/postgresql_specific_schema.rb +0 -114
- data/test/schema/schema.rb +0 -550
- data/test/schema/schema2.rb +0 -6
- data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -0,0 +1,315 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module FinderMethods
|
3
|
+
# Find operates with four different retrieval approaches:
|
4
|
+
#
|
5
|
+
# * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
|
6
|
+
# If no record can be found for all of the listed ids, then RecordNotFound will be raised.
|
7
|
+
# * Find first - This will return the first record matched by the options used. These options can either be specific
|
8
|
+
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
|
9
|
+
# <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
|
10
|
+
# * Find last - This will return the last record matched by the options used. These options can either be specific
|
11
|
+
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
|
12
|
+
# <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
|
13
|
+
# * Find all - This will return all the records matched by the options used.
|
14
|
+
# If no records are found, an empty array is returned. Use
|
15
|
+
# <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
|
16
|
+
#
|
17
|
+
# All approaches accept an options hash as their last parameter.
|
18
|
+
#
|
19
|
+
# ==== Parameters
|
20
|
+
#
|
21
|
+
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
|
22
|
+
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
|
23
|
+
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
|
24
|
+
# * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
|
25
|
+
# * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
|
26
|
+
# * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
|
27
|
+
# * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
|
28
|
+
# named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s),
|
29
|
+
# or an array containing a mixture of both strings and named associations.
|
30
|
+
# If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
|
31
|
+
# Pass <tt>:readonly => false</tt> to override.
|
32
|
+
# * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
|
33
|
+
# to already defined associations. See eager loading under Associations.
|
34
|
+
# * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not
|
35
|
+
# include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
|
36
|
+
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
|
37
|
+
# of a database view).
|
38
|
+
# * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
|
39
|
+
# * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
|
40
|
+
# <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
|
41
|
+
#
|
42
|
+
# ==== Examples
|
43
|
+
#
|
44
|
+
# # find by id
|
45
|
+
# Person.find(1) # returns the object for ID = 1
|
46
|
+
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
|
47
|
+
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
|
48
|
+
# Person.find([1]) # returns an array for the object with ID = 1
|
49
|
+
# Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
|
50
|
+
#
|
51
|
+
# Note that returned records may not be in the same order as the ids you
|
52
|
+
# provide since database rows are unordered. Give an explicit <tt>:order</tt>
|
53
|
+
# to ensure the results are sorted.
|
54
|
+
#
|
55
|
+
# ==== Examples
|
56
|
+
#
|
57
|
+
# # find first
|
58
|
+
# Person.find(:first) # returns the first object fetched by SELECT * FROM people
|
59
|
+
# Person.find(:first, :conditions => [ "user_name = ?", user_name])
|
60
|
+
# Person.find(:first, :conditions => [ "user_name = :u", { :u => user_name }])
|
61
|
+
# Person.find(:first, :order => "created_on DESC", :offset => 5)
|
62
|
+
#
|
63
|
+
# # find last
|
64
|
+
# Person.find(:last) # returns the last object fetched by SELECT * FROM people
|
65
|
+
# Person.find(:last, :conditions => [ "user_name = ?", user_name])
|
66
|
+
# Person.find(:last, :order => "created_on DESC", :offset => 5)
|
67
|
+
#
|
68
|
+
# # find all
|
69
|
+
# Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
|
70
|
+
# Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
|
71
|
+
# Person.find(:all, :conditions => { :friends => ["Bob", "Steve", "Fred"] }
|
72
|
+
# Person.find(:all, :offset => 10, :limit => 10)
|
73
|
+
# Person.find(:all, :include => [ :account, :friends ])
|
74
|
+
# Person.find(:all, :group => "category")
|
75
|
+
#
|
76
|
+
# Example for find with a lock: Imagine two concurrent transactions:
|
77
|
+
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
|
78
|
+
# in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
|
79
|
+
# transaction has to wait until the first is finished; we get the
|
80
|
+
# expected <tt>person.visits == 4</tt>.
|
81
|
+
#
|
82
|
+
# Person.transaction do
|
83
|
+
# person = Person.find(1, :lock => true)
|
84
|
+
# person.visits += 1
|
85
|
+
# person.save!
|
86
|
+
# end
|
87
|
+
def find(*args, &block)
|
88
|
+
return to_a.find(&block) if block_given?
|
89
|
+
|
90
|
+
options = args.extract_options!
|
91
|
+
|
92
|
+
if options.present?
|
93
|
+
apply_finder_options(options).find(*args)
|
94
|
+
else
|
95
|
+
case args.first
|
96
|
+
when :first, :last, :all
|
97
|
+
send(args.first)
|
98
|
+
else
|
99
|
+
find_with_ids(*args)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
|
105
|
+
# same arguments to this method as you can to <tt>find(:first)</tt>.
|
106
|
+
def first(*args)
|
107
|
+
args.any? ? apply_finder_options(args.first).first : find_first
|
108
|
+
end
|
109
|
+
|
110
|
+
# A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
|
111
|
+
# same arguments to this method as you can to <tt>find(:last)</tt>.
|
112
|
+
def last(*args)
|
113
|
+
args.any? ? apply_finder_options(args.first).last : find_last
|
114
|
+
end
|
115
|
+
|
116
|
+
# A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
|
117
|
+
# same arguments to this method as you can to <tt>find(:all)</tt>.
|
118
|
+
def all(*args)
|
119
|
+
args.any? ? apply_finder_options(args.first).to_a : to_a
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns true if a record exists in the table that matches the +id+ or
|
123
|
+
# conditions given, or false otherwise. The argument can take five forms:
|
124
|
+
#
|
125
|
+
# * Integer - Finds the record with this primary key.
|
126
|
+
# * String - Finds the record with a primary key corresponding to this
|
127
|
+
# string (such as <tt>'5'</tt>).
|
128
|
+
# * Array - Finds the record that matches these +find+-style conditions
|
129
|
+
# (such as <tt>['color = ?', 'red']</tt>).
|
130
|
+
# * Hash - Finds the record that matches these +find+-style conditions
|
131
|
+
# (such as <tt>{:color => 'red'}</tt>).
|
132
|
+
# * No args - Returns false if the table is empty, true otherwise.
|
133
|
+
#
|
134
|
+
# For more information about specifying conditions as a Hash or Array,
|
135
|
+
# see the Conditions section in the introduction to ActiveRecord::Base.
|
136
|
+
#
|
137
|
+
# Note: You can't pass in a condition as a string (like <tt>name =
|
138
|
+
# 'Jamie'</tt>), since it would be sanitized and then queried against
|
139
|
+
# the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
|
140
|
+
#
|
141
|
+
# ==== Examples
|
142
|
+
# Person.exists?(5)
|
143
|
+
# Person.exists?('5')
|
144
|
+
# Person.exists?(:name => "David")
|
145
|
+
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
146
|
+
# Person.exists?
|
147
|
+
def exists?(id = nil)
|
148
|
+
case id
|
149
|
+
when Array, Hash
|
150
|
+
where(id).exists?
|
151
|
+
else
|
152
|
+
relation = select(primary_key).limit(1)
|
153
|
+
relation = relation.where(primary_key.eq(id)) if id
|
154
|
+
relation.first ? true : false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
|
160
|
+
def find_with_associations
|
161
|
+
including = (@eager_load_values + @includes_values).uniq
|
162
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
|
163
|
+
rows = construct_relation_for_association_find(join_dependency).to_a
|
164
|
+
join_dependency.instantiate(rows)
|
165
|
+
rescue ThrowResult
|
166
|
+
[]
|
167
|
+
end
|
168
|
+
|
169
|
+
def construct_relation_for_association_calculations
|
170
|
+
including = (@eager_load_values + @includes_values).uniq
|
171
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel))
|
172
|
+
|
173
|
+
relation = except(:includes, :eager_load, :preload)
|
174
|
+
apply_join_dependency(relation, join_dependency)
|
175
|
+
end
|
176
|
+
|
177
|
+
def construct_relation_for_association_find(join_dependency)
|
178
|
+
relation = except(:includes, :eager_load, :preload, :select).select(@klass.send(:column_aliases, join_dependency))
|
179
|
+
apply_join_dependency(relation, join_dependency)
|
180
|
+
end
|
181
|
+
|
182
|
+
def apply_join_dependency(relation, join_dependency)
|
183
|
+
for association in join_dependency.join_associations
|
184
|
+
relation = association.join_relation(relation)
|
185
|
+
end
|
186
|
+
|
187
|
+
limitable_reflections = @klass.send(:using_limitable_reflections?, join_dependency.reflections)
|
188
|
+
|
189
|
+
if !limitable_reflections && relation.limit_value
|
190
|
+
limited_id_condition = construct_limited_ids_condition(relation.except(:select))
|
191
|
+
relation = relation.where(limited_id_condition)
|
192
|
+
end
|
193
|
+
|
194
|
+
relation = relation.except(:limit, :offset) unless limitable_reflections
|
195
|
+
|
196
|
+
relation
|
197
|
+
end
|
198
|
+
|
199
|
+
def construct_limited_ids_condition(relation)
|
200
|
+
orders = relation.order_values.join(", ")
|
201
|
+
values = @klass.connection.distinct("#{@klass.connection.quote_table_name @klass.table_name}.#{@klass.primary_key}", orders)
|
202
|
+
|
203
|
+
ids_array = relation.select(values).collect {|row| row[@klass.primary_key]}
|
204
|
+
ids_array.empty? ? raise(ThrowResult) : primary_key.in(ids_array)
|
205
|
+
end
|
206
|
+
|
207
|
+
def find_by_attributes(match, attributes, *args)
|
208
|
+
conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h}
|
209
|
+
result = where(conditions).send(match.finder)
|
210
|
+
|
211
|
+
if match.bang? && result.blank?
|
212
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
|
213
|
+
else
|
214
|
+
result
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def find_or_instantiator_by_attributes(match, attributes, *args)
|
219
|
+
guard_protected_attributes = false
|
220
|
+
|
221
|
+
if args[0].is_a?(Hash)
|
222
|
+
guard_protected_attributes = true
|
223
|
+
attributes_for_create = args[0].with_indifferent_access
|
224
|
+
conditions = attributes_for_create.slice(*attributes).symbolize_keys
|
225
|
+
else
|
226
|
+
attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h}
|
227
|
+
end
|
228
|
+
|
229
|
+
record = where(conditions).first
|
230
|
+
|
231
|
+
unless record
|
232
|
+
record = @klass.new { |r| r.send(:attributes=, attributes_for_create, guard_protected_attributes) }
|
233
|
+
yield(record) if block_given?
|
234
|
+
record.save if match.instantiator == :create
|
235
|
+
end
|
236
|
+
|
237
|
+
record
|
238
|
+
end
|
239
|
+
|
240
|
+
def find_with_ids(*ids, &block)
|
241
|
+
return to_a.find(&block) if block_given?
|
242
|
+
|
243
|
+
expects_array = ids.first.kind_of?(Array)
|
244
|
+
return ids.first if expects_array && ids.first.empty?
|
245
|
+
|
246
|
+
ids = ids.flatten.compact.uniq
|
247
|
+
|
248
|
+
case ids.size
|
249
|
+
when 0
|
250
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
|
251
|
+
when 1
|
252
|
+
result = find_one(ids.first)
|
253
|
+
expects_array ? [ result ] : result
|
254
|
+
else
|
255
|
+
find_some(ids)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def find_one(id)
|
260
|
+
record = where(primary_key.eq(id)).first
|
261
|
+
|
262
|
+
unless record
|
263
|
+
conditions = arel.send(:where_clauses).join(', ')
|
264
|
+
conditions = " [WHERE #{conditions}]" if conditions.present?
|
265
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}"
|
266
|
+
end
|
267
|
+
|
268
|
+
record
|
269
|
+
end
|
270
|
+
|
271
|
+
def find_some(ids)
|
272
|
+
result = where(primary_key.in(ids)).all
|
273
|
+
|
274
|
+
expected_size =
|
275
|
+
if @limit_value && ids.size > @limit_value
|
276
|
+
@limit_value
|
277
|
+
else
|
278
|
+
ids.size
|
279
|
+
end
|
280
|
+
|
281
|
+
# 11 ids with limit 3, offset 9 should give 2 results.
|
282
|
+
if @offset_value && (ids.size - @offset_value < expected_size)
|
283
|
+
expected_size = ids.size - @offset_value
|
284
|
+
end
|
285
|
+
|
286
|
+
if result.size == expected_size
|
287
|
+
result
|
288
|
+
else
|
289
|
+
conditions = arel.send(:where_clauses).join(', ')
|
290
|
+
conditions = " [WHERE #{conditions}]" if conditions.present?
|
291
|
+
|
292
|
+
error = "Couldn't find all #{@klass.name.pluralize} with IDs "
|
293
|
+
error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
|
294
|
+
raise RecordNotFound, error
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def find_first
|
299
|
+
if loaded?
|
300
|
+
@records.first
|
301
|
+
else
|
302
|
+
@first ||= limit(1).to_a[0]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def find_last
|
307
|
+
if loaded?
|
308
|
+
@records.last
|
309
|
+
else
|
310
|
+
@last ||= reverse_order.limit(1).to_a[0]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class PredicateBuilder
|
3
|
+
|
4
|
+
def initialize(engine)
|
5
|
+
@engine = engine
|
6
|
+
end
|
7
|
+
|
8
|
+
def build_from_hash(attributes, default_table)
|
9
|
+
predicates = attributes.map do |column, value|
|
10
|
+
table = default_table
|
11
|
+
|
12
|
+
if value.is_a?(Hash)
|
13
|
+
table = Arel::Table.new(column, :engine => @engine)
|
14
|
+
build_from_hash(value, table)
|
15
|
+
else
|
16
|
+
column = column.to_s
|
17
|
+
|
18
|
+
if column.include?('.')
|
19
|
+
table_name, column = column.split('.', 2)
|
20
|
+
table = Arel::Table.new(table_name, :engine => @engine)
|
21
|
+
end
|
22
|
+
|
23
|
+
attribute = table[column] || Arel::Attribute.new(table, column.to_sym)
|
24
|
+
|
25
|
+
case value
|
26
|
+
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
|
27
|
+
values = value.to_a
|
28
|
+
attribute.in(values)
|
29
|
+
when Range
|
30
|
+
# TODO : Arel should handle ranges with excluded end.
|
31
|
+
if value.exclude_end?
|
32
|
+
[attribute.gteq(value.begin), attribute.lt(value.end)]
|
33
|
+
else
|
34
|
+
attribute.in(value)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
attribute.eq(value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
predicates.flatten
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module QueryMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
(ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method|
|
7
|
+
attr_accessor :"#{query_method}_values"
|
8
|
+
|
9
|
+
class_eval <<-CEVAL
|
10
|
+
def #{query_method}(*args)
|
11
|
+
new_relation = spawn
|
12
|
+
value = Array.wrap(args.flatten).reject {|x| x.blank? }
|
13
|
+
new_relation.#{query_method}_values += value if value.present?
|
14
|
+
new_relation
|
15
|
+
end
|
16
|
+
CEVAL
|
17
|
+
end
|
18
|
+
|
19
|
+
[:where, :having].each do |query_method|
|
20
|
+
class_eval <<-CEVAL
|
21
|
+
def #{query_method}(*args)
|
22
|
+
new_relation = spawn
|
23
|
+
value = build_where(*args)
|
24
|
+
new_relation.#{query_method}_values += [*value] if value.present?
|
25
|
+
new_relation
|
26
|
+
end
|
27
|
+
CEVAL
|
28
|
+
end
|
29
|
+
|
30
|
+
ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
|
31
|
+
attr_accessor :"#{query_method}_value"
|
32
|
+
|
33
|
+
class_eval <<-CEVAL
|
34
|
+
def #{query_method}(value = true)
|
35
|
+
new_relation = spawn
|
36
|
+
new_relation.#{query_method}_value = value
|
37
|
+
new_relation
|
38
|
+
end
|
39
|
+
CEVAL
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def lock(locks = true)
|
44
|
+
relation = spawn
|
45
|
+
case locks
|
46
|
+
when String, TrueClass, NilClass
|
47
|
+
spawn.tap {|new_relation| new_relation.lock_value = locks || true }
|
48
|
+
else
|
49
|
+
spawn.tap {|new_relation| new_relation.lock_value = false }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def reverse_order
|
54
|
+
order_clause = arel.send(:order_clauses).join(', ')
|
55
|
+
relation = except(:order)
|
56
|
+
|
57
|
+
if order_clause.present?
|
58
|
+
relation.order(reverse_sql_order(order_clause))
|
59
|
+
else
|
60
|
+
relation.order("#{@klass.table_name}.#{@klass.primary_key} DESC")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def arel
|
65
|
+
@arel ||= build_arel
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_arel
|
69
|
+
arel = table
|
70
|
+
|
71
|
+
joined_associations = []
|
72
|
+
association_joins = []
|
73
|
+
|
74
|
+
joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
|
75
|
+
|
76
|
+
# Build association joins first
|
77
|
+
joins.each do |join|
|
78
|
+
association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
|
79
|
+
end
|
80
|
+
|
81
|
+
if association_joins.any?
|
82
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins.uniq, nil)
|
83
|
+
to_join = []
|
84
|
+
|
85
|
+
join_dependency.join_associations.each do |association|
|
86
|
+
if (association_relation = association.relation).is_a?(Array)
|
87
|
+
to_join << [association_relation.first, association.association_join.first]
|
88
|
+
to_join << [association_relation.last, association.association_join.last]
|
89
|
+
else
|
90
|
+
to_join << [association_relation, association.association_join]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
to_join.each do |tj|
|
95
|
+
unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] }
|
96
|
+
joined_associations << tj
|
97
|
+
arel = arel.join(tj[0]).on(*tj[1])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
joins.each do |join|
|
103
|
+
next if join.blank?
|
104
|
+
|
105
|
+
@implicit_readonly = true
|
106
|
+
|
107
|
+
case join
|
108
|
+
when Relation::JoinOperation
|
109
|
+
arel = arel.join(join.relation, join.join_class).on(*join.on)
|
110
|
+
when Hash, Array, Symbol
|
111
|
+
if array_of_strings?(join)
|
112
|
+
join_string = join.join(' ')
|
113
|
+
arel = arel.join(join_string)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
arel = arel.join(join)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@where_values.uniq.each do |where|
|
121
|
+
next if where.blank?
|
122
|
+
|
123
|
+
case where
|
124
|
+
when Arel::SqlLiteral
|
125
|
+
arel = arel.where(where)
|
126
|
+
else
|
127
|
+
sql = where.is_a?(String) ? where : where.to_sql
|
128
|
+
arel = arel.where(Arel::SqlLiteral.new("(#{sql})"))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
@having_values.uniq.each do |h|
|
133
|
+
arel = h.is_a?(String) ? arel.having(h) : arel.having(*h)
|
134
|
+
end
|
135
|
+
|
136
|
+
if defined?(@limit_value) && @limit_value.present?
|
137
|
+
arel = arel.take(@limit_value)
|
138
|
+
end
|
139
|
+
|
140
|
+
if defined?(@offset_value) && @offset_value.present?
|
141
|
+
arel = arel.skip(@offset_value)
|
142
|
+
end
|
143
|
+
|
144
|
+
@group_values.uniq.each do |g|
|
145
|
+
arel = arel.group(g) if g.present?
|
146
|
+
end
|
147
|
+
|
148
|
+
@order_values.uniq.each do |o|
|
149
|
+
arel = arel.order(Arel::SqlLiteral.new(o.to_s)) if o.present?
|
150
|
+
end
|
151
|
+
|
152
|
+
selects = @select_values.uniq
|
153
|
+
|
154
|
+
quoted_table_name = @klass.quoted_table_name
|
155
|
+
|
156
|
+
if selects.present?
|
157
|
+
selects.each do |s|
|
158
|
+
@implicit_readonly = false
|
159
|
+
arel = arel.project(s) if s.present?
|
160
|
+
end
|
161
|
+
else
|
162
|
+
arel = arel.project(quoted_table_name + '.*')
|
163
|
+
end
|
164
|
+
|
165
|
+
arel =
|
166
|
+
if defined?(@from_value) && @from_value.present?
|
167
|
+
arel.from(@from_value)
|
168
|
+
else
|
169
|
+
arel.from(quoted_table_name)
|
170
|
+
end
|
171
|
+
|
172
|
+
case @lock_value
|
173
|
+
when TrueClass
|
174
|
+
arel = arel.lock
|
175
|
+
when String
|
176
|
+
arel = arel.lock(@lock_value)
|
177
|
+
end
|
178
|
+
|
179
|
+
arel
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_where(*args)
|
183
|
+
return if args.blank?
|
184
|
+
|
185
|
+
builder = PredicateBuilder.new(table.engine)
|
186
|
+
|
187
|
+
conditions = if [String, Array].include?(args.first.class)
|
188
|
+
@klass.send(:sanitize_sql, args.size > 1 ? args : args.first)
|
189
|
+
elsif args.first.is_a?(Hash)
|
190
|
+
attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first)
|
191
|
+
builder.build_from_hash(attributes, table)
|
192
|
+
else
|
193
|
+
args.first
|
194
|
+
end
|
195
|
+
|
196
|
+
conditions
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def reverse_sql_order(order_query)
|
202
|
+
order_query.to_s.split(/,/).each { |s|
|
203
|
+
if s.match(/\s(asc|ASC)$/)
|
204
|
+
s.gsub!(/\s(asc|ASC)$/, ' DESC')
|
205
|
+
elsif s.match(/\s(desc|DESC)$/)
|
206
|
+
s.gsub!(/\s(desc|DESC)$/, ' ASC')
|
207
|
+
else
|
208
|
+
s.concat(' DESC')
|
209
|
+
end
|
210
|
+
}.join(',')
|
211
|
+
end
|
212
|
+
|
213
|
+
def array_of_strings?(o)
|
214
|
+
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
end
|