factory_boy 1.0.5 → 2.0.0
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.
- data/README.rdoc +68 -31
- data/lib/blank_slate.rb +3 -0
- data/lib/plant.rb +37 -46
- data/lib/query.rb +44 -0
- data/lib/reflection.rb +40 -0
- data/lib/selector.rb +181 -0
- data/lib/setup.rb +13 -22
- data/lib/stubber.rb +164 -21
- data/test/Rakefile.rb +11 -0
- data/test/app/models/address.rb +9 -0
- data/test/app/models/customer.rb +3 -0
- data/test/app/models/profile.rb +2 -0
- data/test/app/models/user.rb +7 -0
- data/test/databases.rake.rb +513 -0
- data/test/db/migrate/20101230223546_create_users.rb.rb +14 -0
- data/test/db/migrate/20101230223547_create_profiles.rb +15 -0
- data/test/db/migrate/20101230223548_create_customers.rb +11 -0
- data/test/db/migrate/20101230223549_create_addresses.rb +13 -0
- data/test/db/schema.rb +41 -0
- data/test/help_test.rb +15 -5
- data/test/plants.rb +5 -5
- data/test/test_basic_queries.rb +36 -0
- data/test/test_plant_definition.rb +129 -0
- data/test/test_plants_ids.rb +16 -0
- data/test/test_queries_on_has_many_association.rb +51 -0
- data/test/test_queries_on_has_one_association.rb +45 -0
- data/test/test_queries_on_model_attributes.rb +59 -0
- data/test/test_queries_with_like.rb +22 -0
- data/test/test_queries_with_limit.rb +28 -0
- data/test/test_queries_with_named_scope.rb +18 -0
- data/test/test_queries_with_order.rb +17 -0
- data/test/test_queries_with_ranges.rb +21 -0
- data/test/test_selector_condition.rb +26 -0
- data/test/test_stubbing.rb +43 -0
- metadata +60 -22
- data/test/models/adress.rb +0 -12
- data/test/models/customer.rb +0 -7
- data/test/models/profile.rb +0 -8
- data/test/models/user.rb +0 -8
- data/test/plant_tests.rb +0 -115
- data/test/test_plant.rb +0 -7
- data/test/test_plant_with_active_support.rb +0 -8
data/README.rdoc
CHANGED
@@ -1,29 +1,64 @@
|
|
1
1
|
== Overview
|
2
|
-
|
3
|
-
|
4
|
-
ActiveRecord::Base
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
Factory Boy aims to avoid slow unit tests due to usage of create/find fixtures in database, with factory_girl for example.
|
3
|
+
Factory Boy can be used as factory_girl except that factories are not created in database.
|
4
|
+
ActiveRecord::Base finders method is stubbed to return fixtures (plants) you have instanciate.
|
5
|
+
|
6
|
+
Now, Factory Boy 2 handle stub of Active Record (3+) queries.
|
7
|
+
This means, the fixtures(plants) created with factory boy are retrieved via a AR queries(and only with AR new queries) of your models.
|
8
|
+
It does not pretend to stub 100% of all queries, but the coverage can be estimated at about 80%-90% of useful queries.
|
9
|
+
|
10
|
+
|
11
|
+
Active Record is stubbed only when at least one Plant is created in a test.
|
12
|
+
After each test everything is unstubbed.
|
13
|
+
That means, if you have a case where a particular(complex) query is executed but not right stubbed with factory boy you can test using fixtures in databases(with factory girl or just model.create ..), skipping factory boy process.
|
14
|
+
|
15
|
+
Tested with Active Record 3.0.1
|
16
|
+
Tests are suppose to use ActiveSupport::TestCase
|
8
17
|
|
9
18
|
See some examples below.
|
10
|
-
You
|
19
|
+
You should see unit tests to inspect tested stubbed queries!
|
11
20
|
|
12
|
-
== Install
|
13
21
|
|
14
|
-
|
22
|
+
== Queries it supposes to handle
|
23
|
+
|
24
|
+
- where clauses on attributes and associations
|
25
|
+
- chained where clauses
|
26
|
+
- like sql predicate
|
27
|
+
- limit, offset
|
28
|
+
- order (with only one order clause)
|
29
|
+
- ranges (ie where(:age => (20..30)))
|
30
|
+
- IS NULL and IS NOT NULL sql predicates
|
31
|
+
|
32
|
+
The better way to see queries handled is to see all unit tests.
|
33
|
+
|
34
|
+
== Queries NOT handled
|
35
|
+
|
36
|
+
- Queries with explicit sql string(find_by_sql("..."))
|
37
|
+
- #order with more than one order clause (ie .order(name asc, age desc))
|
38
|
+
- IS and IS NOT with other operand than NULL
|
39
|
+
|
40
|
+
== Ids
|
15
41
|
|
42
|
+
Each plant fixture has now an (unique) id.
|
16
43
|
|
17
|
-
==
|
44
|
+
== Usage
|
18
45
|
|
19
|
-
Define your Plants (
|
46
|
+
Define your Plants (~ Factories if factory_girl) in test/plants.rb
|
20
47
|
|
21
48
|
Example :
|
22
49
|
|
50
|
+
Plant.define :address do |address|
|
51
|
+
address.number = 12
|
52
|
+
address.street = "rue de Brest"
|
53
|
+
end
|
54
|
+
|
23
55
|
Plant.define :user do |user|
|
24
56
|
user.name="Bart"
|
25
57
|
user.age=800
|
58
|
+
user.addresses = [Plant(:address)]
|
26
59
|
end
|
60
|
+
|
61
|
+
|
27
62
|
|
28
63
|
Get it with :
|
29
64
|
|
@@ -34,10 +69,16 @@ Get it with :
|
|
34
69
|
|
35
70
|
|
36
71
|
def test___1
|
37
|
-
|
72
|
+
address = Plant(:address, :street => 'rue des Lilas')
|
73
|
+
user = Plant(:user, :name => 'Joe', :addresses => [address])
|
74
|
+
|
38
75
|
assert_equal user, User.find #OK
|
39
76
|
assert_equal user, User.find(:first) #OK
|
40
77
|
assert_equal user, User.find(:last) #OK
|
78
|
+
assert_equal [user], User.where(:name => 'Joe') #OK
|
79
|
+
assert_equal [user], User.where("name = 'Joe' and addresses.street = 'rue des Lilas'").joins(':addresses) #OK
|
80
|
+
assert_equal [address], user.addresses.where(:street => 'rue des Lilas') #OK
|
81
|
+
|
41
82
|
end
|
42
83
|
|
43
84
|
|
@@ -51,6 +92,7 @@ Get it with :
|
|
51
92
|
|
52
93
|
user = Plant(:user, :name => "Marie", :age => age)
|
53
94
|
|
95
|
+
|
54
96
|
== Specification of the class of the fixture definition
|
55
97
|
|
56
98
|
Plant.define :admin, :class => User do |user|
|
@@ -66,16 +108,16 @@ Assign fixtures to association in definition of plant :
|
|
66
108
|
profile.password = "BREIZH!"
|
67
109
|
end
|
68
110
|
|
69
|
-
Plant.define :
|
70
|
-
|
71
|
-
|
111
|
+
Plant.define :address do |address|
|
112
|
+
address.number = 12
|
113
|
+
address.street = "rue de Brest"
|
72
114
|
end
|
73
115
|
|
74
116
|
Plant.define :user do |user|
|
75
117
|
user.name = "Bart"
|
76
118
|
user.age = 800
|
77
|
-
user.profile = Plant
|
78
|
-
user.adresses = [Plant
|
119
|
+
user.profile = Plant(:profile)
|
120
|
+
user.adresses = [Plant(:address)]
|
79
121
|
end
|
80
122
|
|
81
123
|
|
@@ -86,7 +128,7 @@ If you want to use the value of another attribute in definition, do like that :
|
|
86
128
|
|
87
129
|
Plant.define :user do |user|
|
88
130
|
user.name = "Marie"
|
89
|
-
user.adresses = [
|
131
|
+
user.adresses = [Plant(:address, :street => "Rue de #{user.name}")]
|
90
132
|
end
|
91
133
|
|
92
134
|
|
@@ -102,26 +144,21 @@ As with factory_girl you are able to use sequences, like that :
|
|
102
144
|
Plant.next(:email) # => "incognito2@kantena.com"
|
103
145
|
|
104
146
|
|
105
|
-
== Dependencies
|
106
|
-
|
107
|
-
No dependency.
|
108
|
-
Doesn't work, for now, with Active Support 3.0 (In development)
|
109
147
|
|
110
148
|
== In Development
|
111
149
|
|
112
|
-
- Stubs
|
113
|
-
- Plant(:xx).clear
|
114
|
-
- Provide an id to each Plant
|
115
|
-
- When assign instance to an association(ie profile of user), set foreign_key id (ie profile_id of user)
|
116
|
-
- To work with Rails 3 (Active Support 3)
|
150
|
+
- Stubs aggregations methods in queries(sum, count ...)
|
117
151
|
|
118
|
-
== Change Log
|
119
152
|
|
120
|
-
|
121
|
-
|
153
|
+
== Install
|
154
|
+
|
155
|
+
gem install factory_boy
|
156
|
+
|
157
|
+
|
158
|
+
== Change Log
|
122
159
|
|
123
|
-
== Notes
|
124
160
|
|
125
161
|
|
162
|
+
== Issues
|
126
163
|
|
127
164
|
<b>Report Bugs here , on github</b>
|
data/lib/blank_slate.rb
ADDED
data/lib/plant.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'active_support/inflector'
|
3
1
|
require 'stubber'
|
2
|
+
require 'selector'
|
4
3
|
require 'setup'
|
4
|
+
require 'query'
|
5
|
+
require 'reflection'
|
5
6
|
|
6
7
|
module Plant
|
7
8
|
|
@@ -9,83 +10,73 @@ module Plant
|
|
9
10
|
@@pool = {}
|
10
11
|
@@map = {}
|
11
12
|
@@sequences = {}
|
12
|
-
|
13
|
-
|
13
|
+
@@id = 0
|
14
|
+
|
14
15
|
def self.define symbol, args={}
|
15
16
|
klass = args[:class] || symbol.to_s.camelize.constantize
|
16
|
-
|
17
|
-
yield
|
18
|
-
add_plant(klass,
|
19
|
-
add_plant(symbol,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def self.stubs klass
|
24
|
-
unless @@stubbed.include?(klass)
|
25
|
-
Plant::Stubber.stubs_find(klass)
|
26
|
-
@@stubbed << klass
|
27
|
-
end
|
17
|
+
definition = klass.new
|
18
|
+
yield definition if block_given?
|
19
|
+
add_plant(klass, definition)
|
20
|
+
add_plant(symbol, definition) if args[:class]
|
21
|
+
Plant::Reflection.reflect(klass)
|
22
|
+
Plant::Stubber.stubs
|
28
23
|
end
|
29
|
-
|
30
|
-
def self.unstub_find_for_each_class
|
31
|
-
@@stubbed.each {|klass| Plant::Stubber.unstubs_find_for(klass)}
|
32
|
-
@@stubbed = []
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.all
|
36
|
-
@@pool
|
37
|
-
end
|
38
|
-
|
24
|
+
|
39
25
|
def self.add_plant klass, instance
|
40
26
|
@@plants[klass] = instance
|
41
27
|
end
|
42
|
-
|
28
|
+
|
43
29
|
def self.plants
|
44
30
|
@@plants
|
45
31
|
end
|
46
32
|
|
47
|
-
def self.
|
33
|
+
def self.all
|
34
|
+
@@pool
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.reload
|
48
38
|
load "#{RAILS_ROOT}/test/plants.rb" if Plant.plants.empty?
|
49
39
|
end
|
50
|
-
|
40
|
+
|
51
41
|
def self.pool symbol
|
52
|
-
|
53
|
-
object =
|
42
|
+
definition = plants[symbol] || plants[symbol.to_s.camelize.constantize]
|
43
|
+
object = Plant::Reflection.clone(definition,@@id += 1)
|
54
44
|
yield object if block_given?
|
55
|
-
@@pool[
|
56
|
-
@@pool[
|
45
|
+
@@pool[definition.class] ||= []
|
46
|
+
@@pool[definition.class] << object
|
57
47
|
object
|
58
48
|
end
|
59
|
-
|
49
|
+
|
60
50
|
def self.destroy
|
61
51
|
@@pool = {}
|
62
52
|
@@plants = {}
|
63
53
|
@@map = {}
|
64
54
|
@@sequences = {}
|
65
|
-
|
55
|
+
@@id = 0
|
66
56
|
end
|
67
|
-
|
57
|
+
|
68
58
|
def self.sequence symbol, &proc
|
69
59
|
@@sequences[symbol] = {:lambda => proc, :index => 0}
|
70
60
|
end
|
71
|
-
|
61
|
+
|
72
62
|
def self.next symbol
|
73
63
|
@@sequences[symbol][:lambda].call(@@sequences[symbol][:index] += 1)
|
74
64
|
end
|
75
65
|
|
76
|
-
def self.association
|
77
|
-
Plant.
|
66
|
+
def self.set_foreign_keys object, association, value
|
67
|
+
fk_setter = lambda {|value| value.send(Plant::Reflection.foreign_key(object, association) + '=', object.id)}
|
68
|
+
|
69
|
+
case
|
70
|
+
when Plant::Reflection.has_many_association?(object.class, association) then value.each {|v| fk_setter.call(v)}
|
71
|
+
when value && Plant::Reflection.has_one_association?(object.class, association) then fk_setter.call(value)
|
72
|
+
end
|
78
73
|
end
|
79
|
-
|
74
|
+
|
80
75
|
end
|
81
76
|
|
82
77
|
def Plant symbol, args={}
|
83
|
-
Plant.reload
|
78
|
+
Plant.reload
|
84
79
|
Plant.pool(symbol) do |instance|
|
85
|
-
args.each {|key, value| instance.send(key.to_s + '=', value)}
|
80
|
+
args.each {|key, value| Plant.set_foreign_keys(instance, key, value); instance.send(key.to_s + '=', value)}
|
86
81
|
end
|
87
82
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
data/lib/query.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Plant
|
2
|
+
module Query
|
3
|
+
|
4
|
+
@@wheres = nil
|
5
|
+
|
6
|
+
def self.wheres= wheres
|
7
|
+
@@wheres = wheres
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.wheres
|
11
|
+
@@wheres
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find_all klass
|
15
|
+
Plant.all[klass] || []
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find_by_ids klass, ids
|
19
|
+
plants = Plant.all[klass].select{|plant| ids.include?(plant.id)}
|
20
|
+
return plants.first if plants.size == 1
|
21
|
+
plants
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.select klass
|
25
|
+
Plant::Selector.new(:klass => klass, :wheres => @@wheres, :plants => Plant.all[klass].to_a).select
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.order objects, args
|
29
|
+
attribute, order = args.split(" ")
|
30
|
+
objects.sort {|x, y| (x,y = y,x if order == 'desc'); x.send(attribute.to_sym) <=> y.send(attribute.to_sym)}
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.limit objects, limit_value
|
34
|
+
@@objects = objects
|
35
|
+
@@limit = limit_value
|
36
|
+
objects.first(limit_value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.offset objects, offset_value
|
40
|
+
@@objects[offset_value..-1].first(@@limit)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/reflection.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Plant
|
2
|
+
module Reflection
|
3
|
+
@@reflections = {}
|
4
|
+
|
5
|
+
def self.reflect klass
|
6
|
+
return if @@reflections[klass]
|
7
|
+
associations = lambda {|klass, macro| klass.reflect_on_all_associations(macro).map{|association| association.name} || []}
|
8
|
+
|
9
|
+
reflection = @@reflections[klass] = Hash.new
|
10
|
+
[:has_many, :has_one, :belongs_to].each {|relation| reflection[relation] = associations.call(klass, relation)}
|
11
|
+
reflection[:attributes] = klass.new.attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.clone object, id
|
15
|
+
clone = object.class.new
|
16
|
+
cloner = Proc.new {|attribute| clone.send(attribute.to_s + "=", object.send(attribute))}
|
17
|
+
reflection = @@reflections[object.class]
|
18
|
+
|
19
|
+
clone.id = id
|
20
|
+
reflection[:attributes].keys.each(&cloner)
|
21
|
+
[:has_many, :has_one, :belongs_to].each {|relation| reflection[relation].each(&cloner)}
|
22
|
+
|
23
|
+
clone
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.has_many_association? klass, method
|
27
|
+
@@reflections[klass][:has_many].include?(method)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.has_one_association? klass, method
|
31
|
+
@@reflections[klass][:has_one].include?(method)
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.foreign_key object, association
|
36
|
+
object.class.name.underscore + '_id'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/selector.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'blank_slate'
|
2
|
+
|
3
|
+
module Plant
|
4
|
+
|
5
|
+
class Selector
|
6
|
+
|
7
|
+
class Condition
|
8
|
+
|
9
|
+
def initialize wheres, klass
|
10
|
+
@wheres = wheres
|
11
|
+
@klass = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_ruby
|
15
|
+
@wheres.inject("") do |sql, where|
|
16
|
+
copy = where.clone
|
17
|
+
|
18
|
+
copy.gsub!(/\s=\s/, " == ")
|
19
|
+
copy.gsub!('"','')
|
20
|
+
|
21
|
+
copy.gsub!(/\s<>\s/, " != ")
|
22
|
+
copy.gsub!('"','')
|
23
|
+
|
24
|
+
copy.match(/(\sLIKE\s*)'/i)
|
25
|
+
copy.gsub!($1,'.match ') if $1
|
26
|
+
|
27
|
+
copy.match(/(\sIN\s*)\(/i)
|
28
|
+
copy.gsub!($1,'.included_in?') if $1
|
29
|
+
|
30
|
+
copy.match(/(\sBETWEEN\s*)((\d*)\sAND\s(\d*))/i)
|
31
|
+
between, range = $1, ($3.to_i .. $4.to_i)
|
32
|
+
copy.gsub!($2, '') if $2
|
33
|
+
copy.gsub!(between, ".included_in?(#{range}) ") if between
|
34
|
+
|
35
|
+
copy.match(/(\sIS\sNULL)/i)
|
36
|
+
copy.gsub!($1,'== nil') if $1
|
37
|
+
|
38
|
+
copy.match(/(\sIS\sNOT\sNULL)/i)
|
39
|
+
copy.gsub!($1,'!= nil') if $1
|
40
|
+
|
41
|
+
|
42
|
+
sql << (sql.blank? ? "" : " and ") + copy
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ArrayCollection < BlankSlate
|
48
|
+
|
49
|
+
def initialize collection
|
50
|
+
@collection = collection
|
51
|
+
end
|
52
|
+
|
53
|
+
def compare operator, operand
|
54
|
+
collection = @collection.dup
|
55
|
+
collection.map{|object| Attribute.new(object, @method)}.any? {|object| object.send(operator, operand) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing method, *args, &block
|
59
|
+
if (@method)
|
60
|
+
return compare(method, *args)
|
61
|
+
end
|
62
|
+
@method = method
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Association
|
68
|
+
|
69
|
+
def initialize(association)
|
70
|
+
@association = association
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing method, *args, &block
|
74
|
+
return nil unless @association
|
75
|
+
Attribute.new(@association, method)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class Attribute
|
81
|
+
|
82
|
+
def initialize reference, method=nil
|
83
|
+
@reference = reference
|
84
|
+
@method = method
|
85
|
+
end
|
86
|
+
|
87
|
+
def compare operator, operand
|
88
|
+
value = @reference.send(@method)
|
89
|
+
operand = type_cast(operand, value)
|
90
|
+
value.send(operator, operand)
|
91
|
+
end
|
92
|
+
|
93
|
+
def type_cast operand, value
|
94
|
+
case value
|
95
|
+
when TrueClass, FalseClass : (operand == 't' || operand == '1')
|
96
|
+
else operand
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def == operand
|
101
|
+
compare(:==, operand)
|
102
|
+
end
|
103
|
+
|
104
|
+
def > operand
|
105
|
+
compare(:>, operand)
|
106
|
+
end
|
107
|
+
|
108
|
+
def < operand
|
109
|
+
compare(:<, operand)
|
110
|
+
end
|
111
|
+
|
112
|
+
def >= operand
|
113
|
+
compare(:>=, operand)
|
114
|
+
end
|
115
|
+
|
116
|
+
def <= operand
|
117
|
+
compare(:<=, operand)
|
118
|
+
end
|
119
|
+
|
120
|
+
def match operand
|
121
|
+
operand.gsub!(/\%/,'(.*)')
|
122
|
+
operand = '^' + operand + '$'
|
123
|
+
@reference.send(@method).match(operand)
|
124
|
+
end
|
125
|
+
|
126
|
+
def included_in? *operand
|
127
|
+
range = operand.first if operand.first.is_a?(Range)
|
128
|
+
return range.include?(@reference.send(@method)) if range
|
129
|
+
operand.include?(@reference.send(@method))
|
130
|
+
end
|
131
|
+
|
132
|
+
def method_missing method, *args, &block
|
133
|
+
@method = method
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
def initialize opt={}
|
140
|
+
@wheres = opt[:wheres]
|
141
|
+
@plants = opt[:plants]
|
142
|
+
@klass = opt[:klass]
|
143
|
+
end
|
144
|
+
|
145
|
+
def select
|
146
|
+
condition = Condition.new(@wheres, @klass)
|
147
|
+
|
148
|
+
Plant::Stubber.stubs_associations_collections
|
149
|
+
Plant::Stubber.stubs_attribute_methods
|
150
|
+
objects = @plants.select {|object| @binding = binding(); eval("#{condition.to_ruby}")}
|
151
|
+
Plant::Stubber.unstubs_associations_collections
|
152
|
+
Plant::Stubber.unstubs_attribute_methods
|
153
|
+
|
154
|
+
objects
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def method_missing method, *args, &block
|
160
|
+
case
|
161
|
+
when has_one_association?(method) then Association.new(eval("object.#{method.to_s[0..-2]}", @binding))
|
162
|
+
when has_many_association?(method) then ArrayCollection.new(eval("object.#{method}", @binding))
|
163
|
+
when self_reference?(method) then Attribute.new(eval("object", @binding))
|
164
|
+
else Attribute.new(eval("object", @binding), method)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def self_reference? method
|
169
|
+
@klass.name.downcase == method.to_s[0..-2]
|
170
|
+
end
|
171
|
+
|
172
|
+
def has_one_association? method
|
173
|
+
@klass.new.respond_to?(method.to_s[0..-2])
|
174
|
+
end
|
175
|
+
|
176
|
+
def has_many_association? method
|
177
|
+
Plant::Reflection.has_many_association?(@klass, method)
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|