dart 0.0.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 +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
|