dart 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/dart.gemspec +28 -0
- data/lib/dart.rb +1 -0
- data/lib/dart/active_record_model_reflection.rb +7 -0
- data/lib/dart/core.rb +17 -0
- data/lib/dart/core/association.rb +29 -0
- data/lib/dart/core/direct_association.rb +36 -0
- data/lib/dart/core/foreign_key_info.rb +8 -0
- data/lib/dart/core/many_to_many_association.rb +55 -0
- data/lib/dart/core/many_to_one_association.rb +18 -0
- data/lib/dart/core/one_to_many_association.rb +13 -0
- data/lib/dart/core/one_to_one_association.rb +17 -0
- data/lib/dart/core/relation.rb +46 -0
- data/lib/dart/database.rb +8 -0
- data/lib/dart/database/many_to_many_association.rb +14 -0
- data/lib/dart/database/many_to_one_association.rb +14 -0
- data/lib/dart/database/one_to_many_association.rb +14 -0
- data/lib/dart/database/relation.rb +21 -0
- data/lib/dart/database/test_helpers.rb +23 -0
- data/lib/dart/naming_conventions.rb +18 -0
- data/lib/dart/naming_conventions/abstract_base.rb +71 -0
- data/lib/dart/naming_conventions/association_helpers.rb +16 -0
- data/lib/dart/naming_conventions/direct_association_helpers.rb +21 -0
- data/lib/dart/naming_conventions/foreign_key_finder.rb +26 -0
- data/lib/dart/naming_conventions/many_to_many_association_helpers.rb +52 -0
- data/lib/dart/naming_conventions/many_to_one_association_helpers.rb +20 -0
- data/lib/dart/naming_conventions/one_to_many_association_helpers.rb +26 -0
- data/lib/dart/naming_conventions/relation_helpers.rb +40 -0
- data/lib/dart/reflection/abstract_resolver.rb +7 -0
- data/lib/dart/reflection/active_record_model/resolver.rb +122 -0
- data/lib/dart/reflection/orm_model_resolver.rb +64 -0
- data/lib/dart/reflection/sequel/naming_conventions.rb +25 -0
- data/lib/dart/reflection/sequel/sequelizer.rb +14 -0
- data/lib/dart/reflection/sequel_model/resolver.rb +78 -0
- data/lib/dart/reflection/sequel_table/foreign_key_finder.rb +50 -0
- data/lib/dart/reflection/sequel_table/reflector.rb +151 -0
- data/lib/dart/reflection/sequel_table/resolver.rb +60 -0
- data/lib/dart/reflection/sequel_table/schema.rb +52 -0
- data/lib/dart/sequel_model_reflection.rb +10 -0
- data/lib/dart/sequel_table_reflection.rb +26 -0
- data/lib/dart/version.rb +3 -0
- data/test/dart/database/many_to_many_association_test.rb +80 -0
- data/test/dart/naming_conventions/abstract_base_test.rb +38 -0
- data/test/dart/reflection/orm_model_resolver_test.rb +66 -0
- data/test/test_helper.rb +6 -0
- metadata +182 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dart
|
2
|
+
module Database
|
3
|
+
# A Database::Relation extends Dart::Relation with naming conventions to support table reflection
|
4
|
+
class Relation < Dart::Relation
|
5
|
+
include NamingConventions::RelationHelpers
|
6
|
+
|
7
|
+
def add_many_to_one(*ass_args)
|
8
|
+
add_association ManyToOneAssociation.new(*ass_args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_one_to_many(*ass_args)
|
12
|
+
add_association OneToManyAssociation.new(*ass_args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_many_to_many(*ass_args)
|
16
|
+
add_association ManyToManyAssociation.new(*ass_args)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dart
|
2
|
+
module Database
|
3
|
+
|
4
|
+
# Test support class used to easily construct associations
|
5
|
+
module TestHelpers
|
6
|
+
def relation(*args)
|
7
|
+
Relation.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def many_to_one_ass(*args)
|
11
|
+
ManyToOneAssociation.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def one_to_many_ass(*args)
|
15
|
+
OneToManyAssociation.new(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def many_to_many_ass(*args)
|
19
|
+
ManyToManyAssociation.new(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'naming_conventions/foreign_key_finder'
|
2
|
+
|
3
|
+
require_relative 'naming_conventions/relation_helpers'
|
4
|
+
require_relative 'naming_conventions/association_helpers'
|
5
|
+
require_relative 'naming_conventions/direct_association_helpers'
|
6
|
+
require_relative 'naming_conventions/many_to_many_association_helpers'
|
7
|
+
require_relative 'naming_conventions/many_to_one_association_helpers'
|
8
|
+
require_relative 'naming_conventions/one_to_many_association_helpers'
|
9
|
+
|
10
|
+
module Dart
|
11
|
+
module NamingConventions
|
12
|
+
|
13
|
+
# NamingConventions.instance must be set by the user of this module. It is normally a subclass of AbstractBase
|
14
|
+
class << self
|
15
|
+
attr_accessor :instance
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
|
4
|
+
PUBLIC_API_METHODS = [:parent_table_for, :singular_association_name, :plural_association_name, :long_association_name, :conventional_join_table_names]
|
5
|
+
|
6
|
+
class AbstractBase
|
7
|
+
abstract_method :conventional_primary_key, :foreign_key_regex
|
8
|
+
abstract_method :pluralize, :singularize
|
9
|
+
|
10
|
+
# Returns the name of a possibly referenced table if the given possible_foreign_key follows the naming convention,
|
11
|
+
# otherwise returns nil
|
12
|
+
#
|
13
|
+
# Examples:
|
14
|
+
# parent_table_for('group_id') => 'groups'
|
15
|
+
# parent_table_for('created_by') => nil
|
16
|
+
#
|
17
|
+
# @param [String|Symbol] possible_foreign_key name of the possibly referencing column
|
18
|
+
# @return [String|NilClass] name of the possibly referenced table if found by convention
|
19
|
+
#
|
20
|
+
def parent_table_for(possible_foreign_key)
|
21
|
+
pluralize singular_association_name(possible_foreign_key) if possible_foreign_key =~ foreign_key_regex
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a many_to_one association name based on the given foreign_key according to the naming convention
|
25
|
+
# Examples:
|
26
|
+
# to_association('group_id') => 'group'
|
27
|
+
# to_association('team_id') => 'team'
|
28
|
+
# to_association('created_by') => 'created_by'
|
29
|
+
#
|
30
|
+
def singular_association_name(foreign_key)
|
31
|
+
if foreign_key =~ foreign_key_regex
|
32
|
+
foreign_key[0...foreign_key.index(foreign_key_regex)]
|
33
|
+
else
|
34
|
+
foreign_key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a one_to_many association name based on the given foreign_key according to the naming convention
|
39
|
+
# Examples:
|
40
|
+
# to_association('group_id') => 'groups'
|
41
|
+
# to_association('team_id') => 'teams'
|
42
|
+
# to_association('created_by') => 'created_bies'
|
43
|
+
#
|
44
|
+
def plural_association_name(foreign_key)
|
45
|
+
pluralize singular_association_name(foreign_key)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a long many_to_many association name based on the given join table and foreign_keys according to the
|
49
|
+
# naming convention
|
50
|
+
# Examples:
|
51
|
+
# long_association_name('topic_assignments', 'group_id', 'groups', 'user_id') => 'topic_assignment_users'
|
52
|
+
# long_association_name('broadcasts', 'created_by', 'users', 'item_id') => 'broadcast_created_by_items'
|
53
|
+
#
|
54
|
+
def long_association_name(join_table, left_key, left_table, right_key)
|
55
|
+
left_key_qualifier = "_#{left_key}" if left_table != parent_table_for(left_key)
|
56
|
+
"#{singularize(join_table)}#{left_key_qualifier}_#{plural_association_name(right_key)}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns unique singular and plural combinations of the given table names that would be used in constructing a
|
60
|
+
# conventional join table name (assumes the given table_names are plural)
|
61
|
+
def conventional_join_table_names(left_table_name, right_table_name)
|
62
|
+
# TODO consider [left_table_name, pluralize(left_table_name), singularize(left_table_name)].uniq
|
63
|
+
left_names = [left_table_name, singularize(left_table_name)].uniq
|
64
|
+
right_names = [right_table_name, singularize(right_table_name)].uniq
|
65
|
+
|
66
|
+
(left_names.product(right_names) + right_names.product(left_names)).map { |t1, t2| "#{t1}_#{t2}" }
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module AssociationHelpers
|
4
|
+
|
5
|
+
abstract_method :conventional_name, :name_is_conventional?
|
6
|
+
|
7
|
+
def naming_conventions
|
8
|
+
Dart::NamingConventions.instance or fail "naming conventions have not been configured"
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_conventional_name!
|
12
|
+
set_name! conventional_name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module DirectAssociationHelpers
|
4
|
+
|
5
|
+
# Returns true if this association has a conventional foreign key pointing to the given table_name
|
6
|
+
def conventional_parent?(table_name)
|
7
|
+
parent_table == table_name && conventional_foreign_key?
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns true if this association has a conventional primary key on the parent table
|
11
|
+
def conventional_primary_key?
|
12
|
+
primary_key == naming_conventions.conventional_primary_key
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if this association has a conventionally named foreign key based on the parent table name
|
16
|
+
def conventional_foreign_key?
|
17
|
+
parent_table == naming_conventions.parent_table_for(foreign_key)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
class ForeignKeyFinder
|
4
|
+
|
5
|
+
# Returns a set of possible foreign keys based on columns in this relation matching the naming convention and
|
6
|
+
# reference a table name that is in the given schema
|
7
|
+
#
|
8
|
+
# @param [Relation] relation the relation to search
|
9
|
+
# @param [Schema] schema defines the set of referenceable tables
|
10
|
+
#
|
11
|
+
def foreign_keys_for(relation, schema)
|
12
|
+
naming_conventions = Dart::NamingConventions.instance
|
13
|
+
relation.column_names.map do |possible_foreign_key|
|
14
|
+
if parent_table = naming_conventions.parent_table_for(possible_foreign_key)
|
15
|
+
if schema.has_table?(parent_table)
|
16
|
+
ForeignKeyInfo.new(relation.table_name,
|
17
|
+
possible_foreign_key,
|
18
|
+
parent_table,
|
19
|
+
naming_conventions.conventional_primary_key)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end.compact
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module ManyToManyAssociationHelpers
|
4
|
+
include AssociationHelpers
|
5
|
+
|
6
|
+
# Returns the name of a referenced association according to the naming convention
|
7
|
+
#
|
8
|
+
# @return [String] the name of the referenced association
|
9
|
+
#
|
10
|
+
def conventional_name
|
11
|
+
# just return e.g. 'groups' if right_ass.foreign_key is group_id
|
12
|
+
naming_conventions.plural_association_name(right_ass.foreign_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def name_is_conventional?
|
16
|
+
name == associated_table
|
17
|
+
end
|
18
|
+
|
19
|
+
def name_and_right_foreign_key_are_conventional?
|
20
|
+
# name == associated_table == naming_conventions.parent_table_for(right_ass.foreign_key)
|
21
|
+
name_is_conventional? && right_foreign_key_is_conventional?
|
22
|
+
end
|
23
|
+
|
24
|
+
def left_foreign_key_is_conventional?
|
25
|
+
left_ass.conventional_foreign_key?
|
26
|
+
end
|
27
|
+
|
28
|
+
def right_foreign_key_is_conventional?
|
29
|
+
right_ass.conventional_foreign_key?
|
30
|
+
end
|
31
|
+
|
32
|
+
# forces long-form name to disambiguate from other joins to same association, where short-form name is the same
|
33
|
+
def disambiguate_name!
|
34
|
+
unless is_semi_conventional_join_table?
|
35
|
+
# add a foreign_key disambiguator when the key referencing me is unconventional
|
36
|
+
set_name! naming_conventions.long_association_name(join_table, left_ass.foreign_key, left_ass.parent_table, right_ass.foreign_key)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Returns true if the table joining t1 and t2 is semi-conventional, i.e. some variation of t1_t2 or t2_t1, for
|
43
|
+
# plural or singular forms of t1 and t2
|
44
|
+
def is_semi_conventional_join_table?
|
45
|
+
@semi_conventional_join_table ||= begin
|
46
|
+
naming_conventions.conventional_join_table_names(left_ass.parent_table, right_ass.parent_table).include?(join_table)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module ManyToOneAssociationHelpers
|
4
|
+
include AssociationHelpers
|
5
|
+
include DirectAssociationHelpers
|
6
|
+
|
7
|
+
# Returns the name of a referenced association according to the naming convention
|
8
|
+
#
|
9
|
+
# @return [String] the name of the referenced association
|
10
|
+
#
|
11
|
+
def conventional_name
|
12
|
+
naming_conventions.singular_association_name(foreign_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def name_is_conventional?
|
16
|
+
naming_conventions.plural_association_name(foreign_key) == associated_table
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module OneToManyAssociationHelpers
|
4
|
+
include AssociationHelpers
|
5
|
+
include DirectAssociationHelpers
|
6
|
+
|
7
|
+
# Returns the name of a referenced association according to the naming convention
|
8
|
+
#
|
9
|
+
# @return [String] the name of the referenced association
|
10
|
+
#
|
11
|
+
def conventional_name
|
12
|
+
if conventional_foreign_key?
|
13
|
+
associated_table
|
14
|
+
else
|
15
|
+
"#{naming_conventions.singular_association_name(foreign_key)}_#{child_table}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def name_is_conventional?
|
20
|
+
name == associated_table
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Dart
|
2
|
+
module NamingConventions
|
3
|
+
module RelationHelpers
|
4
|
+
|
5
|
+
# Returns true if this relation has a many_to_one association with the given table_name
|
6
|
+
# @param [String] table_name
|
7
|
+
def has_direct_conventional_parent?(table_name)
|
8
|
+
parent_associations.any? { |a| a.conventional_parent?(table_name) }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Finds joins with the same name and marks all but the conventional one as requiring a long name.
|
12
|
+
#
|
13
|
+
def disambiguate_conflicting_join_names!
|
14
|
+
# Corner case problem: if the schema changes and a conventional join table is added, then what was formerly
|
15
|
+
# foo many_to_many: :bars join_table: poop_stinks, will need to be (manually) changed to something like
|
16
|
+
# foo many_to_many: :poop_stink_bars, class: :Bar, join_table: poop_stinks
|
17
|
+
# and a request for foo.bars will now return bars from the foo_bars join table instead of the poop_stinks join
|
18
|
+
# table. Making all non-conventional joins have long names is worse since in most cases (80/20) the "real"
|
19
|
+
# join is just the existing unconventionally named table.
|
20
|
+
# many_to_many :bars seems better for a vast majority of joins that are not conventional.
|
21
|
+
|
22
|
+
duplicate_join_association_names.each do |_, join_associations|
|
23
|
+
join_associations.each { |join| join.disambiguate_name! }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a Hash of ass_name => number of copies for all duplicated join associations
|
28
|
+
def duplicate_join_association_names
|
29
|
+
join_associations.group_by(&:name).select { |name, join_associations| join_associations.count > 1 }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns pairs of many_to_one associations that could make up a join through this relation
|
33
|
+
# @return [Array<[String,String]]
|
34
|
+
def possible_join_pairs
|
35
|
+
parent_associations.combination(2).to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Dart
|
2
|
+
module Reflection
|
3
|
+
module ActiveRecordModel
|
4
|
+
class Resolver < OrmModelResolver
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def reflection_from(ass_name)
|
9
|
+
this_model_class.reflect_on_association(active_recordize(ass_name))
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_column?(col_name)
|
13
|
+
this_model_class.column_names.include?(col_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# = Active Record Associations
|
17
|
+
#
|
18
|
+
# This is the root class of all associations ('+ Foo' signifies an included module Foo):
|
19
|
+
#
|
20
|
+
# Association
|
21
|
+
# SingularAssociation
|
22
|
+
# HasOneAssociation + ForeignAssociation
|
23
|
+
# HasOneThroughAssociation + ThroughAssociation
|
24
|
+
# BelongsToAssociation
|
25
|
+
# BelongsToPolymorphicAssociation
|
26
|
+
# CollectionAssociation
|
27
|
+
# HasManyAssociation + ForeignAssociation
|
28
|
+
# HasManyThroughAssociation + ThroughAssociation
|
29
|
+
|
30
|
+
def build_association(ass_reflection)
|
31
|
+
# use class name because association_class is the actual class, not an instance of it, so === won't match it
|
32
|
+
ass = case ass_reflection.association_class.name.demodulize # is it possible to have activerecord without activesupport?
|
33
|
+
when 'HasOneAssociation'
|
34
|
+
OneToOneAssociation.new(child_table: ass_reflection.table_name,
|
35
|
+
foreign_key: ass_reflection.foreign_key,
|
36
|
+
parent_table: ass_reflection.active_record.table_name,
|
37
|
+
primary_key: ass_reflection.active_record_primary_key)
|
38
|
+
|
39
|
+
# TODO HasOneThroughAssociation
|
40
|
+
# when 'HasOneThroughAssociation'
|
41
|
+
|
42
|
+
when 'HasManyAssociation'
|
43
|
+
OneToManyAssociation.new(child_table: ass_reflection.table_name,
|
44
|
+
foreign_key: ass_reflection.foreign_key,
|
45
|
+
parent_table: ass_reflection.active_record.table_name,
|
46
|
+
primary_key: ass_reflection.active_record_primary_key)
|
47
|
+
|
48
|
+
when 'BelongsToAssociation'
|
49
|
+
# pk = ass_reflection.primary_key_column.name
|
50
|
+
ManyToOneAssociation.new(child_table: ass_reflection.active_record.table_name,
|
51
|
+
foreign_key: ass_reflection.foreign_key,
|
52
|
+
parent_table: ass_reflection.table_name,
|
53
|
+
primary_key: ass_reflection.association_primary_key)
|
54
|
+
|
55
|
+
when 'HasManyThroughAssociation'
|
56
|
+
join_ass = ass_reflection.through_reflection # has_many through:
|
57
|
+
left_ass = ManyToOneAssociation.new(child_table: join_ass.table_name,
|
58
|
+
foreign_key: join_ass.foreign_key,
|
59
|
+
parent_table: ass_reflection.active_record.table_name,
|
60
|
+
primary_key: ass_reflection.active_record_primary_key)
|
61
|
+
right_ass = ManyToOneAssociation.new(child_table: join_ass.table_name,
|
62
|
+
foreign_key: ass_reflection.association_foreign_key,
|
63
|
+
parent_table: ass_reflection.table_name,
|
64
|
+
primary_key: ass_reflection.association_primary_key)
|
65
|
+
ManyToManyAssociation.new(left_ass, right_ass)
|
66
|
+
|
67
|
+
# TODO BelongsToPolymorphicAssociation
|
68
|
+
# when 'BelongsToPolymorphicAssociation'
|
69
|
+
else
|
70
|
+
fail "don't yet know how to resolve associations of type '#{ass_reflection.association_class}' model=#{ass_reflection.klass} association=#{ass_reflection.name}"
|
71
|
+
end
|
72
|
+
|
73
|
+
ass.model_class = ass_reflection.klass
|
74
|
+
|
75
|
+
ass.scope = scope_for_association(ass_reflection)
|
76
|
+
|
77
|
+
ass.set_name!(ass_reflection.name)
|
78
|
+
ass
|
79
|
+
end
|
80
|
+
|
81
|
+
# TODO just put the association_reflection in the association and get all the SQL including the JOINs from
|
82
|
+
# the ORM
|
83
|
+
def scope_for_association(ass_reflection)
|
84
|
+
case ActiveRecord::VERSION::MAJOR
|
85
|
+
when 3
|
86
|
+
# in rails 3 ass_reflection.options contains everything except the where
|
87
|
+
non_where_scope = ass_reflection.options.slice(*QUERY_OPTIONS)
|
88
|
+
|
89
|
+
# in rails 3 where is in ass_reflection.options[:conditions]
|
90
|
+
conds = ass_reflection.options[:conditions]
|
91
|
+
conds = conds.call if conds.is_a?(Proc) # options[:conditions] could be a proc that needs to be evaluated
|
92
|
+
scope_hash_from(ass_reflection.klass.where(conds).to_sql).merge(non_where_scope)
|
93
|
+
|
94
|
+
# This one-liner almost works but adds an "AND fk IS NULL" that would need to be extracted out of the where
|
95
|
+
# sql_string = ass_reflection.klass.new.association(ass_name).send(:scoped).to_sql
|
96
|
+
|
97
|
+
when 4
|
98
|
+
# TODO use scope chain for through associations e.g. Broadcast.tracked_conversations
|
99
|
+
|
100
|
+
# With rails 4, it seems there is either a scope on the association reflection (which includes the target
|
101
|
+
# scope) or just a target scope on the association itself
|
102
|
+
sql_string = if scope = ass_reflection.scope
|
103
|
+
ass_reflection.klass.instance_exec(&scope).to_sql
|
104
|
+
else # just use the target scope
|
105
|
+
model_class.new.association(ass_name).send(:target_scope).to_sql
|
106
|
+
end
|
107
|
+
|
108
|
+
scope_hash_from(sql_string)
|
109
|
+
else
|
110
|
+
fail "ActiveRecord version #{ActiveRecord::VERSION::MAJOR}.x is not supported"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Converts the given identifier to the format needed by ActiveRecord
|
115
|
+
def active_recordize(id)
|
116
|
+
id.to_sym
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|