doeskeyvalue 0.2.2 → 0.9.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/.rspec +1 -0
- data/Gemfile +14 -4
- data/Gemfile.lock +42 -7
- data/LICENSE.txt +1 -1
- data/README.rdoc +102 -67
- data/Rakefile +43 -19
- data/VERSION +1 -1
- data/doeskeyvalue.gemspec +43 -22
- data/lib/doeskeyvalue.rb +46 -79
- data/lib/doeskeyvalue/accessors.rb +136 -0
- data/lib/doeskeyvalue/column_storage.rb +95 -0
- data/lib/doeskeyvalue/configuration.rb +32 -0
- data/lib/doeskeyvalue/index.rb +145 -0
- data/lib/doeskeyvalue/state.rb +48 -0
- data/lib/doeskeyvalue/table_storage.rb +79 -0
- data/lib/doeskeyvalue/util.rb +25 -18
- data/lib/generators/doeskeyvalue/doeskeyvalue_generator.rb +9 -5
- data/lib/generators/doeskeyvalue/templates/create_key_value_index.rb +22 -7
- data/spec/doeskeyvalue/column_storage_spec.rb +139 -0
- data/spec/doeskeyvalue/table_storage_spec.rb +140 -0
- data/spec/spec_helper.rb +25 -0
- metadata +151 -57
- data/.document +0 -5
- data/lib/doeskeyvalue/indexes.rb +0 -104
- data/lib/doeskeyvalue/key_manager.rb +0 -116
- data/lib/doeskeyvalue/keys.rb +0 -46
@@ -0,0 +1,48 @@
|
|
1
|
+
# AWEXOME LABS
|
2
|
+
# DoesKeyValue
|
3
|
+
#
|
4
|
+
# State -- Singleton state class representing the state of supported objects,
|
5
|
+
# known keys, and powered indexes
|
6
|
+
|
7
|
+
require "singleton"
|
8
|
+
|
9
|
+
module DoesKeyValue
|
10
|
+
class State
|
11
|
+
|
12
|
+
# There can be only one!
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
attr_reader :classes, :keys
|
16
|
+
|
17
|
+
# Add support for a single class of objects:
|
18
|
+
def add_class(klass, opts={})
|
19
|
+
DoesKeyValue.log("State: Add support for class #{klass.to_s} with opts:#{opts.inspect}")
|
20
|
+
@classes ||= Hash.new
|
21
|
+
@classes[klass.to_s] = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a key for a given class:
|
25
|
+
def add_key(klass, key_name, opts={})
|
26
|
+
DoesKeyValue.log("State: Add key #{key_name} to class #{klass.to_s} with opts:#{opts.inspect}")
|
27
|
+
@keys ||= Hash.new
|
28
|
+
@keys[klass.to_s] ||= Array.new
|
29
|
+
@keys[klass.to_s] << {name:key_name, options:opts}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the configuration for a given klass:
|
33
|
+
def options_for_class(klass)
|
34
|
+
DoesKeyValue.log("State: Querying options_for_class for class:#{klass.to_s}")
|
35
|
+
@classes[klass.to_s] rescue {}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the list of keys for a given klass:
|
39
|
+
def options_for_key(klass, key_name)
|
40
|
+
DoesKeyValue.log("State: Querying options_for_key for class:#{klass.to_s} and key:#{key_name}")
|
41
|
+
key_def = (@keys[klass.to_s] || Array.new).find{|x| x[:name]==key_name.to_sym} || {}
|
42
|
+
opts = key_def[:options]
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end # State
|
47
|
+
end # DoesKeyValue
|
48
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# AWEXOME LABS
|
2
|
+
# DoesKeyValue
|
3
|
+
#
|
4
|
+
# TableStorage -- Support and methods for key-value pairs stored in an altogether
|
5
|
+
# separate database table.
|
6
|
+
|
7
|
+
module DoesKeyValue
|
8
|
+
module TableStorage
|
9
|
+
|
10
|
+
# Define a supported key
|
11
|
+
def has_key(key_name, opts={})
|
12
|
+
opts = {
|
13
|
+
index: true,
|
14
|
+
type: "string",
|
15
|
+
default: nil
|
16
|
+
}.merge(opts)
|
17
|
+
|
18
|
+
key_indexed = opts[:index]
|
19
|
+
key_type = opts[:type].to_sym
|
20
|
+
key_default_value = opts[:default]
|
21
|
+
|
22
|
+
raise Exception.new("Data type not supported: #{key_type}") unless DoesKeyValue::SUPPORTED_DATA_TYPES.include?(key_type.to_s)
|
23
|
+
|
24
|
+
DoesKeyValue::State.instance.add_key(self, key_name, opts)
|
25
|
+
storage_table = DoesKeyValue::State.instance.options_for_class(self)[:table]
|
26
|
+
|
27
|
+
# Accessor for new key with support for default value:
|
28
|
+
define_method(key_name) do
|
29
|
+
DoesKeyValue.log("Accessing BY TABLE key:#{key_name} for class:#{self.class}")
|
30
|
+
if self.new_record?
|
31
|
+
DoesKeyValue.log("-- Object does not have a database ID. No resource to query against.")
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
|
35
|
+
value = DoesKeyValue::Index.read_index(self, key_name)
|
36
|
+
if !value.nil?
|
37
|
+
return value
|
38
|
+
elsif default_value = self.class.key_options(key_name)[:default]
|
39
|
+
return default_value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Manipulator for new key:
|
44
|
+
define_method("#{key_name}=") do |value|
|
45
|
+
DoesKeyValue.log("Modifying BY TABLE value for key:#{key_name} to value:#{value}")
|
46
|
+
unless self.new_record?
|
47
|
+
DoesKeyValue::Index.update_index(self, key_name, value)
|
48
|
+
else
|
49
|
+
DoesKeyValue.log("-- Object does not have a database ID. Holding back table index update.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# All table-based key-value stores have index finders and scopes:
|
54
|
+
scope "with_#{key_name}", lambda {|value|
|
55
|
+
DoesKeyValue::Index.find_objects(self, key_name, value)
|
56
|
+
}
|
57
|
+
DoesKeyValue.log("Scope with_#{key_name} added for table-storage key #{key_name}")
|
58
|
+
|
59
|
+
# Delete the index after destroy:
|
60
|
+
define_method("destroy_index_for_#{key_name}") do
|
61
|
+
DoesKeyValue::Index.delete_index(self, key_name)
|
62
|
+
end
|
63
|
+
after_destroy "destroy_index_for_#{key_name}"
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Return a list of currently supported keys:
|
69
|
+
def keys
|
70
|
+
DoesKeyValue::State.instance.keys[self.to_s]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return the specific configuration for a given key:
|
74
|
+
def key_options(key_name)
|
75
|
+
DoesKeyValue::State.instance.options_for_key(self, key_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
end # TableStorage
|
79
|
+
end # DoesKeyValue
|
data/lib/doeskeyvalue/util.rb
CHANGED
@@ -1,27 +1,34 @@
|
|
1
1
|
# AWEXOME LABS
|
2
2
|
# DoesKeyValue
|
3
|
+
#
|
4
|
+
# Util -- Utility methods and helpers for use in data access and manipulation
|
3
5
|
|
6
|
+
require "singleton"
|
4
7
|
|
5
8
|
module DoesKeyValue
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
class Util
|
10
|
+
|
11
|
+
# Convert a value to the given type:
|
12
|
+
def self.to_type(value, type)
|
13
|
+
DoesKeyValue.log("Converting type of value:#{value} to type:#{type}")
|
14
|
+
case type.to_sym
|
15
|
+
when :string
|
16
|
+
value.to_s
|
17
|
+
when :integer
|
18
|
+
value.to_i
|
19
|
+
when :boolean
|
20
|
+
converted = true if value == true || value =~ /(true|t|yes|y|1)$/i
|
21
|
+
converted = false if value == false || value =~ /(false|f|no|n|0)$/i
|
22
|
+
converted
|
23
|
+
when :decimal
|
24
|
+
value.to_f
|
25
|
+
when :datetime
|
26
|
+
value.to_datetime
|
19
27
|
else
|
20
|
-
|
21
|
-
end
|
22
|
-
self
|
28
|
+
value
|
23
29
|
end
|
24
30
|
end
|
25
|
-
|
26
|
-
end
|
31
|
+
|
32
|
+
end # Util
|
27
33
|
end # DoesKeyValue
|
34
|
+
|
@@ -1,14 +1,15 @@
|
|
1
1
|
# AWEXOME LABS
|
2
2
|
# DoesKeyValue
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# Generator -- Migration generator for the column-backed index table and/or
|
5
|
+
# the table-backed storage tables
|
5
6
|
|
6
|
-
require
|
7
|
-
require
|
7
|
+
require "rails/generators"
|
8
|
+
require "rails/generators/migration"
|
8
9
|
|
9
10
|
class DoeskeyvalueGenerator < Rails::Generators::Base
|
10
11
|
include Rails::Generators::Migration
|
11
|
-
source_root File.expand_path(
|
12
|
+
source_root File.expand_path("../templates", __FILE__)
|
12
13
|
|
13
14
|
def self.next_migration_number(path)
|
14
15
|
if ActiveRecord::Base.timestamped_migrations
|
@@ -19,7 +20,10 @@ class DoeskeyvalueGenerator < Rails::Generators::Base
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def create_migration_file
|
22
|
-
|
23
|
+
puts "Creating DoesKeyValue index table migration"
|
24
|
+
index_table_name = ARGV.first || "key_value_index"
|
25
|
+
puts "=> #{index_table_name} table to be created"
|
26
|
+
migration_template "create_key_value_index.rb", "db/migrate/create_#{index_table_name}.rb", table_name:index_table_name
|
23
27
|
end
|
24
28
|
|
25
29
|
end
|
@@ -3,22 +3,37 @@
|
|
3
3
|
#
|
4
4
|
# CreateKeyValueIndex -- generated migration template for key/value index table
|
5
5
|
|
6
|
-
|
6
|
+
<%
|
7
|
+
table_name = config[:table_name]
|
8
|
+
-%>
|
9
|
+
|
10
|
+
class Create<%=table_name.camelize-%> < ActiveRecord::Migration
|
7
11
|
def self.up
|
8
|
-
create_table
|
12
|
+
create_table :<%=table_name-%> do |t|
|
13
|
+
# The object is linked by class type and id:
|
9
14
|
t.string :obj_type
|
10
|
-
t.string :key_name
|
11
|
-
t.string :value
|
12
15
|
t.integer :obj_id
|
16
|
+
|
17
|
+
# The key is identified by name and data type:
|
18
|
+
t.string :key_name
|
19
|
+
t.string :key_type
|
13
20
|
|
21
|
+
# The value is stored in various possible formats:
|
22
|
+
t.string :value_string
|
23
|
+
t.integer :value_integer
|
24
|
+
t.decimal :value_decimal
|
25
|
+
t.boolean :value_boolean
|
26
|
+
t.datetime :value_datetime
|
27
|
+
|
28
|
+
# Traditional record-keeping for the index:
|
14
29
|
t.timestamps
|
15
30
|
end
|
16
31
|
|
17
32
|
# Index is important here:
|
18
|
-
add_index
|
33
|
+
add_index :<%=table_name-%>, [:obj_type, :key_name]
|
19
34
|
end
|
20
35
|
|
21
36
|
def self.down
|
22
|
-
drop_table
|
37
|
+
drop_table :<%=table_name%>
|
23
38
|
end
|
24
|
-
end
|
39
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# AWEXOME LABS
|
2
|
+
# DoesKeyValue Test Suite : Column Storage
|
3
|
+
|
4
|
+
# Get started:
|
5
|
+
require File.expand_path(File.dirname(__FILE__)+"/../spec_helper")
|
6
|
+
|
7
|
+
# Create schema for sample User model for testing column-based storage:
|
8
|
+
ActiveRecord::Base.connection.drop_table(:users) if ActiveRecord::Base.connection.table_exists?(:users)
|
9
|
+
ActiveRecord::Base.connection.create_table(:users) do |t|
|
10
|
+
t.string :name
|
11
|
+
t.string :email
|
12
|
+
t.text :settings
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
# Build the generator-style key value index table:
|
17
|
+
ActiveRecord::Base.connection.drop_table(:key_value_index) if ActiveRecord::Base.connection.table_exists?(:key_value_index)
|
18
|
+
ActiveRecord::Base.connection.create_table(:key_value_index) do |t|
|
19
|
+
t.string :obj_type
|
20
|
+
t.integer :obj_id
|
21
|
+
t.string :key_name
|
22
|
+
t.string :key_type
|
23
|
+
t.string :value_string
|
24
|
+
t.integer :value_integer
|
25
|
+
t.decimal :value_decimal
|
26
|
+
t.boolean :value_boolean
|
27
|
+
t.datetime :value_datetime
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Define the sample User model which will exhibit column-based storage:
|
33
|
+
class User < ActiveRecord::Base
|
34
|
+
attr_accessible :name, :email, :settings
|
35
|
+
has_many :posts
|
36
|
+
|
37
|
+
# Key-Value storage:
|
38
|
+
does_keys :column=>"settings"
|
39
|
+
has_key :string_key
|
40
|
+
has_key :integer_key, :type=>:integer
|
41
|
+
has_key :decimal_key, :type=>:decimal
|
42
|
+
has_key :bool_key, :type=>:boolean
|
43
|
+
has_key :date_key, :type=>:datetime
|
44
|
+
has_key :default_val_key, :default=>"The Default"
|
45
|
+
has_key :indexless_key, :index=>false
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Test the ColumnStorage Module against the sample User class
|
50
|
+
describe "column_storage" do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
54
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
55
|
+
@user = User.new
|
56
|
+
end
|
57
|
+
|
58
|
+
after(:each) do
|
59
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
60
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
61
|
+
end
|
62
|
+
|
63
|
+
it "defines key read accessors" do
|
64
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key indexless_key).each do |key_name|
|
65
|
+
@user.respond_to?( key_name.to_sym )
|
66
|
+
@user.methods.include?( key_name.to_sym ).should be_true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "defines key write accessors" do
|
71
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key indexless_key).each do |key_name|
|
72
|
+
@user.respond_to?( "#{key_name}=".to_sym )
|
73
|
+
@user.methods.include?( "#{key_name}=".to_sym ).should be_true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns default value if none defined" do
|
78
|
+
@user.default_val_key.should == "The Default"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "saves and returns the same value for keys" do
|
82
|
+
@user.string_key = "Ron Swanson"
|
83
|
+
@user.save
|
84
|
+
@user.reload
|
85
|
+
@user.string_key.should == "Ron Swanson"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "sets a string value when assigned" do
|
89
|
+
@user.string_key = "Hello"
|
90
|
+
@user.string_key.should == "Hello"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "sets an integer value when assigned" do
|
94
|
+
@user.integer_key = 123
|
95
|
+
@user.integer_key.should == 123
|
96
|
+
@user.integer_key.class.should == Fixnum
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets a decimal value when assigned" do
|
100
|
+
@user.decimal_key = 12.21
|
101
|
+
@user.decimal_key.should == 12.21
|
102
|
+
@user.decimal_key.class.should == Float
|
103
|
+
end
|
104
|
+
|
105
|
+
it "sets a boolean value when assigned" do
|
106
|
+
@user.bool_key = true
|
107
|
+
@user.bool_key.should be_true
|
108
|
+
@user.bool_key = false
|
109
|
+
@user.bool_key.should be_false
|
110
|
+
end
|
111
|
+
|
112
|
+
it "sets a datetime value when assigned" do
|
113
|
+
d0 = DateTime.now
|
114
|
+
@user.date_key = d0
|
115
|
+
@user.date_key.should == d0
|
116
|
+
@user.date_key.class.should == DateTime
|
117
|
+
end
|
118
|
+
|
119
|
+
it "defines with_ scope for indexed keys" do
|
120
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key).each do |key_name|
|
121
|
+
User.methods.include?("with_#{key_name}".to_sym).should be_true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "does not define with_ scope for non-indexed keys" do
|
126
|
+
User.methods.include?(:with_indexless_key).should be_false
|
127
|
+
end
|
128
|
+
|
129
|
+
it "finds objects via the scope and index" do
|
130
|
+
@user.string_key = "Champion"
|
131
|
+
@user.save
|
132
|
+
find_results = User.with_string_key("Champion")
|
133
|
+
find_results.length.should == 1
|
134
|
+
find_results.first.should == @user
|
135
|
+
end
|
136
|
+
|
137
|
+
end # column_stage
|
138
|
+
|
139
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# AWEXOME LABS
|
2
|
+
# DoesKeyValue Test Suite : Table Storage
|
3
|
+
|
4
|
+
# Get started:
|
5
|
+
#require File.expand_path(File.dirname(__FILE__)+"/../spec_helper")
|
6
|
+
|
7
|
+
# Create schema for sample User model for testing column-based storage:
|
8
|
+
ActiveRecord::Base.connection.drop_table(:posts) if ActiveRecord::Base.connection.table_exists?(:posts)
|
9
|
+
ActiveRecord::Base.connection.create_table(:posts) do |t|
|
10
|
+
t.integer :user_id
|
11
|
+
t.string :title
|
12
|
+
t.text :body
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
# Build the generator-style key value index table:
|
17
|
+
ActiveRecord::Base.connection.drop_table(:post_preferences) if ActiveRecord::Base.connection.table_exists?(:post_preferences)
|
18
|
+
ActiveRecord::Base.connection.create_table(:post_preferences) do |t|
|
19
|
+
t.string :obj_type
|
20
|
+
t.integer :obj_id
|
21
|
+
t.string :key_name
|
22
|
+
t.string :key_type
|
23
|
+
t.string :value_string
|
24
|
+
t.integer :value_integer
|
25
|
+
t.decimal :value_decimal
|
26
|
+
t.boolean :value_boolean
|
27
|
+
t.datetime :value_datetime
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Define the sample Post model which will exhibit table-based storage:
|
33
|
+
class Post < ActiveRecord::Base
|
34
|
+
attr_accessible :user_id, :title, :body
|
35
|
+
belongs_to :user
|
36
|
+
|
37
|
+
# Key-Value storage:
|
38
|
+
does_keys :table=>"post_preferences"
|
39
|
+
has_key :string_key
|
40
|
+
has_key :integer_key, :type=>:integer
|
41
|
+
has_key :decimal_key, :type=>:decimal
|
42
|
+
has_key :bool_key, :type=>:boolean
|
43
|
+
has_key :date_key, :type=>:datetime
|
44
|
+
has_key :default_val_key, :default=>"The Default"
|
45
|
+
has_key :indexless_key, :index=>false
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Test the ColumnStorage Module against the sample User class
|
50
|
+
describe "table_storage" do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
54
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
55
|
+
@post = Post.new
|
56
|
+
@post.save
|
57
|
+
end
|
58
|
+
|
59
|
+
after(:each) do
|
60
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
61
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
62
|
+
end
|
63
|
+
|
64
|
+
it "defines key read accessors" do
|
65
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key indexless_key).each do |key_name|
|
66
|
+
@post.respond_to?( key_name.to_sym )
|
67
|
+
@post.methods.include?( key_name.to_sym ).should be_true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "defines key write accessors" do
|
72
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key indexless_key).each do |key_name|
|
73
|
+
@post.respond_to?( "#{key_name}=".to_sym )
|
74
|
+
@post.methods.include?( "#{key_name}=".to_sym ).should be_true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns default value if none defined" do
|
79
|
+
@post.default_val_key.should == "The Default"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "saves and returns the same value for keys" do
|
83
|
+
@post.string_key = "Ron Swanson"
|
84
|
+
@post.save
|
85
|
+
@post.reload
|
86
|
+
@post.string_key.should == "Ron Swanson"
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets a string value when assigned" do
|
90
|
+
@post.string_key = "Hello"
|
91
|
+
@post.string_key.should == "Hello"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "sets an integer value when assigned" do
|
95
|
+
@post.integer_key = 123
|
96
|
+
@post.integer_key.should == 123
|
97
|
+
@post.integer_key.class.should == Fixnum
|
98
|
+
end
|
99
|
+
|
100
|
+
it "sets a decimal value when assigned" do
|
101
|
+
@post.decimal_key = 12.21
|
102
|
+
@post.decimal_key.should == 12.21
|
103
|
+
@post.decimal_key.class.should == Float
|
104
|
+
end
|
105
|
+
|
106
|
+
it "sets a boolean value when assigned" do
|
107
|
+
@post.bool_key = true
|
108
|
+
@post.bool_key.should be_true
|
109
|
+
@post.bool_key = false
|
110
|
+
@post.bool_key.should be_false
|
111
|
+
end
|
112
|
+
|
113
|
+
it "sets a datetime value when assigned" do
|
114
|
+
d0 = DateTime.now
|
115
|
+
@post.date_key = d0
|
116
|
+
@post.date_key.should == d0
|
117
|
+
@post.date_key.class.should == DateTime
|
118
|
+
end
|
119
|
+
|
120
|
+
it "defines with_ scope for indexed keys" do
|
121
|
+
%w(string_key integer_key decimal_key bool_key date_key default_val_key).each do |key_name|
|
122
|
+
User.methods.include?("with_#{key_name}".to_sym).should be_true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "does not define with_ scope for non-indexed keys" do
|
127
|
+
User.methods.include?(:with_indexless_key).should be_false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "finds objects via the scope and index" do
|
131
|
+
@post.string_key = "Champion"
|
132
|
+
@post.save
|
133
|
+
find_results = User.with_string_key("Champion")
|
134
|
+
find_results.length.should == 1
|
135
|
+
find_results.first.should == @post
|
136
|
+
end
|
137
|
+
|
138
|
+
end # column_stage
|
139
|
+
|
140
|
+
|