datts_right 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.textile +27 -27
- data/VERSION +1 -1
- data/datts_right.gemspec +20 -4
- data/lib/datts_right.rb +31 -67
- data/lib/datts_right/{datt.rb → dynamic_attribute.rb} +1 -1
- data/lib/datts_right/instance_methods.rb +102 -105
- data/lib/datts_right/page.rb +1 -1
- data/spec/datt_spec.rb +6 -6
- data/spec/datts_right_spec.rb +132 -77
- data/spec/spec_helper.rb +3 -3
- metadata +94 -6
- data/spec/has_datts_migration_generator_spec.rb +0 -13
data/Gemfile.lock
CHANGED
data/README.textile
CHANGED
@@ -11,8 +11,8 @@ h2. Why make this?
|
|
11
11
|
# I needed to allow users to create their own dynamic attributes on certain records. I was already running PostgreSQL. My database and code was designed largely on relational structures. I was thinking of moving to MongoDB but saw that I'd pretty much have to rewrite all the models. Creating this gem was the first step to using dynamic attributes on my app.
|
12
12
|
# The "available":http://codaset.com/joelmoss/dynamic-attributes "plugins":https://github.com/moiristo/dynamic_attributes out there that did something like this stuffed the dynamic attributes in a column in the model that had dynamic attributes. Because of this, you:
|
13
13
|
** Could not order things by dynamic columns straight with SQL
|
14
|
-
** Could not (as far as I know) chain scopes, like so: @MyModel.where(:name_which_is_a_real_attribute => "Joe").
|
15
|
-
** Could not find by dynamic attribute straight with SQL, like so: @MyModel.
|
14
|
+
** Could not (as far as I know) chain scopes, like so: @MyModel.where(:name_which_is_a_real_attribute => "Joe").where_dynamic_attribute(:phone_which_is_dynamic => "23218793")@
|
15
|
+
** Could not find by dynamic attribute straight with SQL, like so: @MyModel.find_by_dynamic_attribute_phone_which_is_dynamic("2398291308")@
|
16
16
|
** Had to add migrations to each model that you wanted to have dynamic attributes
|
17
17
|
|
18
18
|
h2. Installation
|
@@ -22,7 +22,7 @@ Create a migration:
|
|
22
22
|
<pre>
|
23
23
|
class CreateDatts < ActiveRecord::Migration
|
24
24
|
def self.up
|
25
|
-
create_table(:
|
25
|
+
create_table(:dynamic_attributes) do |t|
|
26
26
|
t.string :name, :null => false
|
27
27
|
t.string :attr_key, :null => false
|
28
28
|
t.string :object_type, :null => false
|
@@ -33,17 +33,17 @@ class CreateDatts < ActiveRecord::Migration
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
add_index "
|
37
|
-
add_index "
|
38
|
-
add_index "
|
36
|
+
add_index "dynamic_attributes", ["attributable_id"], :name => "index_dynamic_attributes_on_attributable_id"
|
37
|
+
add_index "dynamic_attributes", ["attributable_type"], :name => "index_dynamic_attributes_on_attributable_type"
|
38
|
+
add_index "dynamic_attributes", ["attr_key"], :name => "index_dynamic_attributes_on_attr_key"
|
39
39
|
end
|
40
40
|
|
41
41
|
def self.down
|
42
|
-
remove_index "
|
43
|
-
remove_index "
|
44
|
-
remove_index "
|
42
|
+
remove_index "dynamic_attributes", :name => "index_dynamic_attributes_on_attr_key"
|
43
|
+
remove_index "dynamic_attributes", :name => "index_dynamic_attributes_on_attributable_type"
|
44
|
+
remove_index "dynamic_attributes", :name => "index_dynamic_attributes_on_attributable_id"
|
45
45
|
|
46
|
-
drop_table(:
|
46
|
+
drop_table(:dynamic_attributes)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
</pre>
|
@@ -60,7 +60,7 @@ Add this to your Gemfile:
|
|
60
60
|
|
61
61
|
Add this to the model that you want to have dynamic attributes:
|
62
62
|
|
63
|
-
@
|
63
|
+
@has_dynamic_attributes@
|
64
64
|
|
65
65
|
h2. Usage
|
66
66
|
|
@@ -96,28 +96,28 @@ Returns true or false
|
|
96
96
|
|
97
97
|
h3. Dynamic find
|
98
98
|
|
99
|
-
pre. User.
|
99
|
+
pre. User.find_by_dynamic_attribute_age(240) # returns the first user with that age
|
100
100
|
|
101
101
|
You can also use:
|
102
102
|
|
103
|
-
pre.
|
104
|
-
|
103
|
+
pre. find_all_by_dynamic_attribute
|
104
|
+
find_last_by_dynamic_attribute
|
105
105
|
|
106
106
|
h2. Stuff that make things faster
|
107
107
|
|
108
108
|
The dynamic attributes are only actually saved when save is called on the record that has them.
|
109
109
|
|
110
|
-
pre. @user.add_dynamic_attribute(:gunslinger, "boolean") # a column is already written on the "
|
111
|
-
@user.add_dynamic_attribute(:middle_name, "string") # a column is already written on the "
|
110
|
+
pre. @user.add_dynamic_attribute(:gunslinger, "boolean") # a column is already written on the "dynamic_attributes" table, with a null value.
|
111
|
+
@user.add_dynamic_attribute(:middle_name, "string") # a column is already written on the "dynamic_attributes" table, with a null value.
|
112
112
|
@user.gunslinger = true # saves into memory
|
113
113
|
@user.middle_name = "Unknown" # saves into memory
|
114
|
-
@user.save # the respective dynamic attribute columns are written in the
|
114
|
+
@user.save # the respective dynamic attribute columns are written in the dynamic_attributes table
|
115
115
|
|
116
|
-
h2. Structure of the
|
116
|
+
h2. Structure of the dynamic_attributes table
|
117
117
|
|
118
|
-
Although you probably shouldn't work with it directly, the
|
118
|
+
Although you probably shouldn't work with it directly, the dynamic_attributes table looks like this:
|
119
119
|
|
120
|
-
pre. ActiveRecord::Base.connection.create_table(:
|
120
|
+
pre. ActiveRecord::Base.connection.create_table(:dynamic_attributes) do |t|
|
121
121
|
t.string :name, :null => false
|
122
122
|
t.string :attr_key, :null => false
|
123
123
|
t.string :object_type, :null => false
|
@@ -128,10 +128,10 @@ pre. ActiveRecord::Base.connection.create_table(:datts) do |t|
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
This means that depending on the type of dynamic attribute you're creating, things will get saved in the respective column in the
|
131
|
+
This means that depending on the type of dynamic attribute you're creating, things will get saved in the respective column in the dynamic_attributes table.
|
132
132
|
|
133
133
|
pre. @user.add_dynamic_attribute(:gunslinger, "boolean")
|
134
|
-
adds this to the
|
134
|
+
adds this to the dynamic_attributes table:
|
135
135
|
|
136
136
|
pre. {
|
137
137
|
:attr_key => "gunslinger",
|
@@ -172,7 +172,7 @@ h2. Ordering
|
|
172
172
|
|
173
173
|
You can call
|
174
174
|
|
175
|
-
pre. Product.
|
175
|
+
pre. Product.order_dynamic_attribute("price", "float") # returns all users with their dynamic attribute "name" in ascending order
|
176
176
|
|
177
177
|
Why pass the "float" in the order method? Because what if 2 different user records both have the dynamic attribute "price", but for one it's a float, and for the other it's an integer? How would you know which to order things by?
|
178
178
|
|
@@ -180,17 +180,17 @@ h2. Where
|
|
180
180
|
|
181
181
|
You can do:
|
182
182
|
|
183
|
-
pre. Product.
|
183
|
+
pre. Product.where_dynamic_attribute(:price => 200.0, :rating => 5)
|
184
184
|
|
185
185
|
As of now it accepts a hash only. If you want to scope to normal attributes, use the normal @where@ method.
|
186
186
|
|
187
|
-
h2. Contributing to
|
187
|
+
h2. Contributing to dynamic_attributes_right
|
188
188
|
|
189
189
|
There are definitely things that don't work, and could be done better. For example, it would be nice to:
|
190
190
|
|
191
|
-
# Do away with the special scopes, such as @
|
191
|
+
# Do away with the special scopes, such as @where_dynamic_attribute@ and @order_dynamic_attribute@. I've tried to override ActiveRecord to make the normal @where@ and @order@ methods to see if the attributes being passed were dynamic, and do the changes necessary to the resulting SQL, but my head "started to hurt":http://stackoverflow.com/q/5590191/61018.
|
192
192
|
# Have smarter caching, so that finds with dynamic attributes aren't very expensive.
|
193
|
-
# Have configurable "
|
193
|
+
# Have configurable "dynamic_attributes" table
|
194
194
|
|
195
195
|
Warning: the code is full of comments because I needed to visualize the code running.
|
196
196
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
data/datts_right.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{datts_right}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ramon Tayag"]
|
@@ -29,14 +29,13 @@ Gem::Specification.new do |s|
|
|
29
29
|
"VERSION",
|
30
30
|
"datts_right.gemspec",
|
31
31
|
"lib/datts_right.rb",
|
32
|
-
"lib/datts_right/
|
32
|
+
"lib/datts_right/dynamic_attribute.rb",
|
33
33
|
"lib/datts_right/exceptions.rb",
|
34
34
|
"lib/datts_right/instance_methods.rb",
|
35
35
|
"lib/datts_right/page.rb",
|
36
36
|
"lib/datts_right/query_methods.rb",
|
37
37
|
"spec/datt_spec.rb",
|
38
38
|
"spec/datts_right_spec.rb",
|
39
|
-
"spec/has_datts_migration_generator_spec.rb",
|
40
39
|
"spec/spec_helper.rb"
|
41
40
|
]
|
42
41
|
s.homepage = %q{http://github.com/ramontayag/datts_right}
|
@@ -47,7 +46,6 @@ Gem::Specification.new do |s|
|
|
47
46
|
s.test_files = [
|
48
47
|
"spec/datt_spec.rb",
|
49
48
|
"spec/datts_right_spec.rb",
|
50
|
-
"spec/has_datts_migration_generator_spec.rb",
|
51
49
|
"spec/spec_helper.rb"
|
52
50
|
]
|
53
51
|
|
@@ -166,6 +164,12 @@ Gem::Specification.new do |s|
|
|
166
164
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
167
165
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
168
166
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
167
|
+
s.add_development_dependency(%q<autotest>, [">= 0"])
|
168
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
169
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
170
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
171
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
172
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
169
173
|
else
|
170
174
|
s.add_dependency(%q<datts_right>, [">= 0"])
|
171
175
|
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
@@ -278,6 +282,12 @@ Gem::Specification.new do |s|
|
|
278
282
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
279
283
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
280
284
|
s.add_dependency(%q<rcov>, [">= 0"])
|
285
|
+
s.add_dependency(%q<autotest>, [">= 0"])
|
286
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
287
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
288
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
289
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
290
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
281
291
|
end
|
282
292
|
else
|
283
293
|
s.add_dependency(%q<datts_right>, [">= 0"])
|
@@ -391,6 +401,12 @@ Gem::Specification.new do |s|
|
|
391
401
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
392
402
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
393
403
|
s.add_dependency(%q<rcov>, [">= 0"])
|
404
|
+
s.add_dependency(%q<autotest>, [">= 0"])
|
405
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
406
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
407
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
408
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
409
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
394
410
|
end
|
395
411
|
end
|
396
412
|
|
data/lib/datts_right.rb
CHANGED
@@ -1,45 +1,47 @@
|
|
1
1
|
require 'datts_right/instance_methods'
|
2
|
-
require 'datts_right/
|
3
|
-
#require 'datts_right/query_methods'
|
4
|
-
#require 'datts_right/exceptions'
|
2
|
+
require 'datts_right/dynamic_attribute'
|
5
3
|
|
6
4
|
module DattsRight
|
7
|
-
def
|
5
|
+
def has_dynamic_attributes(options={})
|
8
6
|
include DattsRight::InstanceMethods
|
9
7
|
#include DattsRight::QueryMethods
|
10
8
|
#ActiveRecord::QueryMethods.extend DattsRight::QueryMethods
|
11
9
|
#include DattsRight::Exceptions
|
12
10
|
|
13
|
-
cattr_accessor :
|
14
|
-
self.
|
11
|
+
cattr_accessor :dynamic_attributes_options
|
12
|
+
self.dynamic_attributes_options = options
|
15
13
|
|
16
|
-
has_many :
|
14
|
+
has_many :dynamic_attributes, :as => :attributable, :dependent => :destroy
|
17
15
|
|
18
16
|
# Carry out delayed actions before save
|
19
17
|
before_save :build_dynamic_attributes
|
20
18
|
|
21
|
-
# scope :scope_self when looking through attributes so we don't look through all
|
19
|
+
# scope :scope_self when looking through attributes so we don't look through all dynamic_attributes
|
22
20
|
# Why? What if you have Friend and Page models.
|
23
|
-
# * Some Phone records have a
|
24
|
-
# * Some Page records have a
|
21
|
+
# * Some Phone records have a dynamic_attribute :price
|
22
|
+
# * Some Page records have a dynamic_attribute :price
|
25
23
|
#
|
26
|
-
# When we do Page.find_by_price(400) we want to search only the
|
27
|
-
# and we want to disregard the rest of the
|
28
|
-
scope :scope_self, lambda { joins(:
|
29
|
-
scope :
|
30
|
-
scope :
|
24
|
+
# When we do Page.find_by_price(400) we want to search only the dynamic_attributes that belong to Page
|
25
|
+
# and we want to disregard the rest of the dynamic_attributes.
|
26
|
+
scope :scope_self, lambda { joins(:dynamic_attributes).where("dynamic_attributes.attributable_type = :klass", :klass => self.name) }
|
27
|
+
scope :with_datt_key, lambda { |args| with_dynamic_attribute_key(args) }
|
28
|
+
scope :with_dynamic_attribute_key, lambda { |datt_key| scope_self.joins(:dynamic_attributes).where("dynamic_attributes.attr_key = :datt_key", :datt_key => datt_key)}
|
29
|
+
scope :with_datt_type, lambda { |args| with_dynamic_attribute_type(args) }
|
30
|
+
scope :with_dynamic_attribute_type, lambda { |object_type| scope_self.joins(:dynamic_attributes).where("object_type = :object_type", :object_type => object_type) }
|
31
31
|
|
32
|
-
scope :
|
32
|
+
scope :order_by_datt, lambda { |attr_key_with_order, object_type| order_by_dynamic_attribute(attr_key_with_order, object_type) }
|
33
|
+
scope :order_by_dynamic_attribute, lambda { |attr_key_with_order, object_type|
|
33
34
|
# possible attr_key_with_order forms: "field_name", "field_name ASC", "field_name DESC"
|
34
35
|
split_attr_key_with_order = attr_key_with_order.split(" ")
|
35
36
|
attr_key = split_attr_key_with_order.first
|
36
37
|
order_by = split_attr_key_with_order.last if split_attr_key_with_order.size > 1
|
37
|
-
order_value = "
|
38
|
+
order_value = "dynamic_attributes.#{object_type}_value"
|
38
39
|
order_value << " #{order_by}" if order_by
|
39
|
-
scope_self.
|
40
|
+
scope_self.with_dynamic_attribute_key(attr_key).joins(:dynamic_attributes).with_dynamic_attribute_type(object_type).order(order_value)
|
40
41
|
}
|
41
42
|
|
42
|
-
scope :where_datt, lambda { |opts|
|
43
|
+
scope :where_datt, lambda { |opts| where_dynamic_attribute(opts) }
|
44
|
+
scope :where_dynamic_attribute, lambda { |opts|
|
43
45
|
# TODO accept stuff other than the normal hash
|
44
46
|
# Lifted from AR::Relation#build_where
|
45
47
|
attributes = case opts
|
@@ -48,18 +50,13 @@ module DattsRight
|
|
48
50
|
when Hash
|
49
51
|
opts
|
50
52
|
end
|
51
|
-
results = self
|
52
|
-
# Look for all DattsRight records whose:
|
53
|
-
# 1. Has all dynamic_attributes
|
54
|
-
# 2. Those dynamic attributes match there conditions
|
53
|
+
results = self
|
55
54
|
attributes.each do |k, v|
|
56
55
|
conditions = "exists (" +
|
57
|
-
"select 1 from
|
58
|
-
#
|
59
|
-
|
60
|
-
"
|
61
|
-
"and datt.attributable_type = :attributable_type " +
|
62
|
-
"and datt.name = :name and datt.#{Datt.attr_column(v)} = :value" +
|
56
|
+
"select 1 from dynamic_attributes dynamic_attribute where " +
|
57
|
+
"#{self.table_name}.id = dynamic_attribute.attributable_id " +
|
58
|
+
"and dynamic_attribute.attributable_type = :attributable_type " +
|
59
|
+
"and dynamic_attribute.name = :name and dynamic_attribute.#{DynamicAttribute.attr_column(v)} = :value" +
|
63
60
|
")"
|
64
61
|
results = results.where(conditions, :attributable_type => self.name, :name => k.to_s, :value => v)
|
65
62
|
end
|
@@ -71,16 +68,14 @@ module DattsRight
|
|
71
68
|
begin # Prioritize ActiveRecord's method_missing
|
72
69
|
super(method_id, *arguments)
|
73
70
|
rescue NoMethodError => e
|
74
|
-
|
75
|
-
if method_id.to_s =~ /^find_(all_|last_)?by_datt_([_a-z]\w*)$/
|
71
|
+
if method_id.to_s =~ /^find_(all_|last_)?by_(dynamic_attribute|datt)_([_a-z]\w*)$/
|
76
72
|
all_or_last = $1
|
77
|
-
attributes = $
|
73
|
+
attributes = $3.split("_and_")
|
78
74
|
results = self
|
79
75
|
attributes.each_with_index do |attribute, i|
|
80
|
-
results = results.
|
76
|
+
results = results.where_dynamic_attribute(attribute.to_sym => arguments[i])
|
81
77
|
end
|
82
78
|
|
83
|
-
#puts "this is $1 of the method '#{method_id}': #{all_or_last}."
|
84
79
|
case all_or_last
|
85
80
|
when "all_"
|
86
81
|
results
|
@@ -94,39 +89,6 @@ module DattsRight
|
|
94
89
|
else
|
95
90
|
raise e
|
96
91
|
end
|
97
|
-
|
98
|
-
#if method_id.to_s =~ /^find_(all_|last_)by_datt_([_a-z]\w*)$/
|
99
|
-
#all_or_last = $1
|
100
|
-
#attributes = $2.split("_and_")
|
101
|
-
#results = self
|
102
|
-
#attributes.each_with_index do |attribute, i|
|
103
|
-
#results = results.where_datt(attribute.to_sym => arguments[i])
|
104
|
-
#end
|
105
|
-
|
106
|
-
#puts "this is $1 of the method '#{method_id}': #{all_or_last}."
|
107
|
-
#case all_or_last
|
108
|
-
#when "all_"
|
109
|
-
#results
|
110
|
-
#when "last_"
|
111
|
-
#results.last
|
112
|
-
#else
|
113
|
-
#nil
|
114
|
-
#end
|
115
|
-
#elsif method_id.to_s =~ /^find_by_datt_([_a-z]\w*)$/
|
116
|
-
##puts "returning a find_by_datt_#{$1}"
|
117
|
-
#attributes = $1.split("_and_")
|
118
|
-
##puts "here are the attributes: #{attributes.inspect}"
|
119
|
-
#results = self
|
120
|
-
#attributes.each_with_index do |attribute, i|
|
121
|
-
##puts "Adding where_datt(:#{attribute} => #{arguments[i]})"
|
122
|
-
#results = results.where_datt(attribute.to_sym => arguments[i])
|
123
|
-
#end
|
124
|
-
|
125
|
-
##puts "Will return the FIRST of #{results.to_sql}"
|
126
|
-
#results.first
|
127
|
-
#else
|
128
|
-
#nil
|
129
|
-
#end
|
130
92
|
end
|
131
93
|
end
|
132
94
|
|
@@ -147,6 +109,8 @@ module DattsRight
|
|
147
109
|
super
|
148
110
|
end
|
149
111
|
end
|
112
|
+
|
113
|
+
alias :has_datts :has_dynamic_attributes
|
150
114
|
end
|
151
115
|
|
152
116
|
ActiveRecord::Base.extend DattsRight
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module DattsRight
|
2
2
|
module InstanceMethods
|
3
3
|
def add_dynamic_attribute(name, klass)
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
unless
|
8
|
-
|
4
|
+
key = name.to_s
|
5
|
+
unless attributes.keys.include?(key)
|
6
|
+
dynamic_attribute = dynamic_attributes.find_by_attr_key(key.underscore)
|
7
|
+
unless dynamic_attribute
|
8
|
+
dynamic_attribute = dynamic_attributes.create :name => key, :attr_key => key.underscore, :object_type => klass
|
9
9
|
dynamic_columns(true)
|
10
10
|
end
|
11
|
-
#puts "Just added #{
|
12
|
-
|
11
|
+
#puts "Just added #{key} to #{self.class.name}##{id}, and here are the dynamic_attributes: #{dynamic_columns.inspect}"
|
12
|
+
dynamic_attribute
|
13
13
|
else
|
14
14
|
false
|
15
15
|
end
|
@@ -17,25 +17,26 @@ module DattsRight
|
|
17
17
|
|
18
18
|
def remove_dynamic_attribute(name)
|
19
19
|
name = name.to_s
|
20
|
-
|
21
|
-
if
|
22
|
-
|
20
|
+
dynamic_attribute = dynamic_attributes.find_by_attr_key(name)
|
21
|
+
if dynamic_attribute
|
22
|
+
dynamic_attribute.destroy
|
23
23
|
dynamic_columns(true)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
# Works like ActiveRecord's attributes except it returns dynamic attributes only
|
28
|
-
def dynamic_attributes(reload=false)
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
@dynamic_attributes
|
35
|
-
|
36
|
-
#
|
37
|
-
@dynamic_attributes
|
38
|
-
|
28
|
+
#def dynamic_attributes(reload=false)
|
29
|
+
#puts "dynamic_attributes called with reload? #{reload}"
|
30
|
+
#@dynamic_attributes ||= {}
|
31
|
+
#return @dynamic_attributes if !reload
|
32
|
+
##puts "Reloading dynamic_attributes"
|
33
|
+
#dynamic_attributes.each do |dynamic_attribute|
|
34
|
+
##puts "Adding this to @dynamic_attributes: #{dynamic_attribute.attr_key} => #{dynamic_attribute.value}"
|
35
|
+
#@dynamic_attributes.merge({dynamic_attribute.attr_key => dynamic_attribute.value})
|
36
|
+
#end
|
37
|
+
##puts "Here are teh dynamic_attributes: #{dynamic_attributes.inspect}, and this is what we're returning: #{@dynamic_attributes}"
|
38
|
+
#@dynamic_attributes.symbolize_keys!
|
39
|
+
#end
|
39
40
|
|
40
41
|
# Because we cannot determine the difference between the inexistence
|
41
42
|
# between key value pair point to nil (eg :hi => nil) and
|
@@ -45,18 +46,18 @@ module DattsRight
|
|
45
46
|
def dynamic_columns(reload=false)
|
46
47
|
@dynamic_columns ||= {}
|
47
48
|
@dynamic_columns if !reload
|
48
|
-
#puts "Building the dynamic_columns cache, and we'll look through #{
|
49
|
+
#puts "Building the dynamic_columns cache, and we'll look through #{dynamic_attributes.count} dynamic_attributes"
|
49
50
|
@dynamic_columns={}
|
50
|
-
|
51
|
-
|
52
|
-
#puts "Going through
|
51
|
+
dynamic_attributes.reload
|
52
|
+
dynamic_attributes.each do |dynamic_attribute|
|
53
|
+
#puts "Going through dynamic_attribute##{dynamic_attribute.id}"
|
53
54
|
dynamic_column = {
|
54
|
-
|
55
|
-
:object_type =>
|
56
|
-
:value =>
|
55
|
+
dynamic_attribute.attr_key.to_sym => {
|
56
|
+
:object_type => dynamic_attribute.object_type,
|
57
|
+
:value => dynamic_attribute.value
|
57
58
|
}
|
58
59
|
}
|
59
|
-
#puts "Added #{dynamic_column} to the dynamic columns"
|
60
|
+
#puts "Added #{dynamic_column.inspect} to the dynamic columns"
|
60
61
|
@dynamic_columns.merge!(dynamic_column)
|
61
62
|
end
|
62
63
|
#puts "in dynamic_columns, returning: #{@dynamic_columns.inspect}"
|
@@ -65,7 +66,7 @@ module DattsRight
|
|
65
66
|
|
66
67
|
# Determines if the given attribute is a dynamic attribute.
|
67
68
|
def dynamic_attribute?(attr)
|
68
|
-
#puts "in dynamic_attribute?(:#{attr}).
|
69
|
+
#puts "in dynamic_attribute?(:#{attr}). dynamic_attributes: #{dynamic_attributes.inspect}. Datt: #{Datt.all.inspect}"
|
69
70
|
#dynamic_columns.include?(attr.to_s)
|
70
71
|
!dynamic_columns[attr.to_sym].nil?
|
71
72
|
#return dynamic_attributes.include?(attr.to_sym) unless dynamic_attributes.empty?
|
@@ -94,101 +95,44 @@ module DattsRight
|
|
94
95
|
#assign_multiparameter_attributes(multi_parameter_attributes)
|
95
96
|
#end
|
96
97
|
|
97
|
-
|
98
|
-
# Needed to change it because the AR::Persistence method updated the attributes
|
99
|
-
# method directly.
|
100
|
-
# Ex.
|
101
|
-
# self.attributes = attributes
|
102
|
-
#
|
103
|
-
# That won't work because our dynamic attributes aren't in the attributes field
|
104
|
-
def update_attributes(attributes)
|
98
|
+
def update_dynamic_attributes(attributes)
|
105
99
|
# The following transaction covers any possible database side-effects of the
|
106
100
|
# attributes assignment. For example, setting the IDs of a child collection.
|
107
101
|
with_transaction_returning_status do
|
108
|
-
attributes.each do |k, v|
|
109
|
-
self.
|
102
|
+
attributes.symbolize_keys.each do |k, v|
|
103
|
+
self.write_dynamic_attribute(k, v)
|
110
104
|
end
|
111
105
|
save
|
112
106
|
end
|
113
107
|
end
|
114
108
|
|
115
|
-
|
116
|
-
# See DattsRight#update_attributes for the reason why
|
117
|
-
def update_attributes!(attributes)
|
109
|
+
def update_dynamic_attributes!(attributes)
|
118
110
|
with_transaction_returning_status do
|
119
|
-
attributes.each do |k, v|
|
120
|
-
self.
|
111
|
+
attributes.symbolize_keys.each do |k, v|
|
112
|
+
self.write_dynamic_attribute(k, v)
|
121
113
|
end
|
122
114
|
save!
|
123
115
|
end
|
124
116
|
end
|
125
117
|
|
126
|
-
private
|
127
|
-
|
128
|
-
# Called after validation on update so that dynamic attributes behave
|
129
|
-
# like normal attributes in the fact that the database is not touched
|
130
|
-
# until save is called.
|
131
|
-
def build_dynamic_attributes
|
132
|
-
return if @save_dynamic_attr.nil?
|
133
|
-
# This originally saves things into the "datts" column.
|
134
|
-
#write_attribute_without_dynamic_attributes "datts", @save_dynamic_attr
|
135
|
-
# We save things into the datts table
|
136
|
-
@save_dynamic_attr.each do |k, v|
|
137
|
-
k = k.to_s
|
138
|
-
datt = datts.find_by_attr_key(k)
|
139
|
-
#puts "to create or to update #{k}?"
|
140
|
-
if datt
|
141
|
-
datt.update_attribute(:value, v)
|
142
|
-
#puts "updated: #{datt.inspect}"
|
143
|
-
#else
|
144
|
-
#datt = datts.create! :name => k, :attr_key => k, :value => v
|
145
|
-
#puts "created: #{datt.inspect} among these datts: #{datts.inspect}"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
@save_dynamic_attr = {}
|
149
|
-
true
|
150
|
-
end
|
151
|
-
|
152
|
-
# Implements dynamic-attributes as if real getter/setter methods
|
153
|
-
# were defined.
|
154
|
-
def method_missing(method_id, *args, &block)
|
155
|
-
puts "in DattsRight::InstanceMethods#method_missing with method_id #{method_id}"
|
156
|
-
begin
|
157
|
-
super method_id, *args, &block
|
158
|
-
rescue NoMethodError => e
|
159
|
-
puts "AR doesn't have #{method_id} so DattsRight will take care of it instead"
|
160
|
-
attr_name = method_id.to_s.sub(/\=$/, '')
|
161
|
-
#puts "Now we check to see if price is a dynamic attribute. dynamic_columns: #{dynamic_columns}. is #{attr_name} in those dynamic_columns? #{dynamic_attribute?(attr_name)}"
|
162
|
-
if dynamic_attribute?(attr_name)
|
163
|
-
#puts "== And #{method_id} is a dynamic method"
|
164
|
-
if method_id.to_s =~ /\=$/
|
165
|
-
return write_attribute(attr_name, args[0])
|
166
|
-
else
|
167
|
-
return read_attribute(attr_name)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
raise e
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
118
|
# Overrides AR::Base#read_attribute
|
175
|
-
def
|
119
|
+
def read_dynamic_attribute(attr_name)
|
176
120
|
attr_name = attr_name.to_s
|
177
121
|
if dynamic_attribute?(attr_name)
|
178
|
-
#puts "trying to read
|
122
|
+
#puts "trying to read dynamic_attribute #{attr_name}"
|
179
123
|
# If value is in the @save_dynamic_attr cache, and it's not blank
|
180
124
|
if !@save_dynamic_attr.blank? and @save_dynamic_attr[attr_name]
|
181
|
-
#puts "
|
125
|
+
#puts "dynamic_attribute #{attr_name} IS in cache, so let's return its value"
|
182
126
|
# Then we return the value
|
183
127
|
return @save_dynamic_attr[attr_name]
|
184
128
|
else # The value is not there, or it's blank
|
185
|
-
#puts "
|
129
|
+
#puts "dynamic_attribute #{attr_name} is NOT in cache, so let's load it from the dynamic_attribute table"
|
186
130
|
#attrs = read_attribute_without_dynamic_attributes(dynamic_attributes_options[:column_name].to_s)
|
187
|
-
#puts "Here are ALL the
|
188
|
-
#puts "Here are my the
|
189
|
-
|
190
|
-
#puts "This is the dat I find: #{
|
191
|
-
return
|
131
|
+
#puts "Here are ALL the dynamic_attributes: #{Datt.all.inspect} and my id is: #{id}"
|
132
|
+
#puts "Here are my the dynamic_attributes: #{dynamic_attributes.inspect} and my id is: #{id}. and the first dynamic_attribute's value is #{dynamic_attributes.first.value}"
|
133
|
+
dynamic_attribute = dynamic_attributes.find_by_attr_key(attr_name)
|
134
|
+
#puts "This is the dat I find: #{dynamic_attribute.inspect}"
|
135
|
+
return dynamic_attribute.try(:value)
|
192
136
|
#attrs = attrs.nil? ? nil : YAML.load(attrs).symbolize_keys! unless attrs.is_a? Hash
|
193
137
|
#return nil if attrs.blank?
|
194
138
|
#return attrs[attr_name.to_sym]
|
@@ -199,7 +143,7 @@ module DattsRight
|
|
199
143
|
end
|
200
144
|
|
201
145
|
# Overrides AR::Base#write_attribute
|
202
|
-
def
|
146
|
+
def write_dynamic_attribute(attr_name, value)
|
203
147
|
#puts "attempting to write: #{attr_name} = #{value}"
|
204
148
|
if dynamic_attribute?(attr_name)
|
205
149
|
#puts "#{attr_name} is a dynamic_attribute"
|
@@ -207,9 +151,62 @@ module DattsRight
|
|
207
151
|
|
208
152
|
@save_dynamic_attr ||= {} # create the cache if needed
|
209
153
|
return @save_dynamic_attr[attr_name] = value
|
154
|
+
return nil
|
210
155
|
end
|
211
|
-
|
212
|
-
super(attr_name, value)
|
213
156
|
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# Called after validation on update so that dynamic attributes behave
|
161
|
+
# like normal attributes in the fact that the database is not touched
|
162
|
+
# until save is called.
|
163
|
+
def build_dynamic_attributes
|
164
|
+
return if @save_dynamic_attr.nil?
|
165
|
+
# This originally saves things into the "dynamic_attributes" column.
|
166
|
+
#write_attribute_without_dynamic_attributes "dynamic_attributes", @save_dynamic_attr
|
167
|
+
# We save things into the dynamic_attributes table
|
168
|
+
@save_dynamic_attr.each do |k, v|
|
169
|
+
k = k.to_s
|
170
|
+
dynamic_attribute = dynamic_attributes.find_by_attr_key(k)
|
171
|
+
#puts "to create or to update #{k}?"
|
172
|
+
if dynamic_attribute
|
173
|
+
dynamic_attribute.update_attribute(:value, v)
|
174
|
+
#puts "updated: #{dynamic_attribute.inspect}"
|
175
|
+
#else
|
176
|
+
#dynamic_attribute = dynamic_attributes.create! :name => k, :attr_key => k, :value => v
|
177
|
+
#puts "created: #{dynamic_attribute.inspect} among these dynamic_attributes: #{dynamic_attributes.inspect}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
@save_dynamic_attr = {}
|
181
|
+
true
|
182
|
+
end
|
183
|
+
|
184
|
+
# Implements dynamic-attributes as if real getter/setter methods
|
185
|
+
# were defined.
|
186
|
+
#def method_missing(method_id, *args, &block)
|
187
|
+
#puts "in DattsRight::InstanceMethods#method_missing with method_id #{method_id}"
|
188
|
+
#begin
|
189
|
+
#super method_id, *args, &block
|
190
|
+
#rescue NoMethodError => e
|
191
|
+
#puts "AR doesn't have #{method_id} so DattsRight will take care of it instead"
|
192
|
+
#attr_name = method_id.to_s.sub(/\=$/, '')
|
193
|
+
##puts "Now we check to see if price is a dynamic attribute. dynamic_columns: #{dynamic_columns}. is #{attr_name} in those dynamic_columns? #{dynamic_attribute?(attr_name)}"
|
194
|
+
#if dynamic_attribute?(attr_name)
|
195
|
+
##puts "== And #{method_id} is a dynamic method"
|
196
|
+
#if method_id.to_s =~ /\=$/
|
197
|
+
#return write_attribute(attr_name, args[0])
|
198
|
+
#else
|
199
|
+
#return read_attribute(attr_name)
|
200
|
+
#end
|
201
|
+
#end
|
202
|
+
#raise e
|
203
|
+
#end
|
204
|
+
#end
|
205
|
+
|
206
|
+
alias :add_datt :add_dynamic_attribute
|
207
|
+
alias :remove_datt :remove_dynamic_attribute
|
208
|
+
alias :read_datt :read_dynamic_attribute
|
209
|
+
alias :write_datt :write_dynamic_attribute
|
210
|
+
alias :update_dynamic_attribute :write_dynamic_attribute
|
214
211
|
end
|
215
212
|
end
|
data/lib/datts_right/page.rb
CHANGED
data/spec/datt_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe DynamicAttribute do
|
4
4
|
before do
|
5
5
|
reset_database
|
6
6
|
@page = Page.create
|
@@ -8,13 +8,13 @@ describe Datt do
|
|
8
8
|
|
9
9
|
it "should allow transferring between String and Text" do
|
10
10
|
@page.add_dynamic_attribute(:foo, "string")
|
11
|
-
@page.foo
|
11
|
+
@page.write_dynamic_attribute :foo, "this is a string"
|
12
12
|
@page.save
|
13
|
-
@page.foo
|
13
|
+
@page.write_dynamic_attribute :foo, "t"*300
|
14
14
|
@page.save
|
15
|
-
@page.
|
16
|
-
@page.foo
|
15
|
+
@page.dynamic_attributes.find_by_attr_key("foo").object_type.should == "text"
|
16
|
+
@page.write_dynamic_attribute :foo, "this is a string again"
|
17
17
|
@page.save
|
18
|
-
@page.
|
18
|
+
@page.dynamic_attributes.find_by_attr_key("foo").object_type.should == "string"
|
19
19
|
end
|
20
20
|
end
|
data/spec/datts_right_spec.rb
CHANGED
@@ -7,9 +7,13 @@ describe DattsRight do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
describe ".add_dynamic_attribute(attr_key, object_type)" do
|
10
|
+
it "should be aliased by add_datt" do
|
11
|
+
Page.instance_method(:add_dynamic_attribute).should == Page.instance_method(:add_datt)
|
12
|
+
end
|
13
|
+
|
10
14
|
it "should add a dynamic attribute with nil value" do
|
11
15
|
@page.add_dynamic_attribute(:rocks, "string")
|
12
|
-
@page.
|
16
|
+
@page.dynamic_columns[:rocks][:value].should be_nil
|
13
17
|
end
|
14
18
|
|
15
19
|
it "should ignore when trying to add same attribute" do
|
@@ -30,6 +34,10 @@ describe DattsRight do
|
|
30
34
|
end
|
31
35
|
|
32
36
|
describe ".remove_dynamic_attribute(attr_key)" do
|
37
|
+
it "should be aliased by remove_datt" do
|
38
|
+
Page.instance_method(:remove_dynamic_attribute).should == Page.instance_method(:remove_datt)
|
39
|
+
end
|
40
|
+
|
33
41
|
it "should remove the attribute completely" do
|
34
42
|
@page.add_dynamic_attribute(:rocks, "string")
|
35
43
|
@page.remove_dynamic_attribute(:rocks)
|
@@ -60,115 +68,129 @@ describe DattsRight do
|
|
60
68
|
|
61
69
|
it "should allow saving of integers" do
|
62
70
|
@page.add_dynamic_attribute(:price, "integer")
|
63
|
-
@page.price
|
71
|
+
@page.write_dynamic_attribute :price, 300
|
64
72
|
@page.save
|
65
73
|
@page = Page.last
|
66
|
-
@page.price.should == 300
|
74
|
+
@page.read_dynamic_attribute(:price).should == 300
|
67
75
|
end
|
68
76
|
|
69
77
|
it "should allow saving of floats" do
|
70
78
|
@page.add_dynamic_attribute(:price, "float")
|
71
|
-
@page.price
|
79
|
+
@page.write_dynamic_attribute :price, 300.0
|
72
80
|
@page.save
|
73
81
|
@page = Page.last
|
74
|
-
@page.price.should == 300.0
|
82
|
+
@page.read_dynamic_attribute(:price).should == 300.0
|
75
83
|
end
|
76
84
|
|
77
85
|
it "should allow saving of true" do
|
78
86
|
@page.add_dynamic_attribute(:real, "boolean")
|
79
|
-
@page.real
|
87
|
+
@page.write_dynamic_attribute :real, true
|
80
88
|
@page.save
|
81
89
|
@page = Page.last
|
82
|
-
@page.real.should be_true
|
90
|
+
@page.read_dynamic_attribute(:real).should be_true
|
83
91
|
end
|
84
92
|
|
85
93
|
it "should allow saving of false" do
|
86
94
|
@page.add_dynamic_attribute(:real, "boolean")
|
87
|
-
@page.real
|
95
|
+
@page.write_dynamic_attribute :real, false
|
88
96
|
@page.save
|
89
97
|
@page = Page.last
|
90
|
-
@page.real.should be_false
|
98
|
+
@page.read_dynamic_attribute(:real).should be_false
|
91
99
|
end
|
92
100
|
|
93
101
|
it "should allow saving of strings" do
|
94
102
|
@page.add_dynamic_attribute(:real, "string")
|
95
|
-
@page.real
|
103
|
+
@page.write_dynamic_attribute :real, "its real alright"
|
96
104
|
@page.save
|
97
105
|
@page = Page.last
|
98
|
-
@page.real.should == "its real alright"
|
106
|
+
@page.read_dynamic_attribute(:real).should == "its real alright"
|
99
107
|
end
|
100
108
|
|
101
109
|
it "should allow saving of text" do
|
102
110
|
@page.add_dynamic_attribute(:real, "text")
|
103
|
-
@page.real
|
111
|
+
@page.write_dynamic_attribute :real, "t"*256
|
104
112
|
@page.save
|
105
113
|
@page = Page.last
|
106
|
-
@page.real.should == "t"*256
|
114
|
+
@page.read_dynamic_attribute(:real).should == "t"*256
|
107
115
|
end
|
108
116
|
|
109
117
|
it "should cache the attribute" do
|
110
118
|
@page.add_dynamic_attribute(:price, "integer")
|
111
|
-
@page.price
|
112
|
-
@page.price.should == 200
|
119
|
+
@page.write_dynamic_attribute :price, 200
|
120
|
+
@page.read_dynamic_attribute(:price).should == 200
|
113
121
|
end
|
114
122
|
|
115
123
|
it "should not save the attribute if save on the attributable object isn't called" do
|
116
124
|
@page.add_dynamic_attribute(:price, "integer")
|
117
|
-
@page.price
|
125
|
+
@page.write_dynamic_attribute :price, 200
|
118
126
|
@page = Page.last
|
119
|
-
@page.price.should be_nil
|
127
|
+
@page.read_dynamic_attribute(:price).should be_nil
|
120
128
|
end
|
121
129
|
|
122
130
|
it "should allow overwriting of values" do
|
123
131
|
@page.add_dynamic_attribute(:price, "integer")
|
124
|
-
@page.price
|
132
|
+
@page.write_dynamic_attribute :price, 200
|
125
133
|
@page.save
|
126
134
|
@page = Page.last
|
127
|
-
@page.price
|
135
|
+
@page.write_dynamic_attribute :price, 300
|
128
136
|
@page.save
|
129
137
|
@page = Page.last
|
130
|
-
@page.price.should == 300
|
138
|
+
@page.read_dynamic_attribute(:price).should == 300
|
131
139
|
end
|
132
140
|
|
133
141
|
it "should allow nullifying of attributes, but keeping the fields there" do
|
134
142
|
@page.add_dynamic_attribute(:farce, "string")
|
135
|
-
@page.farce
|
143
|
+
@page.write_dynamic_attribute :farce, "Nothing here my friend"
|
136
144
|
@page.save
|
137
|
-
@page.farce
|
145
|
+
@page.write_dynamic_attribute :farce, nil
|
138
146
|
@page.dynamic_attribute?(:farce).should be_true
|
139
147
|
end
|
140
148
|
|
141
|
-
describe "
|
149
|
+
describe ".read_dynamic_attribute" do
|
150
|
+
it "should be aliased by read_datt" do
|
151
|
+
Page.instance_method(:read_dynamic_attribute).should == Page.instance_method(:read_datt)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "on dynamic find_by_dynamic_attribute methods" do
|
156
|
+
it "should be aliased by find_by_datt" do
|
157
|
+
@page.add_dynamic_attribute(:price, "integer")
|
158
|
+
@page.write_dynamic_attribute :price, 400
|
159
|
+
@page.save
|
160
|
+
Page.find_by_datt_price(400).should == Page.find_by_dynamic_attribute_price(400)
|
161
|
+
end
|
162
|
+
|
142
163
|
it "should be able to return single records" do
|
143
164
|
@page.add_dynamic_attribute(:price, "integer")
|
144
|
-
@page.price
|
165
|
+
@page.write_dynamic_attribute :price, 400
|
145
166
|
@page.save
|
146
167
|
|
147
168
|
@page_2 = Page.create
|
148
169
|
@page_2.add_dynamic_attribute(:price, "integer")
|
149
|
-
@page_2.price
|
170
|
+
@page_2.write_dynamic_attribute :price, 500
|
150
171
|
@page_2.save
|
151
172
|
|
152
|
-
Page.
|
153
|
-
|
173
|
+
@results = Page.find_by_dynamic_attribute_price(400)
|
174
|
+
@results.should == @page
|
175
|
+
@results.should_not == @page_2
|
154
176
|
end
|
155
177
|
|
156
178
|
it "should be able to return all matching records" do
|
157
179
|
@page.add_dynamic_attribute(:price, "integer")
|
158
|
-
@page.price
|
180
|
+
@page.write_dynamic_attribute :price, 400
|
159
181
|
@page.save
|
160
182
|
|
161
183
|
@page_2 = Page.create
|
162
184
|
@page_2.add_dynamic_attribute(:price, "integer")
|
163
|
-
@page_2.price
|
185
|
+
@page_2.write_dynamic_attribute :price, 500
|
164
186
|
@page_2.save
|
165
187
|
|
166
188
|
@page_3 = Page.create
|
167
189
|
@page_3.add_dynamic_attribute(:price, "integer")
|
168
|
-
@page_3.price
|
190
|
+
@page_3.write_dynamic_attribute :price, 400
|
169
191
|
@page_3.save
|
170
192
|
|
171
|
-
@results = Page.
|
193
|
+
@results = Page.find_all_by_dynamic_attribute_price(400)
|
172
194
|
@results.should include(@page)
|
173
195
|
@results.should_not include(@page_2)
|
174
196
|
@results.should include(@page_3)
|
@@ -180,15 +202,15 @@ describe DattsRight do
|
|
180
202
|
|
181
203
|
it "should be able to return the last record" do
|
182
204
|
@page.add_dynamic_attribute(:price, "integer")
|
183
|
-
@page.price
|
205
|
+
@page.write_dynamic_attribute :price, 400
|
184
206
|
@page.save
|
185
207
|
|
186
208
|
@page_2 = Page.create
|
187
209
|
@page_2.add_dynamic_attribute(:price, "integer")
|
188
|
-
@page_2.price
|
210
|
+
@page_2.write_dynamic_attribute :price, 400
|
189
211
|
@page_2.save
|
190
212
|
|
191
|
-
Page.
|
213
|
+
Page.find_last_by_dynamic_attribute_price(400) == @page_2
|
192
214
|
end
|
193
215
|
|
194
216
|
it "should allow normal attributes to work" do
|
@@ -201,15 +223,15 @@ describe DattsRight do
|
|
201
223
|
it "should allow chaining" do
|
202
224
|
@page.add_dynamic_attribute(:price, "integer")
|
203
225
|
@page.name = "fixed"
|
204
|
-
@page.price
|
226
|
+
@page.write_dynamic_attribute :price, 400
|
205
227
|
@page.save
|
206
228
|
|
207
229
|
@page_2 = Page.create
|
208
230
|
@page_2.add_dynamic_attribute(:price, "integer")
|
209
|
-
@page_2.price
|
231
|
+
@page_2.write_dynamic_attribute :price, 500
|
210
232
|
@page_2.save
|
211
233
|
|
212
|
-
@pages = Page.where(:name => "fixed").
|
234
|
+
@pages = Page.where(:name => "fixed").find_by_dynamic_attribute_price(400)
|
213
235
|
@pages.should == @page
|
214
236
|
@pages.should_not == @page_2
|
215
237
|
end
|
@@ -217,19 +239,19 @@ describe DattsRight do
|
|
217
239
|
|
218
240
|
it "should allow multiple attributes" do
|
219
241
|
@page.add_dynamic_attribute(:price, "integer")
|
220
|
-
@page.price
|
242
|
+
@page.write_dynamic_attribute :price, 400
|
221
243
|
@page.add_dynamic_attribute(:farce, "string")
|
222
|
-
@page.farce
|
244
|
+
@page.write_dynamic_attribute :farce, "hitt"
|
223
245
|
@page.save
|
224
246
|
|
225
247
|
@page_2 = Page.create
|
226
248
|
@page_2.add_dynamic_attribute(:price, "integer")
|
227
|
-
@page_2.price
|
249
|
+
@page_2.write_dynamic_attribute :price, 400
|
228
250
|
@page_2.add_dynamic_attribute(:farce, "string")
|
229
|
-
@page_2.farce
|
251
|
+
@page_2.write_dynamic_attribute :farce, "hi"
|
230
252
|
@page_2.save
|
231
253
|
|
232
|
-
@pages = Page.
|
254
|
+
@pages = Page.find_by_dynamic_attribute_price_and_farce(400, "hi")
|
233
255
|
@pages.should == @page_2
|
234
256
|
@pages.should_not == @page
|
235
257
|
end
|
@@ -240,83 +262,116 @@ describe DattsRight do
|
|
240
262
|
end
|
241
263
|
|
242
264
|
it "should not save the value" do
|
243
|
-
@page.price
|
265
|
+
@page.write_dynamic_attribute :price, 200
|
244
266
|
@page.save
|
245
|
-
@page.price
|
267
|
+
@page.write_dynamic_attribute :price, "hi there"
|
246
268
|
@page.save
|
247
|
-
Page.last.price.should == 200
|
269
|
+
Page.last.read_dynamic_attribute(:price).should == 200
|
248
270
|
end
|
249
271
|
end
|
250
272
|
|
251
273
|
it "should allow mass assignment" do
|
252
274
|
@page.add_dynamic_attribute(:price, "integer")
|
253
275
|
@page.add_dynamic_attribute(:farce, "string")
|
254
|
-
@page.
|
255
|
-
@page.price.should == 200
|
256
|
-
@page.farce.should == "hi"
|
276
|
+
@page.update_dynamic_attributes(:price => 200, :farce => "hi")
|
277
|
+
@page.read_dynamic_attribute(:price).should == 200
|
278
|
+
@page.read_dynamic_attribute(:farce).should == "hi"
|
257
279
|
|
258
|
-
@page.
|
259
|
-
@page.price.should == 300
|
260
|
-
@page.farce.should == "not farce!"
|
280
|
+
@page.update_dynamic_attributes!(:price => 300, :farce => "not farce!")
|
281
|
+
@page.read_dynamic_attribute(:price).should == 300
|
282
|
+
@page.read_dynamic_attribute(:farce).should == "not farce!"
|
261
283
|
end
|
262
284
|
|
263
|
-
it "should allow use of
|
285
|
+
it "should allow use of update_dynamic_attribute" do
|
264
286
|
@page.add_dynamic_attribute(:price, "integer")
|
265
|
-
@page.
|
266
|
-
@page.price.should == 200
|
287
|
+
@page.update_dynamic_attribute(:price, 200)
|
288
|
+
@page.read_dynamic_attribute(:price).should == 200
|
267
289
|
end
|
268
290
|
|
269
|
-
describe "
|
291
|
+
describe "order_by_dynamic_attribute" do
|
292
|
+
it "should be aliased by order_by_datt" do
|
293
|
+
@page.add_dynamic_attribute(:price, "integer")
|
294
|
+
@page.write_dynamic_attribute :price, 2
|
295
|
+
@page.save
|
296
|
+
Page.order_by_dynamic_attribute("price", "integer").should == Page.order_by_datt("price", "integer")
|
297
|
+
end
|
298
|
+
|
270
299
|
it "should default to asc" do
|
271
300
|
@page.add_dynamic_attribute(:price, "integer")
|
272
|
-
@page.price
|
301
|
+
@page.write_dynamic_attribute :price, 2
|
273
302
|
@page.save
|
274
303
|
@page_2 = Page.create
|
275
304
|
@page_2.add_dynamic_attribute(:price, "integer")
|
276
|
-
@page_2.price
|
305
|
+
@page_2.write_dynamic_attribute :price, 1
|
277
306
|
@page_2.save
|
278
307
|
@page_3 = Page.create
|
279
308
|
@page_3.add_dynamic_attribute(:price, "float")
|
280
|
-
@page_3.price
|
309
|
+
@page_3.write_dynamic_attribute :price, 3.0
|
281
310
|
@page_3.save
|
282
311
|
@page_4 = Page.create
|
283
312
|
@page_4.add_dynamic_attribute(:price, "float")
|
284
|
-
@page_4.price
|
313
|
+
@page_4.write_dynamic_attribute :price, 3.5
|
285
314
|
@page_4.save
|
286
315
|
|
287
316
|
# What if different records have price but they are of different object_type?
|
288
|
-
# page_1.price is an "integer" and it's saved in "integer_value" column
|
289
|
-
# page_2.price is a "float" and it's saved in "float_value"
|
317
|
+
# page_1.read_dynamic_attribute(:price) is an "integer" and it's saved in "integer_value" column
|
318
|
+
# page_2.read_dynamic_attribute(:price) is a "float" and it's saved in "float_value"
|
290
319
|
#
|
291
|
-
# How would you work on Page.
|
320
|
+
# How would you work on Page.order_by_dynamic_attribute("price")?
|
292
321
|
#
|
293
322
|
# Answer 1: we could require passing of the object_type in the order method
|
294
|
-
# This way, we know which column to look at =>
|
295
|
-
Page.
|
296
|
-
Page.
|
323
|
+
# This way, we know which column to look at => order_by_dynamic_attribute("price", "integer")
|
324
|
+
Page.order_by_dynamic_attribute("price", "integer").should == [@page_2, @page]
|
325
|
+
Page.order_by_dynamic_attribute("price DESC", "float").should == [@page_4, @page_3]
|
297
326
|
end
|
298
327
|
end
|
299
328
|
|
300
|
-
describe "
|
301
|
-
it "should
|
329
|
+
describe ".write_dynamic_attribute" do
|
330
|
+
it "should be aliased by write_datt" do
|
331
|
+
Page.instance_method(:write_dynamic_attribute).should == Page.instance_method(:write_datt)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe ".with_dynamic_attribute_key" do
|
336
|
+
it "should be aliased by .with_datt_key" do
|
337
|
+
@page.add_dynamic_attribute(:price, "float")
|
338
|
+
Page.with_dynamic_attribute_key(:price).should == Page.with_datt_key(:price)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe ".with_dynamic_attribute_type" do
|
343
|
+
it "should be aliased by .with_datt_type" do
|
344
|
+
@page.add_dynamic_attribute(:price, "float")
|
345
|
+
Page.with_dynamic_attribute_type("float").should == Page.with_datt_type("float")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe "where_dynamic_attribute" do
|
350
|
+
it "should be aliased by where_datt" do
|
351
|
+
@page.add_dynamic_attribute(:price, "float")
|
352
|
+
@page.write_dynamic_attribute :price, 200.0
|
353
|
+
Page.where_dynamic_attribute(:price => 200.0).should == Page.where_datt(:price => 200.0)
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should automatically join in the dynamic_attributes table" do
|
302
357
|
@page.add_dynamic_attribute(:price, "float")
|
303
|
-
@page.price
|
358
|
+
@page.write_dynamic_attribute :price, 200.0
|
304
359
|
@page.add_dynamic_attribute(:farce, "boolean")
|
305
|
-
@page.farce
|
360
|
+
@page.write_dynamic_attribute :farce, true
|
306
361
|
@page.save
|
307
362
|
|
308
363
|
@page_2 = Page.create
|
309
364
|
@page_2.add_dynamic_attribute(:price, "integer")
|
310
365
|
@page_2.add_dynamic_attribute(:farce, "boolean")
|
311
|
-
@page_2.farce
|
312
|
-
@page_2.price
|
366
|
+
@page_2.write_dynamic_attribute :farce, true
|
367
|
+
@page_2.write_dynamic_attribute :price, 1
|
313
368
|
@page_2.save
|
314
369
|
|
315
|
-
@result = Page.
|
370
|
+
@result = Page.where_dynamic_attribute(:price => 200.0, :farce => true)
|
316
371
|
@result.should include(@page)
|
317
372
|
@result.should_not include(@page_2)
|
318
373
|
|
319
|
-
#@result = Page.
|
374
|
+
#@result = Page.where_dynamic_attribute("price < :price", :price => 199.0)
|
320
375
|
#@result.should_not include(@page)
|
321
376
|
#@result.should include(@page_2)
|
322
377
|
end
|
@@ -324,21 +379,21 @@ describe DattsRight do
|
|
324
379
|
it "should allow chaining with where scopes" do
|
325
380
|
@page.add_dynamic_attribute(:price, "integer")
|
326
381
|
@page.name = "aardvark"
|
327
|
-
@page.price
|
382
|
+
@page.write_dynamic_attribute :price, 200
|
328
383
|
@page.save
|
329
384
|
|
330
385
|
@page_2 = Page.create
|
331
386
|
@page_2.add_dynamic_attribute(:price, "integer")
|
332
|
-
@page_2.price
|
387
|
+
@page_2.write_dynamic_attribute :price, 200
|
333
388
|
@page_2.save
|
334
389
|
|
335
|
-
@results = Page.
|
390
|
+
@results = Page.where_dynamic_attribute(:price => 200).where(:name => "aardvark")
|
336
391
|
@results.should include(@page)
|
337
392
|
@results.should_not include(@page_2)
|
338
393
|
end
|
339
394
|
|
340
395
|
it "should return an empty array if we're looking for a field that doesn't exist" do
|
341
|
-
Page.
|
396
|
+
Page.where_dynamic_attribute(:bogus_field => "none").should be_empty
|
342
397
|
end
|
343
398
|
end
|
344
399
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,7 +4,7 @@ require 'rubygems'
|
|
4
4
|
require 'active_record'
|
5
5
|
require 'rspec'
|
6
6
|
require 'datts_right'
|
7
|
-
require 'datts_right/page'
|
7
|
+
require 'datts_right/page'
|
8
8
|
|
9
9
|
# Allow to connect to SQLite
|
10
10
|
root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
@@ -23,13 +23,13 @@ RSpec.configure do |config|
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def reset_database
|
26
|
-
%W(pages
|
26
|
+
%W(pages dynamic_attributes).each do |table_name|
|
27
27
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS '#{table_name}'")
|
28
28
|
end
|
29
29
|
ActiveRecord::Base.connection.create_table(:pages) do |t|
|
30
30
|
t.string :name, :default => "My name"
|
31
31
|
end
|
32
|
-
ActiveRecord::Base.connection.create_table(:
|
32
|
+
ActiveRecord::Base.connection.create_table(:dynamic_attributes) do |t|
|
33
33
|
t.string :name, :null => false
|
34
34
|
t.string :attr_key, :null => false
|
35
35
|
t.string :object_type, :null => false
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datts_right
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 8
|
10
|
+
version: 0.0.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ramon Tayag
|
@@ -1684,6 +1684,96 @@ dependencies:
|
|
1684
1684
|
name: rcov
|
1685
1685
|
version_requirements: *id111
|
1686
1686
|
prerelease: false
|
1687
|
+
- !ruby/object:Gem::Dependency
|
1688
|
+
type: :development
|
1689
|
+
requirement: &id112 !ruby/object:Gem::Requirement
|
1690
|
+
none: false
|
1691
|
+
requirements:
|
1692
|
+
- - ">="
|
1693
|
+
- !ruby/object:Gem::Version
|
1694
|
+
hash: 3
|
1695
|
+
segments:
|
1696
|
+
- 0
|
1697
|
+
version: "0"
|
1698
|
+
name: autotest
|
1699
|
+
version_requirements: *id112
|
1700
|
+
prerelease: false
|
1701
|
+
- !ruby/object:Gem::Dependency
|
1702
|
+
type: :development
|
1703
|
+
requirement: &id113 !ruby/object:Gem::Requirement
|
1704
|
+
none: false
|
1705
|
+
requirements:
|
1706
|
+
- - ">="
|
1707
|
+
- !ruby/object:Gem::Version
|
1708
|
+
hash: 3
|
1709
|
+
segments:
|
1710
|
+
- 0
|
1711
|
+
version: "0"
|
1712
|
+
name: sqlite3
|
1713
|
+
version_requirements: *id113
|
1714
|
+
prerelease: false
|
1715
|
+
- !ruby/object:Gem::Dependency
|
1716
|
+
type: :development
|
1717
|
+
requirement: &id114 !ruby/object:Gem::Requirement
|
1718
|
+
none: false
|
1719
|
+
requirements:
|
1720
|
+
- - ~>
|
1721
|
+
- !ruby/object:Gem::Version
|
1722
|
+
hash: 3
|
1723
|
+
segments:
|
1724
|
+
- 2
|
1725
|
+
- 3
|
1726
|
+
- 0
|
1727
|
+
version: 2.3.0
|
1728
|
+
name: rspec
|
1729
|
+
version_requirements: *id114
|
1730
|
+
prerelease: false
|
1731
|
+
- !ruby/object:Gem::Dependency
|
1732
|
+
type: :development
|
1733
|
+
requirement: &id115 !ruby/object:Gem::Requirement
|
1734
|
+
none: false
|
1735
|
+
requirements:
|
1736
|
+
- - ~>
|
1737
|
+
- !ruby/object:Gem::Version
|
1738
|
+
hash: 23
|
1739
|
+
segments:
|
1740
|
+
- 1
|
1741
|
+
- 0
|
1742
|
+
- 0
|
1743
|
+
version: 1.0.0
|
1744
|
+
name: bundler
|
1745
|
+
version_requirements: *id115
|
1746
|
+
prerelease: false
|
1747
|
+
- !ruby/object:Gem::Dependency
|
1748
|
+
type: :development
|
1749
|
+
requirement: &id116 !ruby/object:Gem::Requirement
|
1750
|
+
none: false
|
1751
|
+
requirements:
|
1752
|
+
- - ~>
|
1753
|
+
- !ruby/object:Gem::Version
|
1754
|
+
hash: 7
|
1755
|
+
segments:
|
1756
|
+
- 1
|
1757
|
+
- 5
|
1758
|
+
- 2
|
1759
|
+
version: 1.5.2
|
1760
|
+
name: jeweler
|
1761
|
+
version_requirements: *id116
|
1762
|
+
prerelease: false
|
1763
|
+
- !ruby/object:Gem::Dependency
|
1764
|
+
type: :development
|
1765
|
+
requirement: &id117 !ruby/object:Gem::Requirement
|
1766
|
+
none: false
|
1767
|
+
requirements:
|
1768
|
+
- - ">="
|
1769
|
+
- !ruby/object:Gem::Version
|
1770
|
+
hash: 3
|
1771
|
+
segments:
|
1772
|
+
- 0
|
1773
|
+
version: "0"
|
1774
|
+
name: rcov
|
1775
|
+
version_requirements: *id117
|
1776
|
+
prerelease: false
|
1687
1777
|
description: Creates a separate table that saves all your dynamic attributes.
|
1688
1778
|
email: ramon@tayag.net
|
1689
1779
|
executables: []
|
@@ -1706,14 +1796,13 @@ files:
|
|
1706
1796
|
- VERSION
|
1707
1797
|
- datts_right.gemspec
|
1708
1798
|
- lib/datts_right.rb
|
1709
|
-
- lib/datts_right/
|
1799
|
+
- lib/datts_right/dynamic_attribute.rb
|
1710
1800
|
- lib/datts_right/exceptions.rb
|
1711
1801
|
- lib/datts_right/instance_methods.rb
|
1712
1802
|
- lib/datts_right/page.rb
|
1713
1803
|
- lib/datts_right/query_methods.rb
|
1714
1804
|
- spec/datt_spec.rb
|
1715
1805
|
- spec/datts_right_spec.rb
|
1716
|
-
- spec/has_datts_migration_generator_spec.rb
|
1717
1806
|
- spec/spec_helper.rb
|
1718
1807
|
has_rdoc: true
|
1719
1808
|
homepage: http://github.com/ramontayag/datts_right
|
@@ -1752,5 +1841,4 @@ summary: Allows saving of dynamic attributes in your ActiveRecord model
|
|
1752
1841
|
test_files:
|
1753
1842
|
- spec/datt_spec.rb
|
1754
1843
|
- spec/datts_right_spec.rb
|
1755
|
-
- spec/has_datts_migration_generator_spec.rb
|
1756
1844
|
- spec/spec_helper.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
#describe "Migration Generator" do
|
4
|
-
#before do
|
5
|
-
#Rails.stub!(:version).and_return("3.0.0")
|
6
|
-
#Dir.mkdir("#{RAILS_ROOT}/db") unless File.exists?("#{RAILS_ROOT}/db")
|
7
|
-
#Dir.mkdir("#{RAILS_ROOT}/db/migrate") unless File.exists?("#{RAILS_ROOT}/db/migrate")
|
8
|
-
#end
|
9
|
-
|
10
|
-
#it "should generate migration file" do
|
11
|
-
#Dir.glob("#{RAILS_ROOT}/db/migrate/*.rb").should_not be_empty
|
12
|
-
#end
|
13
|
-
#end
|