has_eav 1.0.1 → 1.1.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/VERSION +1 -1
- data/has_eav.gemspec +2 -2
- data/lib/has_eav.rb +71 -56
- data/test/app/models/post.rb +3 -1
- data/test/test_has_eav.rb +40 -21
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
data/has_eav.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{has_eav}
|
8
|
-
s.version = "1.0
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Hartog C. de Mik"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-03-21}
|
13
13
|
s.description = %q{Put EAV behaviour on your ActiveRecord models}
|
14
14
|
s.email = %q{hcdm@matchvertise.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/has_eav.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ActsAs
|
3
3
|
module HasEav
|
4
|
-
def self.included(base)
|
5
|
-
base.extend ActiveRecord::ActsAs::HasEav::ClassMethods
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ActiveRecord::ActsAs::HasEav::ClassMethods
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module ClassMethods
|
9
9
|
# Specify that the ActiveModel is an EAV model
|
10
10
|
#
|
@@ -36,59 +36,59 @@ module ActiveRecord
|
|
36
36
|
"Eav Class cannot be nil. Specify a class using " +
|
37
37
|
"has_eav :through => :class"
|
38
38
|
) if klass.blank?
|
39
|
-
|
39
|
+
|
40
40
|
class_eval do
|
41
41
|
after_save :save_eav_attributes
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
@eav_class = klass.to_s.camelize.constantize
|
45
45
|
@eav_attributes = {}
|
46
|
-
|
46
|
+
|
47
47
|
yield if block_given?
|
48
|
-
|
48
|
+
|
49
49
|
send :include, ActiveRecord::ActsAs::HasEav::InstanceMethods
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
# Add an other attribute to the class list
|
53
53
|
def eav_attribute name, type = String
|
54
54
|
name = name.to_s if !name.is_a? String
|
55
|
-
|
55
|
+
|
56
56
|
self.class_eav_attributes[name] = type
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
# class accessor - when the superclass != AR::Base asume we are in STI
|
60
60
|
# mode
|
61
61
|
def class_eav_attributes # :nodoc:
|
62
62
|
superclass != ActiveRecord::Base ?
|
63
63
|
superclass.class_eav_attributes :
|
64
|
-
@eav_attributes
|
64
|
+
@eav_attributes
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
# class accessor - when the superclass != AR::Base asume we are in STI
|
68
68
|
# mode
|
69
69
|
def eav_class # :nodoc:
|
70
70
|
superclass != ActiveRecord::Base ? superclass.eav_class : @eav_class
|
71
|
-
end
|
71
|
+
end
|
72
72
|
end # /ClassMethods
|
73
|
-
|
73
|
+
|
74
74
|
module InstanceMethods
|
75
75
|
# get to the eav class
|
76
76
|
def eav_class
|
77
77
|
self.class.eav_class
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# get the class eav attributes
|
81
81
|
def class_eav_attributes
|
82
82
|
self.class.class_eav_attributes
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
# Override this to get some usable attributes
|
86
|
-
#
|
86
|
+
#
|
87
87
|
# Cowardly refusing to adhere to all
|
88
88
|
def instance_eav_attributes
|
89
89
|
[]
|
90
|
-
end
|
91
|
-
|
90
|
+
end
|
91
|
+
|
92
92
|
# override method missing, but only kick in when super fails with a
|
93
93
|
# NoMethodError
|
94
94
|
#
|
@@ -97,62 +97,62 @@ module ActiveRecord
|
|
97
97
|
rescue NoMethodError => e
|
98
98
|
method_name = method_symbol.to_s
|
99
99
|
attribute_name = method_name.gsub(/[\?=]$/, '')
|
100
|
-
|
100
|
+
|
101
101
|
raise e unless eav_attributes_list.include? attribute_name
|
102
|
-
|
102
|
+
|
103
103
|
attribute = self.eav_attributes.select { |a|
|
104
104
|
a.name == attribute_name
|
105
105
|
}.first
|
106
|
-
|
106
|
+
|
107
107
|
if method_name =~ /\=$/
|
108
108
|
value = args[0]
|
109
|
-
|
109
|
+
|
110
110
|
if attribute
|
111
111
|
if value
|
112
112
|
return attribute.send(:write_attribute, "value", value)
|
113
|
-
|
113
|
+
|
114
114
|
elsif value.nil?
|
115
115
|
return attribute.destroy
|
116
|
-
|
116
|
+
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
else
|
120
120
|
@eav_attributes << eav_class.new(
|
121
121
|
:name => attribute_name,
|
122
122
|
:value => "#{value}"
|
123
123
|
)
|
124
|
-
|
124
|
+
|
125
125
|
return cast_eav_value(value, attribute_name)
|
126
|
-
|
126
|
+
|
127
127
|
end
|
128
128
|
elsif method_name =~ /\?$/
|
129
129
|
return ( attribute and attribute.value == true ) ? true : false
|
130
|
-
|
130
|
+
|
131
131
|
else
|
132
|
-
return attribute ?
|
132
|
+
return attribute ?
|
133
133
|
cast_eav_value(attribute.value, attribute_name) :
|
134
134
|
nil
|
135
|
-
|
135
|
+
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
raise e
|
139
|
-
end
|
140
|
-
|
139
|
+
end
|
140
|
+
|
141
141
|
# override respond_to?
|
142
142
|
def respond_to? method_symbol, is_private=false
|
143
143
|
if super == false
|
144
144
|
method_name = method_symbol.to_s.gsub(/[\?=]$/, '')
|
145
145
|
return true if eav_attributes_list.include? method_name
|
146
|
-
|
146
|
+
|
147
147
|
false
|
148
148
|
else
|
149
149
|
true
|
150
150
|
end
|
151
|
-
end
|
152
|
-
|
151
|
+
end
|
152
|
+
|
153
153
|
# get all the eav attribute instances available for this model instance
|
154
154
|
#
|
155
|
-
# eg: if you model says 'has_eav :through => :post_attribute' these are
|
155
|
+
# eg: if you model says 'has_eav :through => :post_attribute' these are
|
156
156
|
# all PostAttribute's
|
157
157
|
#
|
158
158
|
def eav_attributes
|
@@ -160,60 +160,75 @@ module ActiveRecord
|
|
160
160
|
:conditions => { self_key => self.id }
|
161
161
|
)
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
# save the list of eav_attribute back to the database
|
165
165
|
def save_eav_attributes # :nodoc:
|
166
166
|
eav_attributes.select { |a| a.changed? }.each do |a|
|
167
167
|
if a.new_record?
|
168
168
|
a.send( :write_attribute, self_key, self.id )
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
171
|
a.save!
|
172
172
|
end
|
173
173
|
end
|
174
|
-
|
175
|
-
# override changed - if any of the eav_attributes has changed, the
|
174
|
+
|
175
|
+
# override changed - if any of the eav_attributes has changed, the
|
176
176
|
# object has changed.
|
177
177
|
#
|
178
178
|
def changed?
|
179
|
-
eav_attributes.each do |attribute|
|
179
|
+
eav_attributes.each do |attribute|
|
180
180
|
return true if ( attribute.changed? || attribute.new_record? )
|
181
181
|
end
|
182
|
-
|
182
|
+
|
183
183
|
super
|
184
184
|
end
|
185
|
-
|
185
|
+
|
186
186
|
# get a complete list of eav_attributes (class + instance)
|
187
187
|
def eav_attributes_list # :nodoc:
|
188
188
|
(
|
189
189
|
self.instance_eav_attributes + self.class_eav_attributes.keys
|
190
190
|
).collect { |attribute| attribute.to_s }.uniq
|
191
191
|
end
|
192
|
-
|
192
|
+
|
193
193
|
# get the key to my <3
|
194
194
|
def self_key # :nodoc:
|
195
195
|
klass = self.class
|
196
196
|
if klass.superclass != ActiveRecord::Base
|
197
197
|
klass = klass.superclass
|
198
198
|
end
|
199
|
-
|
199
|
+
|
200
200
|
"#{klass.name.underscore}_id".to_sym
|
201
201
|
end
|
202
|
-
|
202
|
+
|
203
203
|
# cast an eav value to it's desired class
|
204
204
|
def cast_eav_value value, attribute # :nodoc:
|
205
205
|
attributes = self.class_eav_attributes.stringify_keys
|
206
206
|
return value unless attributes.keys.include?(attribute)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
value
|
212
|
-
|
207
|
+
|
208
|
+
|
209
|
+
begin
|
210
|
+
# for core types [eg: String 'foo']
|
211
|
+
eval("#{attributes[attribute]} '#{value}'")
|
212
|
+
|
213
|
+
rescue
|
214
|
+
begin
|
215
|
+
# for BigDecimal [eg: BigDecimal.new("123.45")]
|
216
|
+
eval("#{attributes[attribute]}.new('#{value}')")
|
217
|
+
|
218
|
+
rescue
|
219
|
+
begin
|
220
|
+
# for date/time classes [eg: Date.parse("2011-03-20")]
|
221
|
+
eval("#{attributes[attribute]}.parse('#{value}')")
|
222
|
+
rescue
|
223
|
+
value
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
213
228
|
end
|
214
|
-
|
229
|
+
|
215
230
|
protected :save_eav_attributes, :self_key, :cast_eav_value
|
216
|
-
|
231
|
+
|
217
232
|
end # /InstanceMethods
|
218
233
|
end # /HasEav
|
219
234
|
end # /ActsAs
|
data/test/app/models/post.rb
CHANGED
data/test/test_has_eav.rb
CHANGED
@@ -1,54 +1,57 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class TestHasEav < Test::Unit::TestCase
|
4
|
+
#
|
5
|
+
# A model under EAV ...
|
6
|
+
#
|
4
7
|
should "be able to create new records" do
|
5
8
|
before = Post.count
|
6
9
|
Post.create( :title => "Hello world", :contents => "Lipsum" )
|
7
10
|
assert_equal before + 1, Post.count, "count has incremented by 1"
|
8
11
|
end
|
9
|
-
|
12
|
+
|
10
13
|
should "be able to write to an eav attribute" do
|
11
14
|
p = Post.last
|
12
15
|
assert_equal p.author_name = "hartog", "hartog", "eav attribute set"
|
13
16
|
end
|
14
|
-
|
17
|
+
|
15
18
|
should "propperly respond_to? eav_attributes" do
|
16
19
|
p = Post.new
|
17
20
|
assert p.respond_to?(:author_name), "responds to author name"
|
18
21
|
assert !p.respond_to?(:author_gender), "does not respond to gender"
|
19
22
|
end
|
20
|
-
|
23
|
+
|
21
24
|
should "propperly implement changed?" do
|
22
25
|
p = Post.last
|
23
26
|
assert !p.changed?, "is not changed"
|
24
|
-
|
27
|
+
|
25
28
|
p = Post.last
|
26
29
|
p.author_name = rand 100
|
27
|
-
|
30
|
+
|
28
31
|
assert p.changed?, "is changed only by eav attribute"
|
29
|
-
|
32
|
+
|
30
33
|
p = Post.last
|
31
34
|
p.title = "Bye World"
|
32
35
|
assert p.changed?, "is changed on real attribute"
|
33
36
|
end
|
34
|
-
|
35
|
-
should "save PostAttribute's" do
|
37
|
+
|
38
|
+
should "save PostAttribute's on Post.create" do
|
36
39
|
before = PostAttribute.count
|
37
40
|
p = Post.create( :title => "Hello World", :contents => "Lipsum" )
|
38
41
|
p.author_name = "hartog"
|
39
42
|
p.save!
|
40
|
-
|
43
|
+
|
41
44
|
assert_equal before + 1, PostAttribute.count, "count has incremented by 1"
|
42
45
|
end
|
43
|
-
|
46
|
+
|
44
47
|
should "save ProductAttribute's on Product.create" do
|
45
48
|
before = ProductAttribute.count
|
46
49
|
p = Product.create(
|
47
50
|
:name => "test",
|
48
51
|
:description => "spiffy. new.",
|
49
52
|
:price => 12.95
|
50
|
-
)
|
51
|
-
|
53
|
+
)
|
54
|
+
|
52
55
|
assert_equal before + 1, ProductAttribute.count, "count has incremented by 1"
|
53
56
|
end
|
54
57
|
|
@@ -56,41 +59,57 @@ class TestHasEav < Test::Unit::TestCase
|
|
56
59
|
p = Product.create( :name => "test", :description => "test" )
|
57
60
|
p.price = 12.95
|
58
61
|
p.save!
|
59
|
-
|
60
|
-
p = Product.last
|
62
|
+
|
63
|
+
p = Product.last
|
61
64
|
assert p.price.is_a?(Float), "casted"
|
62
65
|
assert p.price == 12.95, "same"
|
63
66
|
end
|
64
|
-
|
67
|
+
|
65
68
|
should "cast values with new" do
|
66
69
|
test_val ="13.24458724857"
|
67
70
|
p = Product.create(
|
68
71
|
:name => "test", :description => "test", :cost_price => test_val
|
69
72
|
)
|
70
|
-
|
71
|
-
p = Product.last
|
73
|
+
|
74
|
+
p = Product.last
|
72
75
|
assert p.cost_price.is_a?(BigDecimal), "casted"
|
73
76
|
assert p.cost_price == BigDecimal.new(test_val), "same"
|
74
77
|
end
|
75
|
-
|
78
|
+
|
76
79
|
should "not find by eav attribute" do
|
77
80
|
p = Post.create(
|
78
81
|
:title => "title", :contents => "lorem ipsum", :author_name => "hartog"
|
79
82
|
)
|
80
|
-
|
83
|
+
|
81
84
|
post = nil
|
82
85
|
begin
|
83
86
|
post = Post.find_by_author_name "hartog"
|
84
87
|
rescue
|
85
88
|
post = "failed"
|
86
89
|
end
|
87
|
-
|
90
|
+
|
88
91
|
assert_equal "failed", post, "has failed"
|
89
92
|
end
|
90
|
-
|
93
|
+
|
91
94
|
should "work with STI" do
|
92
95
|
p = SpecialPost.create(
|
93
96
|
:title => "title", :contents => "lorem ipsum", :author_name => "hartog"
|
94
97
|
)
|
95
98
|
end
|
99
|
+
|
100
|
+
should "be able to handle Date casting" do
|
101
|
+
p = Post.last
|
102
|
+
p.published_on = Date.yesterday
|
103
|
+
|
104
|
+
assert_equal Date.yesterday, p.published_on, "Date comparisson"
|
105
|
+
end
|
106
|
+
|
107
|
+
should "be able to handle Time casting" do
|
108
|
+
t = Time.now
|
109
|
+
|
110
|
+
p = Post.last
|
111
|
+
p.last_update = t
|
112
|
+
|
113
|
+
assert_equal t.to_i, p.last_update.to_i, "Time comparisson"
|
114
|
+
end
|
96
115
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_eav
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Hartog C. de Mik
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-03-21 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|