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 CHANGED
@@ -1 +1 @@
1
- 1.0.1
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.1"
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{2010-12-10}
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
- return (
209
- eval("#{attributes[attribute]} '#{value}'") ||
210
- eval("#{attributes[attribute]}.new('#{value}')") ||
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
@@ -1,6 +1,8 @@
1
1
  class Post < ActiveRecord::Base
2
2
  has_eav :through => :post_attribute do
3
3
  eav_attribute :author_name
4
- eav_attribute :author_email
4
+ eav_attribute :author_email
5
+ eav_attribute :published_on, Date
6
+ eav_attribute :last_update, Time
5
7
  end
6
8
  end
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: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 0
9
8
  - 1
10
- version: 1.0.1
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: 2010-12-10 00:00:00 +01:00
18
+ date: 2011-03-21 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency