rails_lookup 0.0.1 → 0.0.2
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 +3 -0
- data/Rakefile +1 -1
- data/lib/active_record/lookup.rb +127 -108
- data/rails_lookup.gemspec +7 -3
- data/test/20110808002412_create_test_lookup_db.rb +35 -0
- data/test/test_lookup.rb +223 -0
- metadata +9 -6
- data/Manifest +0 -4
data/README
CHANGED
@@ -5,6 +5,8 @@ By: Nimrod Priell
|
|
5
5
|
|
6
6
|
This gem adds an ActiveRecord macro to define memory-cached, dynamically growing, normalized lookup tables for entity 'type'-like objects. Or in plain English - if you want to have a table containing, say, ProductTypes which can grow with new types simply when you refer to them, and not keep the Product table containing a thousand repeating 'type="book"' entries - sit down and try to follow through.
|
7
7
|
|
8
|
+
Installation is described down the page.
|
9
|
+
|
8
10
|
Motivation
|
9
11
|
----------
|
10
12
|
|
@@ -81,6 +83,7 @@ I've looked thoroughly for a nice rails solution to this, but after failing to f
|
|
81
83
|
|
82
84
|
Installation
|
83
85
|
------------
|
86
|
+
First, run `gem install rails_lookup` to install the latest version of the gem.
|
84
87
|
|
85
88
|
The result of this hook is that you get the exact syntax described above, with only two lines of code (no extra classes or anything):
|
86
89
|
In your ActiveRecord object simply add
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('rails_lookup', '0.0.
|
5
|
+
Echoe.new('rails_lookup', '0.0.2') do |s|
|
6
6
|
s.description = File.read(File.join(File.dirname(__FILE__), 'README'))
|
7
7
|
s.summary = "Lookup table macro for ActiveRecords"
|
8
8
|
s.url = "http://github.com/Nimster/RailsLookup/"
|
data/lib/active_record/lookup.rb
CHANGED
@@ -5,130 +5,149 @@
|
|
5
5
|
# include ActiveRecord::Lookup
|
6
6
|
# in your class that extends ActiveRecord::Base
|
7
7
|
module ActiveRecord
|
8
|
-
module Lookup
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
8
|
+
module Lookup
|
9
|
+
#These will be defined as class methods so they can be called as "macros"
|
10
|
+
#like belongs_to
|
11
|
+
module ClassMethods
|
12
|
+
#We define only this "macro". In your ActiveRecord entity (say, Car), use
|
13
|
+
# lookup :car_type
|
14
|
+
# which will create Car#car_type, Car#car_type=, Car#car_type_id,
|
15
|
+
# Car#car_type_id= and CarType, an ActiveRecord with a name (String)
|
16
|
+
# attribute, that has_many :cars.
|
17
|
+
#
|
18
|
+
# You can also use
|
19
|
+
# lookup :car_type, :as => :type
|
20
|
+
# which will do the same thing but create Car#type, Car#type=, Car#type_id
|
21
|
+
# and Car#type_id= instead.
|
22
|
+
def lookup(lookup_name, opts = {})
|
23
|
+
as_name = opts[:as] || lookup_name
|
24
|
+
|
25
|
+
#Define the setter for the attribute by textual value
|
26
|
+
mycls = self #Class I'm defined in
|
27
|
+
#Ignore anonymous classes:
|
28
|
+
mycls_sym = mycls.name.tableize.to_sym unless mycls.to_s =~ /#<.*>/
|
29
|
+
lookup_cls_name = lookup_name.to_s.camelize
|
30
|
+
begin
|
31
|
+
#If it exists, just tack on the extra has_many
|
32
|
+
cls = Object.const_get lookup_cls_name
|
33
|
+
cls.has_many mycls_sym unless mycls_sym.blank? #for annon classes
|
34
|
+
rescue NameError
|
35
|
+
#Otherwise, actually define it
|
36
|
+
cls = Class.new(ActiveRecord::Base) do
|
37
|
+
has_many mycls_sym unless mycls_sym.blank? #For anon classes
|
38
|
+
validates_uniqueness_of :name
|
39
|
+
validates :name, :presence => true
|
40
|
+
|
41
|
+
def self.id_for(name, id = nil)
|
42
|
+
class_variable_get(:@@rcaches)[name] ||= id
|
43
|
+
end
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
def self.gen_id_for(val)
|
46
|
+
id = id_for val
|
47
|
+
if id.nil?
|
48
|
+
#Define this new possible value
|
49
|
+
new_db_obj = find_or_create_by_name val #You could just override this...
|
50
|
+
id_for val, new_db_obj.id
|
51
|
+
name_for new_db_obj.id, val
|
52
|
+
id = new_db_obj.id
|
53
|
+
end
|
54
|
+
id
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.name_for(id, name = nil)
|
58
|
+
class_variable_get(:@@caches)[id] ||= name
|
59
|
+
end
|
60
|
+
|
61
|
+
#This is especially useful for tests - you want deletion to also clear
|
62
|
+
#the caches otherwise odd stuff might happen
|
63
|
+
def self.delete_all(*args)
|
64
|
+
class_variable_set :@@caches, []
|
65
|
+
class_variable_set :@@rcaches, {}
|
66
|
+
super *args
|
67
|
+
end
|
50
68
|
end
|
51
|
-
|
69
|
+
|
70
|
+
Object.const_set lookup_cls_name, cls #Define it as a global class
|
52
71
|
end
|
53
72
|
|
54
|
-
|
55
|
-
|
56
|
-
|
73
|
+
|
74
|
+
define_method("#{as_name.to_s}_id=") do |id|
|
75
|
+
write_attribute "#{as_name.to_s}".to_sym, id
|
57
76
|
end
|
58
|
-
end
|
59
77
|
|
60
|
-
|
61
|
-
|
62
|
-
|
78
|
+
define_method("#{as_name.to_s}=") do |val|
|
79
|
+
id = cls.gen_id_for val #TODO: Just call #{as_name.to_s}_id=
|
80
|
+
write_attribute "#{as_name.to_s}".to_sym, id
|
81
|
+
end
|
63
82
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
write_attribute "#{as_name.to_s}_id".to_sym, id
|
68
|
-
end
|
83
|
+
define_method("#{as_name.to_s}_id") do
|
84
|
+
read_attribute "#{as_name.to_s}".to_sym
|
85
|
+
end
|
69
86
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
87
|
+
#Define the getter
|
88
|
+
define_method("#{as_name.to_s}") do
|
89
|
+
id = read_attribute "#{as_name.to_s}".to_sym
|
90
|
+
if not id.nil?
|
91
|
+
value = cls.name_for id
|
92
|
+
if value.nil?
|
93
|
+
lookup_obj = cls.find_by_id id
|
94
|
+
if not lookup_obj.nil?
|
95
|
+
#TODO: Extract to a method
|
96
|
+
cls.name_for id, lookup_obj.name
|
97
|
+
cls.id_for lookup_obj.name, id
|
98
|
+
end
|
80
99
|
end
|
81
100
|
end
|
101
|
+
value
|
82
102
|
end
|
83
|
-
value
|
84
|
-
end
|
85
103
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
idx = match.attribute_names.find_index { |n| n == @as_name.to_s }
|
101
|
-
if not idx.nil?
|
102
|
-
self.new.send "#{@as_name}=", arguments[idx] #Make sure there's a cached value
|
103
|
-
arguments[idx] = @cls.id_for arguments[idx] #Change the argument
|
104
|
-
method_id = method_id.to_s.sub /(by|and)_#{@as_name}$/, "\\1_#{@as_name}_id"
|
105
|
-
method_id = method_id.to_s.sub /(by|and)_#{@as_name}_and/, "\\1_#{@as_name}_id_and"
|
106
|
-
method_id = method_id.to_sym
|
104
|
+
|
105
|
+
#Rails ActiveRecord support:
|
106
|
+
@cls_for_name = {} if @cls_for_name.nil?
|
107
|
+
@cls_for_name[as_name.to_s.to_sym] = cls
|
108
|
+
#We need to override this for the dynamic finders.
|
109
|
+
def method_missing(method_id, *arguments, &block)
|
110
|
+
if match = ActiveRecord::DynamicFinderMatch.match(method_id)
|
111
|
+
#reroute kind to _kind_id
|
112
|
+
idxs = match.attribute_names.collect.with_index { |n,i| [i,n.to_sym] if @cls_for_name.has_key? n.to_sym }.compact
|
113
|
+
idxs.each do |elm|
|
114
|
+
if method_id =~ /^find_by_/
|
115
|
+
arguments[elm[0]] = @cls_for_name[elm[1]].gen_id_for arguments[elm[0]] #Change the argument
|
116
|
+
end
|
117
|
+
end
|
107
118
|
end
|
108
|
-
|
109
|
-
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def where(opts, *rest)
|
123
|
+
#Change the values of the keys in the lookup to their ids
|
124
|
+
mapped = opts.map { |k,v| @cls_for_name.has_key?(k) ? [k, @cls_for_name[k].id_for(v)] : [k, v] }
|
125
|
+
opts = Hash[mapped]
|
126
|
+
super(opts, *rest)
|
127
|
+
end
|
128
|
+
|
129
|
+
#Might need to be in class_eval
|
130
|
+
belongs_to lookup_name.to_s.to_sym, :foreign_key => "#{as_name}_id"
|
131
|
+
validates as_name.to_s.to_sym, :presence => true
|
132
|
+
|
133
|
+
#Prefill the hashes from the DB:
|
134
|
+
all_vals = cls.all
|
135
|
+
#No need for the class_exec
|
136
|
+
cls.class_variable_set(:@@rcaches, all_vals.inject({}) do |r, obj|
|
137
|
+
r[obj.name] = obj.id
|
138
|
+
r
|
139
|
+
end)
|
140
|
+
cls.class_variable_set(:@@caches, all_vals.inject([]) do |r, obj|
|
141
|
+
r[obj.id] = obj.name
|
142
|
+
r
|
143
|
+
end)
|
110
144
|
end
|
111
145
|
|
112
|
-
#Define the link between the host class and the newly created ActiveRecord
|
113
|
-
belongs_to lookup_name.to_s.to_sym, :foreign_key => "#{as_name}_id".to_sym
|
114
|
-
validates "#{as_name.to_s}_id".to_sym, :presence => true
|
115
|
-
|
116
|
-
#Prefill the hashes from the DB
|
117
|
-
all_vals = cls.all
|
118
|
-
cls.class_variable_set(:@@rcaches, all_vals.inject({}) do |r, obj|
|
119
|
-
r[obj.name] = obj.id
|
120
|
-
r
|
121
|
-
end)
|
122
|
-
cls.class_variable_set(:@@caches, all_vals.inject([]) do |r, obj|
|
123
|
-
r[obj.id] = obj.name
|
124
|
-
r
|
125
|
-
end)
|
126
146
|
end
|
127
|
-
end
|
128
147
|
|
129
|
-
|
130
|
-
|
131
|
-
|
148
|
+
# extend host class with class methods when we're included
|
149
|
+
def self.included(host_class)
|
150
|
+
host_class.extend(ClassMethods)
|
151
|
+
end
|
132
152
|
end
|
133
153
|
end
|
134
|
-
end
|
data/rails_lookup.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rails_lookup}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = [%q{Nimrod Priell}]
|
9
|
-
s.date = %q{2011-08-
|
9
|
+
s.date = %q{2011-08-08}
|
10
10
|
s.description = %q{Lookup tables with ruby-on-rails
|
11
11
|
--------------------------------
|
12
12
|
|
@@ -14,6 +14,8 @@ By: Nimrod Priell
|
|
14
14
|
|
15
15
|
This gem adds an ActiveRecord macro to define memory-cached, dynamically growing, normalized lookup tables for entity 'type'-like objects. Or in plain English - if you want to have a table containing, say, ProductTypes which can grow with new types simply when you refer to them, and not keep the Product table containing a thousand repeating 'type="book"' entries - sit down and try to follow through.
|
16
16
|
|
17
|
+
Installation is described down the page.
|
18
|
+
|
17
19
|
Motivation
|
18
20
|
----------
|
19
21
|
|
@@ -90,6 +92,7 @@ I've looked thoroughly for a nice rails solution to this, but after failing to f
|
|
90
92
|
|
91
93
|
Installation
|
92
94
|
------------
|
95
|
+
First, run `gem install rails_lookup` to install the latest version of the gem.
|
93
96
|
|
94
97
|
The result of this hook is that you get the exact syntax described above, with only two lines of code (no extra classes or anything):
|
95
98
|
In your ActiveRecord object simply add
|
@@ -149,13 +152,14 @@ I hope this helped you and saved a lot of time and frustration. Follow me on twi
|
|
149
152
|
}
|
150
153
|
s.email = %q{@nimrodpriell}
|
151
154
|
s.extra_rdoc_files = [%q{README}, %q{lib/active_record/lookup.rb}]
|
152
|
-
s.files = [%q{README}, %q{Rakefile}, %q{lib/active_record/lookup.rb}, %q{
|
155
|
+
s.files = [%q{README}, %q{Rakefile}, %q{lib/active_record/lookup.rb}, %q{rails_lookup.gemspec}, %q{test/20110808002412_create_test_lookup_db.rb}, %q{test/test_lookup.rb} ]
|
153
156
|
s.homepage = %q{http://github.com/Nimster/RailsLookup/}
|
154
157
|
s.rdoc_options = [%q{--line-numbers}, %q{--inline-source}, %q{--title}, %q{Rails_lookup}, %q{--main}, %q{README}]
|
155
158
|
s.require_paths = [%q{lib}]
|
156
159
|
s.rubyforge_project = %q{rails_lookup}
|
157
160
|
s.rubygems_version = %q{1.8.7}
|
158
161
|
s.summary = %q{Lookup table macro for ActiveRecords}
|
162
|
+
s.test_files = [%q{test/test_lookup.rb}]
|
159
163
|
|
160
164
|
if s.respond_to? :specification_version then
|
161
165
|
s.specification_version = 3
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class CreateTestLookupDb < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :cars do |t|
|
4
|
+
t.integer :kind
|
5
|
+
t.integer :color
|
6
|
+
t.string :name
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :car_kinds do |t|
|
12
|
+
t.string :name
|
13
|
+
end
|
14
|
+
create_table :planes do |t|
|
15
|
+
t.integer :kind
|
16
|
+
t.string :name
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
create_table :plane_kinds do |t|
|
21
|
+
t.string :name
|
22
|
+
end
|
23
|
+
create_table :car_colors do |t|
|
24
|
+
t.string :name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.down
|
29
|
+
drop_table :cars
|
30
|
+
drop_table :car_kinds
|
31
|
+
drop_table :car_colors
|
32
|
+
drop_table :planes
|
33
|
+
drop_table :plane_kinds
|
34
|
+
end
|
35
|
+
end
|
data/test/test_lookup.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'lookup'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
class Car < ActiveRecord::Base
|
5
|
+
include Lookup
|
6
|
+
lookup :car_kind, :as => :kind
|
7
|
+
lookup :car_color, :as => :color
|
8
|
+
end
|
9
|
+
|
10
|
+
class Plane < ActiveRecord::Base
|
11
|
+
include Lookup
|
12
|
+
lookup :plane_kind, :as => :kind
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestLookup < MiniTest::Unit::TestCase
|
16
|
+
|
17
|
+
def setup
|
18
|
+
clear_tables
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
clear_tables
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear_tables
|
26
|
+
Car.delete_all
|
27
|
+
CarKind.delete_all
|
28
|
+
CarColor.delete_all
|
29
|
+
Plane.delete_all
|
30
|
+
PlaneKind.delete_all
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_initializers
|
34
|
+
assert_equal 0, CarKind.count
|
35
|
+
assert_equal 0, CarColor.count
|
36
|
+
bimba = Car.new(:name => "Bimba", :kind => "Compact", :color => "Yellow")
|
37
|
+
assert_equal "Yellow", bimba.color
|
38
|
+
assert_equal "Compact", bimba.kind
|
39
|
+
assert_equal "Bimba", bimba.name
|
40
|
+
assert_equal 1, CarKind.count
|
41
|
+
assert_equal 1, CarColor.count
|
42
|
+
ferrari = Car.new(:kind => "Sports", :color => "Yellow")
|
43
|
+
assert_equal "Yellow", ferrari.color
|
44
|
+
assert_equal "Sports", ferrari.kind
|
45
|
+
refute(ferrari == bimba)
|
46
|
+
assert_equal bimba.color_id, ferrari.color_id
|
47
|
+
refute_equal bimba.kind_id, ferrari.kind_id
|
48
|
+
assert_equal 2, CarKind.count
|
49
|
+
assert_equal 1, CarColor.count
|
50
|
+
f16 = Plane.new(:name => "F-16", :kind => "Fighter Jet")
|
51
|
+
assert_equal "Fighter Jet", f16.kind
|
52
|
+
assert_equal 1, PlaneKind.count
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_saving_and_creating
|
56
|
+
bimba = Car.new(:name => "Bimba", :kind => "Compact", :color => "Yellow")
|
57
|
+
assert_equal "Yellow", bimba.color
|
58
|
+
assert_equal "Compact", bimba.kind
|
59
|
+
assert_equal "Bimba", bimba.name
|
60
|
+
bimba.save!
|
61
|
+
bimba_reloaded = Car.all.first
|
62
|
+
assert_equal "Yellow", bimba_reloaded.color
|
63
|
+
assert_equal "Compact", bimba_reloaded.kind
|
64
|
+
assert_equal "Bimba", bimba_reloaded.name
|
65
|
+
ferrari = Car.create!(:kind => "Sports", :color => "Yellow")
|
66
|
+
assert_equal "Yellow", ferrari.color
|
67
|
+
assert_equal "Sports", ferrari.kind
|
68
|
+
ferrari_reloaded = Car.all.last
|
69
|
+
assert_equal "Yellow", ferrari_reloaded.color
|
70
|
+
assert_equal "Sports", ferrari_reloaded.kind
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_string_setting_and_getting
|
74
|
+
bimba = Car.new
|
75
|
+
ferrari = Car.new
|
76
|
+
assert_equal 0, CarKind.count
|
77
|
+
assert_equal 0, CarColor.count
|
78
|
+
bimba.color = "Yellow"
|
79
|
+
bimba.name = "Bimba"
|
80
|
+
bimba.kind = "Compact"
|
81
|
+
assert_equal "Yellow", bimba.color
|
82
|
+
assert_equal "Compact", bimba.kind
|
83
|
+
assert_equal "Bimba", bimba.name
|
84
|
+
assert_equal 1, CarKind.count
|
85
|
+
assert_equal 1, CarColor.count
|
86
|
+
ferrari.color = "Yellow"
|
87
|
+
ferrari.kind = "Sports"
|
88
|
+
assert_equal "Yellow", ferrari.color
|
89
|
+
assert_equal "Sports", ferrari.kind
|
90
|
+
assert_equal bimba.color_id, ferrari.color_id
|
91
|
+
refute_equal bimba.kind_id, ferrari.kind_id
|
92
|
+
assert_equal 2, CarKind.count
|
93
|
+
assert_equal 1, CarColor.count
|
94
|
+
bimba.color = "Red"
|
95
|
+
assert_equal 2, CarColor.count
|
96
|
+
assert_equal "Red", bimba.color
|
97
|
+
refute(bimba.color == ferrari.color)
|
98
|
+
bimba.color_id = ferrari.color_id #Set via the property_id
|
99
|
+
assert_equal "Yellow", bimba.color
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_id_for_name_for
|
103
|
+
bimba = Car.new
|
104
|
+
assert_nil CarKind.id_for("Compact")
|
105
|
+
assert_nil CarKind.name_for(1)
|
106
|
+
bimba.kind = "Compact"
|
107
|
+
assert_equal bimba.kind_id, CarKind.id_for("Compact")
|
108
|
+
assert_equal "Compact", CarKind.name_for(bimba.kind_id)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_find_by
|
112
|
+
bimba = Car.create! :name => "Bimba", :kind => "Compact", :color => "Yellow"
|
113
|
+
ferrari = Car.create! :kind => "Sports", :color => "Yellow"
|
114
|
+
bimba2 = Car.find_by_kind_and_name "Compact", "Bimba"
|
115
|
+
assert_equal bimba, bimba2
|
116
|
+
assert_equal "Yellow", bimba2.color
|
117
|
+
bimba2 = Car.find_by_name_and_kind "Bimba", "Compact"
|
118
|
+
assert_equal bimba, bimba2
|
119
|
+
assert_equal "Yellow", bimba2.color
|
120
|
+
bimba2 = Car.find_by_kind "Compact"
|
121
|
+
assert_equal bimba, bimba2
|
122
|
+
assert_equal "Yellow", bimba2.color
|
123
|
+
bimba2 = Car.find_by_kind_and_color_and_name "Compact", "Yellow", "Bimba"
|
124
|
+
assert_equal bimba, bimba2
|
125
|
+
assert_equal "Yellow", bimba2.color
|
126
|
+
assert_equal "Compact", bimba2.kind
|
127
|
+
susita = Car.find_or_create_by_name_and_kind_and_color "Susita", "Compact", "Gray"
|
128
|
+
assert_equal "Gray", susita.color
|
129
|
+
assert_equal "Compact", susita.kind
|
130
|
+
bimba2 = Car.find_or_create_by_name_and_kind_and_color "Bimba", "Compact", "Yellow"
|
131
|
+
assert_equal "Yellow", bimba2.color
|
132
|
+
assert_equal "Compact", bimba2.kind
|
133
|
+
assert_equal "Bimba", bimba2.name
|
134
|
+
f16 = Plane.find_or_create_by_kind_and_name "Fighter Jet", "F-16"
|
135
|
+
assert_equal "Fighter Jet", f16.kind
|
136
|
+
assert_equal "F-16", f16.name
|
137
|
+
ferrari = Car.find_or_create_by_kind_and_color "Sports", "Yellow"
|
138
|
+
assert_equal "Yellow", ferrari.color
|
139
|
+
assert_equal "Sports", ferrari.kind
|
140
|
+
batmobile = Car.find_or_create_by_kind_and_color "Fantasy", "Black"
|
141
|
+
assert_equal "Black", batmobile.color
|
142
|
+
assert_equal "Fantasy", batmobile.kind
|
143
|
+
assert_equal 3, CarKind.count
|
144
|
+
assert_equal 3, CarColor.count
|
145
|
+
assert_equal 1, PlaneKind.count
|
146
|
+
end
|
147
|
+
|
148
|
+
#This test will issue warnings as we re-set existing constants
|
149
|
+
def test_preload_caches
|
150
|
+
#We have to define a new ActiveRecord class for this
|
151
|
+
CarKind.create! name: "Compact"
|
152
|
+
CarKind.create! name: "Sports"
|
153
|
+
assert_nil CarKind.id_for("Compact")
|
154
|
+
assert_nil CarKind.id_for("Sports")
|
155
|
+
#We get a new CarKind class - but this one will be preloaded
|
156
|
+
tmp = CarKind
|
157
|
+
Class.new(ActiveRecord::Base) do
|
158
|
+
include Lookup
|
159
|
+
lookup :car_kind
|
160
|
+
end
|
161
|
+
refute_nil CarKind.id_for("Compact")
|
162
|
+
refute_nil CarKind.id_for("Sports")
|
163
|
+
refute_equal CarKind.id_for("Compact"), CarKind.id_for("Sports")
|
164
|
+
assert_equal "Compact", CarKind.name_for(CarKind.id_for("Compact"))
|
165
|
+
assert_equal "Sports", CarKind.name_for(CarKind.id_for("Sports"))
|
166
|
+
#Revert to the old class
|
167
|
+
#Object.const_set "CarKind", tmp
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_where
|
171
|
+
bimba = Car.create! :name => "Bimba", :kind => "Compact", :color => "Yellow"
|
172
|
+
ferrari = Car.create! :kind => "Sports", :color => "Yellow"
|
173
|
+
assert_equal 1, Car.where(kind: "Compact").count
|
174
|
+
assert_equal 2, Car.where(color: "Yellow").count
|
175
|
+
assert_equal 0, Car.where(color: "Red").count
|
176
|
+
assert_equal 1, Car.where(color: "Yellow", kind: "Compact").count
|
177
|
+
assert_equal 1, Car.where(name: "Bimba", kind: "Compact").count
|
178
|
+
end
|
179
|
+
|
180
|
+
#TODO: This currently fails
|
181
|
+
def test_belongs_to
|
182
|
+
bimba = Car.create! :name => "Bimba", :kind => "Compact", :color => "Yellow"
|
183
|
+
ferrari = Car.create! :kind => "Sports", :color => "Yellow"
|
184
|
+
# yellow = CarColor.find_by_name "Yellow"
|
185
|
+
# assert_equal 2, yellow.cars.size
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
=begin
|
190
|
+
a = TestLookup.new
|
191
|
+
puts "Cache hits: "
|
192
|
+
puts(TestLookupKind.id_for "simian")
|
193
|
+
puts "Setting test_lookup_kind: "
|
194
|
+
a.test_lookup_kind="simian"
|
195
|
+
puts "Reading test_lookup_kind: "
|
196
|
+
puts a.test_lookup_kind
|
197
|
+
puts a.test_lookup_kind_id
|
198
|
+
|
199
|
+
puts "Hacking the ID: "
|
200
|
+
a.test_lookup_kind_id = 1
|
201
|
+
puts a.test_lookup_kind
|
202
|
+
|
203
|
+
puts "Setting another_lookup_kind: "
|
204
|
+
a.another_lookup_kind="simian"
|
205
|
+
puts "Reading another_lookup_kind: "
|
206
|
+
puts a.another_lookup_kind
|
207
|
+
puts a.another_lookup_kind_id
|
208
|
+
|
209
|
+
b = TestLookupKind.new
|
210
|
+
puts "HERE"
|
211
|
+
p TestLookupKind.all
|
212
|
+
|
213
|
+
puts "Testing different class"
|
214
|
+
b = OneMore.new
|
215
|
+
b.taverna_kind = "williwok"
|
216
|
+
puts b.taverna_kind
|
217
|
+
puts b.taverna_kind_id
|
218
|
+
|
219
|
+
puts "Testing original class: "
|
220
|
+
puts a.test_lookup_kind
|
221
|
+
puts a.another_lookup_kind
|
222
|
+
|
223
|
+
=end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rails_lookup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nimrod Priell
|
@@ -10,13 +10,14 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-08-
|
13
|
+
date: 2011-08-08 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: "Lookup tables with ruby-on-rails\n\
|
17
17
|
--------------------------------\n\n\
|
18
18
|
By: Nimrod Priell\n\n\
|
19
19
|
This gem adds an ActiveRecord macro to define memory-cached, dynamically growing, normalized lookup tables for entity 'type'-like objects. Or in plain English - if you want to have a table containing, say, ProductTypes which can grow with new types simply when you refer to them, and not keep the Product table containing a thousand repeating 'type=\"book\"' entries - sit down and try to follow through.\n\n\
|
20
|
+
Installation is described down the page.\n\n\
|
20
21
|
Motivation\n\
|
21
22
|
----------\n\n\
|
22
23
|
A [normalized DB][1] means that you want to keep types as separate tables, with foreign keys pointing from your main entity to its type. For instance, instead of \n\
|
@@ -51,7 +52,8 @@ description: "Lookup tables with ruby-on-rails\n\
|
|
51
52
|
But you wanna minimize generating these numerous type classes. If you're like me, you don't even want to see them lying around in app/model. Who cares about them?\n\n\
|
52
53
|
I've looked thoroughly for a nice rails solution to this, but after failing to find one, I created my own rails metaprogramming hook.\n\n\
|
53
54
|
Installation\n\
|
54
|
-
------------\n\
|
55
|
+
------------\n\
|
56
|
+
First, run `gem install rails_lookup` to install the latest version of the gem.\n\n\
|
55
57
|
The result of this hook is that you get the exact syntax described above, with only two lines of code (no extra classes or anything):\n\
|
56
58
|
In your ActiveRecord object simply add\n\n require 'active_record/lookup'\n class Car < ActiveRecord::Base\n #...\n include ActiveRecord::Lookup\n lookup :car_type\n #...\n end\n\n\
|
57
59
|
That's it. the generated CarType class (which you won't see as a car_type.rb file, obviously, as it is generated in real-time), contains some nice methods to look into the cache as well: So you can call\n\n CarType.id_for \"Sports\" #Returns 2\n CarType.name_for 1 #Returns \"Compact\"\n\n\
|
@@ -74,8 +76,9 @@ files:
|
|
74
76
|
- README
|
75
77
|
- Rakefile
|
76
78
|
- lib/active_record/lookup.rb
|
77
|
-
- Manifest
|
78
79
|
- rails_lookup.gemspec
|
80
|
+
- test/20110808002412_create_test_lookup_db.rb
|
81
|
+
- test/test_lookup.rb
|
79
82
|
homepage: http://github.com/Nimster/RailsLookup/
|
80
83
|
licenses: []
|
81
84
|
|
@@ -108,5 +111,5 @@ rubygems_version: 1.8.7
|
|
108
111
|
signing_key:
|
109
112
|
specification_version: 3
|
110
113
|
summary: Lookup table macro for ActiveRecords
|
111
|
-
test_files:
|
112
|
-
|
114
|
+
test_files:
|
115
|
+
- test/test_lookup.rb
|
data/Manifest
DELETED