remodel-h 0.1.4
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/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/bin/redis-monitor.rb +25 -0
- data/docs/docco.css +185 -0
- data/docs/remodel.html +269 -0
- data/example/book.rb +13 -0
- data/lib/remodel/entity.rb +198 -0
- data/lib/remodel/has_many.rb +71 -0
- data/lib/remodel/mapper.rb +29 -0
- data/lib/remodel.rb +77 -0
- data/remodel-h.gemspec +72 -0
- data/remodel.gemspec +72 -0
- data/test/helper.rb +19 -0
- data/test/test_entity.rb +313 -0
- data/test/test_many_to_many.rb +77 -0
- data/test/test_many_to_one.rb +107 -0
- data/test/test_mappers.rb +67 -0
- data/test/test_monkeypatches.rb +30 -0
- data/test/test_one_to_many.rb +96 -0
- data/test/test_one_to_one.rb +57 -0
- metadata +85 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Remodel
|
2
|
+
|
3
|
+
# Represents the many-end of a many-to-one or many-to-many association.
|
4
|
+
class HasMany < Array
|
5
|
+
def initialize(this, clazz, key, reverse = nil)
|
6
|
+
super _fetch(clazz, key)
|
7
|
+
@this, @clazz, @key, @reverse = this, clazz, key, reverse
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(attributes = {})
|
11
|
+
add(@clazz.create(attributes))
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(id)
|
15
|
+
detect { |x| x.id == id } || raise(EntityNotFound, "no element with id #{id}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(entity)
|
19
|
+
_add_to_reverse_association_of(entity) if @reverse
|
20
|
+
_add(entity)
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove(entity)
|
24
|
+
_remove_from_reverse_association_of(entity) if @reverse
|
25
|
+
_remove(entity)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _add(entity)
|
31
|
+
self << entity
|
32
|
+
_store
|
33
|
+
entity
|
34
|
+
end
|
35
|
+
|
36
|
+
def _remove(entity)
|
37
|
+
delete_if { |x| x.key == entity.key }
|
38
|
+
_store
|
39
|
+
entity
|
40
|
+
end
|
41
|
+
|
42
|
+
def _add_to_reverse_association_of(entity)
|
43
|
+
if entity.send(@reverse).is_a? HasMany
|
44
|
+
entity.send(@reverse).send(:_add, @this)
|
45
|
+
else
|
46
|
+
entity.send("_#{@reverse}=", @this)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def _remove_from_reverse_association_of(entity)
|
51
|
+
if entity.send(@reverse).is_a? HasMany
|
52
|
+
entity.send(@reverse).send(:_remove, @this)
|
53
|
+
else
|
54
|
+
entity.send("_#{@reverse}=", nil)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def _store
|
59
|
+
Remodel.redis.hset(Remodel.context, @key, JSON.generate(self.map(&:key)))
|
60
|
+
end
|
61
|
+
|
62
|
+
def _fetch(clazz, key)
|
63
|
+
keys = JSON.parse(Remodel.redis.hget(Remodel.context, key) || '[]')
|
64
|
+
values = keys.empty? ? [] : Remodel.redis.hmget(Remodel.context, *keys)
|
65
|
+
keys.zip(values).map do |key, json|
|
66
|
+
clazz.restore(key, json) if json
|
67
|
+
end.compact
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Remodel
|
2
|
+
|
3
|
+
# A mapper converts a given value into a native JSON value —
|
4
|
+
# *nil*, *true*, *false*, *Number*, *String*, *Hash*, *Array* —
|
5
|
+
# via `pack`, and back again via `unpack`.
|
6
|
+
#
|
7
|
+
# Without any arguments, `Mapper.new` returns the identity mapper, which
|
8
|
+
# maps every value to itself. If `clazz` is set, the mapper rejects any
|
9
|
+
# value which is not of the given type.
|
10
|
+
class Mapper
|
11
|
+
def initialize(clazz = nil, pack_method = nil, unpack_method = nil)
|
12
|
+
@clazz = clazz
|
13
|
+
@pack_method = pack_method
|
14
|
+
@unpack_method = unpack_method
|
15
|
+
end
|
16
|
+
|
17
|
+
def pack(value)
|
18
|
+
return nil if value.nil?
|
19
|
+
raise(InvalidType, "#{value.inspect} is not a #{@clazz}") if @clazz && !value.is_a?(@clazz)
|
20
|
+
@pack_method ? value.send(@pack_method) : value
|
21
|
+
end
|
22
|
+
|
23
|
+
def unpack(value)
|
24
|
+
return nil if value.nil?
|
25
|
+
@unpack_method ? @clazz.send(@unpack_method, value) : value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/remodel.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'redis'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
# If available, use the superfast YAJL lib to parse JSON.
|
6
|
+
begin
|
7
|
+
require 'yajl/json_gem'
|
8
|
+
rescue LoadError
|
9
|
+
require 'json'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Define `Boolean` -- the missing superclass of `true` and `false`.
|
13
|
+
module Boolean; end
|
14
|
+
true.extend(Boolean)
|
15
|
+
false.extend(Boolean)
|
16
|
+
|
17
|
+
# Find the `Class` object for a given class name, which can be
|
18
|
+
# a `String` or a `Symbol` (or a `Class`).
|
19
|
+
def Class.[](clazz)
|
20
|
+
return clazz if clazz.nil? or clazz.is_a?(Class)
|
21
|
+
clazz.to_s.split('::').inject(Kernel) { |mod, name| mod.const_get(name) }
|
22
|
+
end
|
23
|
+
|
24
|
+
require File.join(File.dirname(__FILE__), 'remodel', 'mapper')
|
25
|
+
require File.join(File.dirname(__FILE__), 'remodel', 'has_many')
|
26
|
+
require File.join(File.dirname(__FILE__), 'remodel', 'entity')
|
27
|
+
|
28
|
+
|
29
|
+
module Remodel
|
30
|
+
|
31
|
+
# Custom errors
|
32
|
+
class Error < ::StandardError; end
|
33
|
+
class EntityNotFound < Error; end
|
34
|
+
class EntityNotSaved < Error; end
|
35
|
+
class InvalidKeyPrefix < Error; end
|
36
|
+
class InvalidType < Error; end
|
37
|
+
class MissingContext < Error; end
|
38
|
+
|
39
|
+
# By default, the redis server is expected to listen at `localhost:6379`.
|
40
|
+
# Otherwise you will have to set `Remodel.redis` to a suitably initialized
|
41
|
+
# redis client.
|
42
|
+
def self.redis
|
43
|
+
@redis ||= Redis.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.redis=(redis)
|
47
|
+
@redis = redis
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.context
|
51
|
+
Thread.current[:remodel_context] || raise(MissingContext)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.context=(context)
|
55
|
+
Thread.current[:remodel_context] = context
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the mapper defined for a given class, or the identity mapper.
|
59
|
+
def self.mapper_for(clazz)
|
60
|
+
mapper_by_class[Class[clazz]]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Define some mappers for common types.
|
64
|
+
def self.mapper_by_class
|
65
|
+
@mapper_by_class ||= Hash.new(Mapper.new).merge(
|
66
|
+
Boolean => Mapper.new(Boolean),
|
67
|
+
String => Mapper.new(String),
|
68
|
+
Integer => Mapper.new(Integer),
|
69
|
+
Float => Mapper.new(Float),
|
70
|
+
Array => Mapper.new(Array),
|
71
|
+
Hash => Mapper.new(Hash),
|
72
|
+
Date => Mapper.new(Date, :to_s, :parse),
|
73
|
+
Time => Mapper.new(Time, :to_i, :at)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/remodel-h.gemspec
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{remodel-h}
|
8
|
+
s.version = "0.1.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tim Lossen"]
|
12
|
+
s.date = %q{2010-07-20}
|
13
|
+
s.default_executable = %q{redis-monitor.rb}
|
14
|
+
s.description = %q{persist your objects to redis hashes.}
|
15
|
+
s.email = %q{tim@lossen.de}
|
16
|
+
s.executables = ["redis-monitor.rb"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/redis-monitor.rb",
|
28
|
+
"docs/docco.css",
|
29
|
+
"docs/remodel.html",
|
30
|
+
"example/book.rb",
|
31
|
+
"lib/remodel.rb",
|
32
|
+
"lib/remodel/entity.rb",
|
33
|
+
"lib/remodel/has_many.rb",
|
34
|
+
"lib/remodel/mapper.rb",
|
35
|
+
"remodel-h.gemspec",
|
36
|
+
"remodel.gemspec",
|
37
|
+
"test/helper.rb",
|
38
|
+
"test/test_entity.rb",
|
39
|
+
"test/test_many_to_many.rb",
|
40
|
+
"test/test_many_to_one.rb",
|
41
|
+
"test/test_mappers.rb",
|
42
|
+
"test/test_monkeypatches.rb",
|
43
|
+
"test/test_one_to_many.rb",
|
44
|
+
"test/test_one_to_one.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/tlossen/remodel}
|
47
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.5}
|
50
|
+
s.summary = %q{remodel variant which uses hashes}
|
51
|
+
s.test_files = [
|
52
|
+
"test/helper.rb",
|
53
|
+
"test/test_entity.rb",
|
54
|
+
"test/test_many_to_many.rb",
|
55
|
+
"test/test_many_to_one.rb",
|
56
|
+
"test/test_mappers.rb",
|
57
|
+
"test/test_monkeypatches.rb",
|
58
|
+
"test/test_one_to_many.rb",
|
59
|
+
"test/test_one_to_one.rb"
|
60
|
+
]
|
61
|
+
|
62
|
+
if s.respond_to? :specification_version then
|
63
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
64
|
+
s.specification_version = 3
|
65
|
+
|
66
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
67
|
+
else
|
68
|
+
end
|
69
|
+
else
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
data/remodel.gemspec
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{remodel}
|
8
|
+
s.version = "0.1.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tim Lossen"]
|
12
|
+
s.date = %q{2010-07-01}
|
13
|
+
s.default_executable = %q{redis-monitor.rb}
|
14
|
+
s.description = %q{build your domain model in ruby, persist your objects to redis.}
|
15
|
+
s.email = %q{tim@lossen.de}
|
16
|
+
s.executables = ["redis-monitor.rb"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/redis-monitor.rb",
|
28
|
+
"docs/docco.css",
|
29
|
+
"docs/remodel.html",
|
30
|
+
"example/book.rb",
|
31
|
+
"lib/remodel.rb",
|
32
|
+
"lib/remodel/entity.rb",
|
33
|
+
"lib/remodel/has_many.rb",
|
34
|
+
"lib/remodel/has_one.rb",
|
35
|
+
"lib/remodel/mapper.rb",
|
36
|
+
"remodel.gemspec",
|
37
|
+
"test/helper.rb",
|
38
|
+
"test/test_entity.rb",
|
39
|
+
"test/test_many_to_many.rb",
|
40
|
+
"test/test_many_to_one.rb",
|
41
|
+
"test/test_mappers.rb",
|
42
|
+
"test/test_monkeypatches.rb",
|
43
|
+
"test/test_one_to_many.rb",
|
44
|
+
"test/test_one_to_one.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/tlossen/remodel}
|
47
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.5}
|
50
|
+
s.summary = %q{a minimal ORM (object-redis-mapper)}
|
51
|
+
s.test_files = [
|
52
|
+
"test/helper.rb",
|
53
|
+
"test/test_entity.rb",
|
54
|
+
"test/test_many_to_many.rb",
|
55
|
+
"test/test_many_to_one.rb",
|
56
|
+
"test/test_mappers.rb",
|
57
|
+
"test/test_monkeypatches.rb",
|
58
|
+
"test/test_one_to_many.rb",
|
59
|
+
"test/test_one_to_one.rb"
|
60
|
+
]
|
61
|
+
|
62
|
+
if s.respond_to? :specification_version then
|
63
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
64
|
+
s.specification_version = 3
|
65
|
+
|
66
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
67
|
+
else
|
68
|
+
end
|
69
|
+
else
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/../lib/remodel.rb'
|
6
|
+
|
7
|
+
class Test::Unit::TestCase
|
8
|
+
|
9
|
+
def redis
|
10
|
+
Remodel.redis
|
11
|
+
end
|
12
|
+
|
13
|
+
def context
|
14
|
+
Remodel.context
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
Remodel.context = 'foo'
|
data/test/test_entity.rb
ADDED
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Foo < Remodel::Entity
|
4
|
+
property :x
|
5
|
+
property :y
|
6
|
+
end
|
7
|
+
|
8
|
+
class Bar < Remodel::Entity
|
9
|
+
property :d, :default => 123
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestEntity < Test::Unit::TestCase
|
13
|
+
|
14
|
+
context "new" do
|
15
|
+
should "set properties" do
|
16
|
+
foo = Foo.new :x => 1, :y => 2
|
17
|
+
assert 1, foo.x
|
18
|
+
assert 2, foo.y
|
19
|
+
end
|
20
|
+
|
21
|
+
should "ignore undefined properties" do
|
22
|
+
foo = Foo.new :z => 3
|
23
|
+
assert foo.instance_eval { !@attributes.key? :z }
|
24
|
+
end
|
25
|
+
|
26
|
+
should "not set the key" do
|
27
|
+
foo = Foo.new :x => 23
|
28
|
+
assert_equal nil, foo.key
|
29
|
+
end
|
30
|
+
|
31
|
+
should "not set the id" do
|
32
|
+
foo = Foo.new :x => 23
|
33
|
+
assert_equal nil, foo.id
|
34
|
+
end
|
35
|
+
|
36
|
+
should "use default values for missing properties" do
|
37
|
+
bar = Bar.new
|
38
|
+
assert_equal 123, bar.d
|
39
|
+
end
|
40
|
+
|
41
|
+
should "not use default values for given properties" do
|
42
|
+
bar = Bar.new :d => 'cool'
|
43
|
+
assert_equal 'cool', bar.d
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "create" do
|
48
|
+
setup do
|
49
|
+
redis.flushdb
|
50
|
+
end
|
51
|
+
|
52
|
+
should "work without attributes" do
|
53
|
+
foo = Foo.create
|
54
|
+
assert foo.is_a?(Foo)
|
55
|
+
end
|
56
|
+
|
57
|
+
should "give the entity a key based on the class name" do
|
58
|
+
assert_equal 'f:1', Foo.create.key
|
59
|
+
assert_equal 'b:1', Bar.create.key
|
60
|
+
assert_equal 'b:2', Bar.create.key
|
61
|
+
end
|
62
|
+
|
63
|
+
should "give the entity an id which is unique per entity class" do
|
64
|
+
assert_equal 1, Foo.create.id
|
65
|
+
assert_equal 1, Bar.create.id
|
66
|
+
assert_equal 2, Bar.create.id
|
67
|
+
end
|
68
|
+
|
69
|
+
should "store the entity under its key" do
|
70
|
+
foo = Foo.create :x => 'hello', :y => false
|
71
|
+
assert redis.hexists(context, foo.key)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "store all properties" do
|
75
|
+
foo = Foo.create :x => 'hello', :y => false
|
76
|
+
foo.reload
|
77
|
+
assert_equal 'hello', foo.x
|
78
|
+
assert_equal false, foo.y
|
79
|
+
end
|
80
|
+
|
81
|
+
should "not store the key as a property" do
|
82
|
+
foo = Foo.create :x => 'hello', :y => false
|
83
|
+
assert !(/f:1/ =~ redis.hget(context, foo.key))
|
84
|
+
end
|
85
|
+
|
86
|
+
should "use default values for missing properties" do
|
87
|
+
bar = Bar.create
|
88
|
+
assert_equal 123, bar.d
|
89
|
+
end
|
90
|
+
|
91
|
+
should "not use default values for given properties" do
|
92
|
+
bar = Bar.create :d => 'cool'
|
93
|
+
assert_equal 'cool', bar.d
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "save" do
|
98
|
+
setup do
|
99
|
+
redis.flushdb
|
100
|
+
end
|
101
|
+
|
102
|
+
should "give the entity a key, if necessary" do
|
103
|
+
foo = Foo.new.save
|
104
|
+
assert foo.key
|
105
|
+
end
|
106
|
+
|
107
|
+
should "store the entity under its key" do
|
108
|
+
foo = Foo.new :x => 'hello', :y => false
|
109
|
+
foo.save
|
110
|
+
assert redis.hexists(context, foo.key)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "store all properties" do
|
114
|
+
foo = Foo.new :x => 'hello', :y => false
|
115
|
+
foo.save
|
116
|
+
foo.reload
|
117
|
+
assert_equal 'hello', foo.x
|
118
|
+
assert_equal false, foo.y
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "reload" do
|
123
|
+
setup do
|
124
|
+
@foo = Foo.create :x => 'hello', :y => true
|
125
|
+
end
|
126
|
+
|
127
|
+
should "reload all properties" do
|
128
|
+
redis.hset context, @foo.key, %q({"x":23,"y":"adios"})
|
129
|
+
@foo.reload
|
130
|
+
assert_equal 23, @foo.x
|
131
|
+
assert_equal 'adios', @foo.y
|
132
|
+
end
|
133
|
+
|
134
|
+
should "keep the key" do
|
135
|
+
key = @foo.key
|
136
|
+
@foo.reload
|
137
|
+
assert_equal key, @foo.key
|
138
|
+
end
|
139
|
+
|
140
|
+
should "stay the same object" do
|
141
|
+
id = @foo.object_id
|
142
|
+
@foo.reload
|
143
|
+
assert_equal id, @foo.object_id
|
144
|
+
end
|
145
|
+
|
146
|
+
should "raise EntityNotFound if the entity does not exist any more" do
|
147
|
+
redis.hdel context, @foo.key
|
148
|
+
assert_raise(Remodel::EntityNotFound) { @foo.reload }
|
149
|
+
end
|
150
|
+
|
151
|
+
should "raise EntityNotSaved if the entity was never saved" do
|
152
|
+
assert_raise(Remodel::EntityNotSaved) { Foo.new.reload }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "update" do
|
157
|
+
setup do
|
158
|
+
redis.flushdb
|
159
|
+
@foo = Foo.create :x => 'Tim', :y => true
|
160
|
+
end
|
161
|
+
|
162
|
+
should "set the given properties" do
|
163
|
+
@foo.update(:x => 12, :y => 'Jan')
|
164
|
+
assert_equal 12, @foo.x
|
165
|
+
assert_equal 'Jan', @foo.y
|
166
|
+
end
|
167
|
+
|
168
|
+
should "save the entity" do
|
169
|
+
@foo.update(:x => 12, :y => 'Jan')
|
170
|
+
@foo.reload
|
171
|
+
assert_equal 12, @foo.x
|
172
|
+
assert_equal 'Jan', @foo.y
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "delete" do
|
177
|
+
setup do
|
178
|
+
redis.flushdb
|
179
|
+
@foo = Foo.create :x => 'Tim', :y => true
|
180
|
+
end
|
181
|
+
|
182
|
+
should "delete the given entity" do
|
183
|
+
@foo.delete
|
184
|
+
assert_nil redis.hget(context, @foo.key)
|
185
|
+
end
|
186
|
+
|
187
|
+
should "ensure that the entity is persistent" do
|
188
|
+
assert_raise(Remodel::EntityNotSaved) { Foo.new.delete }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "to_json" do
|
193
|
+
should "serialize to json" do
|
194
|
+
foo = Foo.new :x => 42, :y => true
|
195
|
+
assert_match /"x":42/, foo.to_json
|
196
|
+
assert_match /"y":true/, foo.to_json
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "as_json" do
|
201
|
+
should "serialize into a hash" do
|
202
|
+
foo = Foo.create :x => 42, :y => true
|
203
|
+
expected = { :id => foo.id, :x => 42, :y => true }
|
204
|
+
assert_equal expected, foo.as_json
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "#set_key_prefix" do
|
209
|
+
should "use the given key prefix" do
|
210
|
+
class Custom < Remodel::Entity; set_key_prefix 'my'; end
|
211
|
+
assert_match /^my:\d+$/, Custom.create.key
|
212
|
+
end
|
213
|
+
|
214
|
+
should "ensure that the prefix is letters only" do
|
215
|
+
assert_raise(Remodel::InvalidKeyPrefix) do
|
216
|
+
class InvalidPrefix < Remodel::Entity; set_key_prefix '666'; end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "#find" do
|
222
|
+
setup do
|
223
|
+
redis.flushdb
|
224
|
+
@foo = Foo.create :x => 'hello', :y => 123
|
225
|
+
Foo.create :x => 'hallo', :y => 124
|
226
|
+
end
|
227
|
+
|
228
|
+
should "find and load an entity by key" do
|
229
|
+
foo = Foo.find(@foo.key)
|
230
|
+
assert_equal foo.x, @foo.x
|
231
|
+
assert_equal foo.y, @foo.y
|
232
|
+
end
|
233
|
+
|
234
|
+
should "find and load an entity by id" do
|
235
|
+
foo = Foo.find(@foo.id)
|
236
|
+
assert_equal foo.x, @foo.x
|
237
|
+
assert_equal foo.y, @foo.y
|
238
|
+
end
|
239
|
+
|
240
|
+
should "reject a key which does not exist" do
|
241
|
+
assert_raise(Remodel::EntityNotFound) { Foo.find('x:66') }
|
242
|
+
end
|
243
|
+
|
244
|
+
should "reject an id which does not exist" do
|
245
|
+
assert_raise(Remodel::EntityNotFound) { Foo.find(66) }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "properties" do
|
250
|
+
should "have property x" do
|
251
|
+
foo = Foo.new
|
252
|
+
foo.x = 23
|
253
|
+
assert_equal 23, foo.x
|
254
|
+
foo.x += 10
|
255
|
+
assert_equal 33, foo.x
|
256
|
+
end
|
257
|
+
|
258
|
+
should "not have property z" do
|
259
|
+
foo = Foo.new
|
260
|
+
assert_raise(NoMethodError) { foo.z }
|
261
|
+
assert_raise(NoMethodError) { foo.z = 42 }
|
262
|
+
end
|
263
|
+
|
264
|
+
context "types" do
|
265
|
+
should "work with nil" do
|
266
|
+
foo = Foo.create :x => nil
|
267
|
+
assert_equal nil, foo.reload.x
|
268
|
+
end
|
269
|
+
|
270
|
+
should "work with booleans" do
|
271
|
+
foo = Foo.create :x => false
|
272
|
+
assert_equal false, foo.reload.x
|
273
|
+
end
|
274
|
+
|
275
|
+
should "work with integers" do
|
276
|
+
foo = Foo.create :x => -42
|
277
|
+
assert_equal -42, foo.reload.x
|
278
|
+
end
|
279
|
+
|
280
|
+
should "work with floats" do
|
281
|
+
foo = Foo.create :x => 3.141
|
282
|
+
assert_equal 3.141, foo.reload.x
|
283
|
+
end
|
284
|
+
|
285
|
+
should "work with strings" do
|
286
|
+
foo = Foo.create :x => 'hello'
|
287
|
+
assert_equal 'hello', foo.reload.x
|
288
|
+
end
|
289
|
+
|
290
|
+
should "work with lists" do
|
291
|
+
foo = Foo.create :x => [1, 2, 3]
|
292
|
+
assert_equal [1, 2, 3], foo.reload.x
|
293
|
+
end
|
294
|
+
|
295
|
+
should "work with hashes" do
|
296
|
+
hash = { 'a' => 17, 'b' => 'test' }
|
297
|
+
foo = Foo.create :x => hash
|
298
|
+
assert_equal hash, foo.reload.x
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context "#restore" do
|
304
|
+
should "restore an entity from json" do
|
305
|
+
before = Foo.create :x => 42, :y => true
|
306
|
+
after = Foo.restore(before.key, before.to_json)
|
307
|
+
assert_equal before.key, after.key
|
308
|
+
assert_equal before.x, after.x
|
309
|
+
assert_equal before.y, after.y
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|