jason-orm 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +41 -0
- data/Readme.md +130 -0
- data/jason.gemspec +28 -0
- data/lib/jason/core_ext/string.rb +7 -0
- data/lib/jason/crypt/document_id.rb +15 -0
- data/lib/jason/encoding/deletable.rb +30 -0
- data/lib/jason/encoding/persistable.rb +102 -0
- data/lib/jason/encoding/persistence_handler.rb +33 -0
- data/lib/jason/encoding/persistence_object.rb +26 -0
- data/lib/jason/encoding/restorable.rb +83 -0
- data/lib/jason/errors.rb +13 -0
- data/lib/jason/operations/file.rb +41 -0
- data/lib/jason/persistence.rb +191 -0
- data/lib/jason/reflection/base.rb +19 -0
- data/lib/jason/relation.rb +127 -0
- data/lib/jason.rb +71 -0
- data/lib/version.rb +3 -0
- data/spec/core_ext/string_spec.rb +22 -0
- data/spec/persistence/persistence_spec.rb +259 -0
- data/spec/relation/relation_spec.rb +105 -0
- data/spec/spec_helper.rb +28 -0
- metadata +127 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
module Jason
|
2
|
+
|
3
|
+
module Persistence
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def included(base)
|
8
|
+
base.class_eval do
|
9
|
+
include InstanceMethods
|
10
|
+
extend ClassMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
attr_accessor :new_record
|
17
|
+
|
18
|
+
define_method(:respond_to_missing?) do |meth_name, include_private|
|
19
|
+
if meth_name.to_s.match(/find_by_/)
|
20
|
+
conditions = meth_name.to_s.split("find_by_").last
|
21
|
+
return defined_attributes_include?(conditions.to_sym)
|
22
|
+
else
|
23
|
+
super(meth_name,include_private)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(meth_name,*args,&block)
|
28
|
+
if meth_name.to_s.match(/find_by_/)
|
29
|
+
conditions = meth_name.to_s.split("find_by_").last
|
30
|
+
if defined_attributes_include?(conditions.to_sym)
|
31
|
+
options = {:klass => self}
|
32
|
+
options[conditions.to_sym] = args.pop
|
33
|
+
return Encoding::PersistenceHandler::restore(:with_conditions, options)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def defined_attributes_include?(symbol)
|
43
|
+
defined_attributes.map{|item| item[:name]}.include?(symbol)
|
44
|
+
end
|
45
|
+
|
46
|
+
def data_type_for_attribute(attribute)
|
47
|
+
defined_attributes.detect{|item| item[:name] == attribute}[:attribute_type]
|
48
|
+
end
|
49
|
+
|
50
|
+
def defined_attributes
|
51
|
+
@defined_attributes ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def find(id)
|
55
|
+
#with_id id do
|
56
|
+
# klass_to_restore self
|
57
|
+
#end
|
58
|
+
Encoding::PersistenceHandler::restore(:by_id,{:id => id,:klass=>self})
|
59
|
+
end
|
60
|
+
|
61
|
+
def all
|
62
|
+
Encoding::PersistenceHandler::restore(:all,{:klass => self})
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_id(id,&block)
|
66
|
+
p id
|
67
|
+
block.call
|
68
|
+
end
|
69
|
+
|
70
|
+
# PUBLIC Add attribute to persistence layer for this model.
|
71
|
+
#
|
72
|
+
# Defines getter and setter methods for given attribute
|
73
|
+
#
|
74
|
+
# The Setter methods converts the attribute into the given
|
75
|
+
# data type.
|
76
|
+
#
|
77
|
+
# args - List of arguments:
|
78
|
+
#
|
79
|
+
# * first argument - attribute name as symbol
|
80
|
+
# * second argument - data type of attribute
|
81
|
+
#
|
82
|
+
# Currently three data types are supported:
|
83
|
+
#
|
84
|
+
# * String
|
85
|
+
# * Integer
|
86
|
+
# * Date
|
87
|
+
def attribute(*args)
|
88
|
+
attribute_name,attribute_type = args[0], args[1]
|
89
|
+
|
90
|
+
unless DATA_TYPES.keys.include?("#{attribute_type}".to_sym)
|
91
|
+
raise Errors::NotSupportedDataTypeError.new("This Kind of type is not supported or missing!")
|
92
|
+
end
|
93
|
+
|
94
|
+
cast_to = DATA_TYPES["#{attribute_type}".to_sym]#eval "Jason::#{attribute_type}"
|
95
|
+
|
96
|
+
define_method attribute_name do
|
97
|
+
instance_variable_get("@#{attribute_name}")
|
98
|
+
end
|
99
|
+
|
100
|
+
define_method "#{attribute_name}=" do |attribute|
|
101
|
+
instance_variable_set("@#{attribute_name}", attribute.send(cast_to))
|
102
|
+
end
|
103
|
+
|
104
|
+
defined_attributes << {:name => attribute_name, :type => attribute_type} unless defined_attributes.include?(attribute_name)
|
105
|
+
|
106
|
+
unless defined_attributes_include?(:id)
|
107
|
+
define_method :id do
|
108
|
+
instance_variable_get("@id")
|
109
|
+
end
|
110
|
+
|
111
|
+
define_method "id=" do |val|
|
112
|
+
instance_variable_set("@id", val)
|
113
|
+
end
|
114
|
+
defined_attributes << {:name => :id, :type => String}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
module InstanceMethods
|
122
|
+
|
123
|
+
def initialize(attrs=nil)
|
124
|
+
@attributes = attrs || {}
|
125
|
+
@new_record = true
|
126
|
+
process_attributes(attrs) unless attrs.nil?
|
127
|
+
yield(self) if block_given?
|
128
|
+
end
|
129
|
+
|
130
|
+
def attributes
|
131
|
+
@attributes.merge!(:id => self.id)
|
132
|
+
end
|
133
|
+
|
134
|
+
def save
|
135
|
+
saved = Encoding::PersistenceHandler.persist(self, new_record? ? {} : {:update => true})
|
136
|
+
@new_record = saved ? false : true
|
137
|
+
return saved
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete
|
141
|
+
raise Jason::Errors::UndeletableError.new "Could not delete an unpersisted object!" if new_record?
|
142
|
+
Encoding::PersistenceHandler.delete(self)
|
143
|
+
end
|
144
|
+
|
145
|
+
def update_attributes(attributes={})
|
146
|
+
process_attributes(attributes.merge(:id => self.id), :reload => true)
|
147
|
+
updated = Encoding::PersistenceHandler.persist(self,:update => true)
|
148
|
+
return updated
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_hsh
|
152
|
+
#p self.attributes
|
153
|
+
self.attributes
|
154
|
+
end
|
155
|
+
alias_method :to_hash, :to_hsh
|
156
|
+
|
157
|
+
def as_json
|
158
|
+
jsonable = {}
|
159
|
+
jsonable[Jason::singularize_key(self.class)] = self.to_hsh
|
160
|
+
return jsonable
|
161
|
+
end
|
162
|
+
|
163
|
+
def new_record?
|
164
|
+
@new_record
|
165
|
+
end
|
166
|
+
alias_method :persisted?, :new_record?
|
167
|
+
|
168
|
+
def reload_attributes
|
169
|
+
self.class.defined_attributes.each do |attribute|
|
170
|
+
called_attribute = self.send(attribute[:name])
|
171
|
+
@attributes[attribute[:name]] = called_attribute if called_attribute
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def process_attributes(attrs={}, options = {})
|
178
|
+
reload = options.fetch(:reload, false)
|
179
|
+
attrs.each_pair{ |key,value| self.send("#{key}=",value) }
|
180
|
+
unless attrs.has_key?(:id)
|
181
|
+
# generate a new id
|
182
|
+
@id = Encryptors::Document.process_document_id
|
183
|
+
end
|
184
|
+
reload_attributes if reload
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Jason
|
2
|
+
|
3
|
+
module Relation
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
include InstanceMethods
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def reflections
|
19
|
+
@reflections ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def reflect_on_relation(relation_name)
|
23
|
+
reflections.detect{|reflection| reflection.name.eql?(relation_name.to_s)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_many(*args)
|
27
|
+
relation_name = args.shift.to_s
|
28
|
+
options = args.empty? ? {} : args.shift
|
29
|
+
|
30
|
+
class_name = options.fetch(:class, relation_name.downcase.singularize).capitalize
|
31
|
+
|
32
|
+
reflections << Reflection::Base.new(:name => relation_name,
|
33
|
+
:class_name => class_name,
|
34
|
+
:type => "has_many")
|
35
|
+
|
36
|
+
ivar_name = "#{relation_name}_ids".to_sym
|
37
|
+
|
38
|
+
attribute ivar_name, String
|
39
|
+
|
40
|
+
method_definition_getter = <<-RUBY
|
41
|
+
|
42
|
+
def #{relation_name}
|
43
|
+
# find all children in *.json if included.
|
44
|
+
# Otherwise return empty array.
|
45
|
+
relation_objects = self.instance_variable_get("@#{relation_name}")
|
46
|
+
finder = ->() do
|
47
|
+
objects = []
|
48
|
+
object_ids = self.send("#{ivar_name}")
|
49
|
+
unless object_ids.nil? or object_ids.empty?
|
50
|
+
object_ids.split(Jason::has_many_separator).each do |object_id|
|
51
|
+
begin
|
52
|
+
klass = Module.const_get("#{class_name}".to_sym)
|
53
|
+
objects << klass.find(object_id)
|
54
|
+
rescue
|
55
|
+
next
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
self.instance_variable_set("@#{relation_name}", objects)
|
60
|
+
return objects
|
61
|
+
end
|
62
|
+
relation_objects ||= finder.call
|
63
|
+
end
|
64
|
+
|
65
|
+
RUBY
|
66
|
+
|
67
|
+
method_definition_setter = <<-RUBY
|
68
|
+
|
69
|
+
def #{relation_name}=(objects)
|
70
|
+
self.send("#{ivar_name}=", objects.map(&:id).join(Jason::has_many_separator))
|
71
|
+
self.instance_variable_set("@#{relation_name}",objects)
|
72
|
+
self.reload_attributes
|
73
|
+
end
|
74
|
+
|
75
|
+
RUBY
|
76
|
+
|
77
|
+
class_eval method_definition_getter
|
78
|
+
class_eval method_definition_setter
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def belongs_to(*args)
|
83
|
+
relation_name = args.shift.to_s
|
84
|
+
options = args.empty? ? {} : args.shift
|
85
|
+
|
86
|
+
class_name = options.fetch(:class, relation_name).capitalize
|
87
|
+
|
88
|
+
reflections << Reflection::Base.new(:name => relation_name,
|
89
|
+
:class_name => class_name,
|
90
|
+
:type => "belongs_to")
|
91
|
+
|
92
|
+
ivar_name = "#{relation_name}_id".to_sym
|
93
|
+
attribute ivar_name, String
|
94
|
+
|
95
|
+
method_definition_setter = <<-RUBY
|
96
|
+
def #{relation_name}=(obj)
|
97
|
+
self.send("#{ivar_name}=", obj.id)
|
98
|
+
self.instance_variable_set("@#{relation_name}",obj)
|
99
|
+
self.reload_attributes
|
100
|
+
end
|
101
|
+
|
102
|
+
RUBY
|
103
|
+
|
104
|
+
method_definition_getter = <<-RUBY
|
105
|
+
def #{relation_name}
|
106
|
+
relation_obj = self.instance_variable_get("@#{relation_name}")
|
107
|
+
finder = ->() do
|
108
|
+
klass = Module.const_get("#{class_name}".to_sym)
|
109
|
+
relation_obj = klass.find(self.send("#{ivar_name}"))
|
110
|
+
self.instance_variable_set("@#{relation_name}", relation_obj)
|
111
|
+
return relation_obj
|
112
|
+
end
|
113
|
+
relation_obj ||= finder.call
|
114
|
+
end
|
115
|
+
RUBY
|
116
|
+
|
117
|
+
class_eval method_definition_setter, "relation.rb", 63
|
118
|
+
class_eval method_definition_getter, "relation.rb", 72
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
data/lib/jason.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
begin
|
4
|
+
Bundler.setup
|
5
|
+
rescue
|
6
|
+
raise RuntimeError, "Bundler couldn't find some gems." +
|
7
|
+
"Did you run 'bundle install'?"
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'active_support/core_ext/module/qualified_const'
|
11
|
+
#require 'active_support/core_ext/string/inflections'
|
12
|
+
require 'active_support/inflector'
|
13
|
+
require 'active_support/json'
|
14
|
+
|
15
|
+
module Jason
|
16
|
+
|
17
|
+
require 'jason/core_ext/string'
|
18
|
+
|
19
|
+
extend self
|
20
|
+
|
21
|
+
module Encryptors
|
22
|
+
autoload :Document, 'jason/crypt/document_id'
|
23
|
+
end
|
24
|
+
|
25
|
+
module Operations
|
26
|
+
autoload :File, 'jason/operations/file'
|
27
|
+
end
|
28
|
+
|
29
|
+
module Encoding
|
30
|
+
autoload :PersistenceHandler, 'jason/encoding/persistence_handler'
|
31
|
+
end
|
32
|
+
|
33
|
+
module Reflection
|
34
|
+
autoload :Base, 'jason/reflection/base'
|
35
|
+
end
|
36
|
+
|
37
|
+
autoload :Errors, 'jason/errors'
|
38
|
+
|
39
|
+
autoload :Relation, 'jason/relation'
|
40
|
+
autoload :Persistence, 'jason/persistence'
|
41
|
+
|
42
|
+
DATA_TYPES = {
|
43
|
+
:Integer => :to_i,
|
44
|
+
:String => :to_s,
|
45
|
+
:Date => :to_date
|
46
|
+
}
|
47
|
+
|
48
|
+
#Integer = :to_i
|
49
|
+
#String = :to_s
|
50
|
+
#Date = :to_date
|
51
|
+
|
52
|
+
def setup(&block)
|
53
|
+
yield(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
mattr_accessor :persistence_path
|
57
|
+
@@persistence_path = File.expand_path(File.join(File.dirname( __FILE__)), 'json')
|
58
|
+
|
59
|
+
mattr_accessor :restore_app
|
60
|
+
@@restore_app = Encoding::PersistenceHandler::Restorable
|
61
|
+
|
62
|
+
mattr_accessor :has_many_separator
|
63
|
+
@@has_many_separator = ","
|
64
|
+
|
65
|
+
def singularize_key(key)
|
66
|
+
key.name.downcase.singularize if key.respond_to?(:name)
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
|
5
|
+
subject{String.new}
|
6
|
+
|
7
|
+
it{should respond_to(:to_date)}
|
8
|
+
|
9
|
+
context "#to_date" do
|
10
|
+
|
11
|
+
it "converts string to Date object" do
|
12
|
+
date_string = "2012-10-09"
|
13
|
+
eql_date = Chronic.parse(date_string)
|
14
|
+
date_string.to_date.should eq eql_date.to_date
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Person
|
4
|
+
|
5
|
+
include Jason::Persistence
|
6
|
+
|
7
|
+
attribute :firstname, String
|
8
|
+
attribute :lastname, String
|
9
|
+
attribute :date_of_birth, Date
|
10
|
+
attribute :age, Integer
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Jason::Persistence" do
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
FileUtils.rm_rf(File.join(fixtures_path, 'people.json'))
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:all) do
|
21
|
+
FileUtils.rm_rf(File.join(fixtures_path, 'people.json'))
|
22
|
+
end
|
23
|
+
|
24
|
+
context "class methods" do
|
25
|
+
|
26
|
+
subject{Person}
|
27
|
+
|
28
|
+
it{should respond_to :attribute}
|
29
|
+
it{should respond_to :find}
|
30
|
+
it{should respond_to :all}
|
31
|
+
|
32
|
+
context "#find" do
|
33
|
+
|
34
|
+
context "when document found" do
|
35
|
+
|
36
|
+
let(:person) do
|
37
|
+
Person.new(:lastname => "Hauptmann", :firstname => "Rene", :age => 12,
|
38
|
+
:date_of_birth => "2000/09/09")
|
39
|
+
end
|
40
|
+
|
41
|
+
before do
|
42
|
+
person.save
|
43
|
+
end
|
44
|
+
|
45
|
+
subject{Person.find(person.id)}
|
46
|
+
|
47
|
+
it "returns single object" do
|
48
|
+
subject.should be_instance_of Person
|
49
|
+
end
|
50
|
+
|
51
|
+
it "id equals the person id" do
|
52
|
+
subject.id.should eq person.id
|
53
|
+
end
|
54
|
+
|
55
|
+
it "lastname equals person lastname" do
|
56
|
+
subject.lastname.should eq person.lastname
|
57
|
+
end
|
58
|
+
|
59
|
+
it "firstname equals person firstname" do
|
60
|
+
subject.firstname.should eq person.firstname
|
61
|
+
end
|
62
|
+
|
63
|
+
it "date_of_birth is a date and equals person date" do
|
64
|
+
subject.date_of_birth.should be_instance_of Date
|
65
|
+
subject.date_of_birth.day.should eq 9
|
66
|
+
subject.date_of_birth.month.should eq 9
|
67
|
+
subject.date_of_birth.year.should eq 2000
|
68
|
+
end
|
69
|
+
|
70
|
+
it "is not a new record" do
|
71
|
+
subject.new_record?.should be false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "age is a fixnum" do
|
75
|
+
subject.age.should be_instance_of Fixnum
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when document not found" do
|
81
|
+
|
82
|
+
it "raises an Jason::DocumentNotFoundError" do
|
83
|
+
expect{Person.find('123456')}.to raise_error Jason::Errors::DocumentNotFoundError
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
context "#all" do
|
91
|
+
|
92
|
+
it "returns an array of documents" do
|
93
|
+
result = Person.all
|
94
|
+
result.should be_instance_of Array
|
95
|
+
result.each do |instance|
|
96
|
+
instance.should be_instance_of Person
|
97
|
+
end
|
98
|
+
result.should_not be_empty
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
context "magic finders" do
|
104
|
+
|
105
|
+
before(:all) do
|
106
|
+
FileUtils.rm_rf(File.join(fixtures_path, 'people.json'))
|
107
|
+
["Max", "Werner", "Claudia", "Werner"].each do |name|
|
108
|
+
person = Person.new(:firstname => name,:lastname => "Winter")
|
109
|
+
person.save
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it{should respond_to(:find_by_firstname)}
|
114
|
+
it{should respond_to(:find_by_lastname)}
|
115
|
+
it{should respond_to(:find_by_date_of_birth)}
|
116
|
+
|
117
|
+
it "find by firstname" do
|
118
|
+
result = Person.find_by_firstname("Werner")
|
119
|
+
result.should be_instance_of Array
|
120
|
+
result.should have(2).items
|
121
|
+
end
|
122
|
+
|
123
|
+
it "find by lastname" do
|
124
|
+
result = Person.find_by_lastname("Winter")
|
125
|
+
result.should have(4).items
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
context "instance methods" do
|
132
|
+
|
133
|
+
let(:person) do
|
134
|
+
Person.new(:firstname => "Bill",
|
135
|
+
:lastname => "Cosby")
|
136
|
+
end
|
137
|
+
subject{person}
|
138
|
+
|
139
|
+
it{should respond_to(:attributes)}
|
140
|
+
it{should respond_to(:to_hsh)}
|
141
|
+
it{should respond_to(:update_attributes)}
|
142
|
+
it{should respond_to(:save)}
|
143
|
+
it{should respond_to(:new_record?)}
|
144
|
+
it{should respond_to(:as_json)}
|
145
|
+
|
146
|
+
context "defining attributes" do
|
147
|
+
|
148
|
+
context "get access to getters" do
|
149
|
+
|
150
|
+
it{should respond_to(:firstname)}
|
151
|
+
it{should respond_to(:lastname)}
|
152
|
+
it{should respond_to(:date_of_birth)}
|
153
|
+
it{should respond_to(:id)}
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
context "get access to setters" do
|
158
|
+
|
159
|
+
it{should respond_to(:firstname=)}
|
160
|
+
it{should respond_to(:lastname=)}
|
161
|
+
it{should respond_to(:date_of_birth=)}
|
162
|
+
it{should respond_to(:id=)}
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
context "setting attributes and getting their values" do
|
167
|
+
|
168
|
+
it "first name should be Bill" do
|
169
|
+
person.firstname.should eq "Bill"
|
170
|
+
end
|
171
|
+
|
172
|
+
it "last name should be Cosby" do
|
173
|
+
person.lastname.should eq "Cosby"
|
174
|
+
end
|
175
|
+
|
176
|
+
context "when using the constructor" do
|
177
|
+
|
178
|
+
it "an id is generated" do
|
179
|
+
person.id.should_not be_nil
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
context "#attributes" do
|
186
|
+
|
187
|
+
subject{person.attributes}
|
188
|
+
|
189
|
+
it "is a Hash" do
|
190
|
+
subject.should be_instance_of Hash
|
191
|
+
end
|
192
|
+
|
193
|
+
it "includes the defined attributes as keys" do
|
194
|
+
keys = subject.keys
|
195
|
+
keys.should include(:firstname)
|
196
|
+
keys.should include(:lastname)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "and its values as value of the key" do
|
200
|
+
values = subject.values
|
201
|
+
values.should include("Bill")
|
202
|
+
values.should include("Cosby")
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "#save" do
|
211
|
+
|
212
|
+
it "returns true if successful" do
|
213
|
+
person.new_record?.should be true
|
214
|
+
person.save.should be true
|
215
|
+
end
|
216
|
+
|
217
|
+
it "sets new_record to false" do
|
218
|
+
person.save
|
219
|
+
person.new_record?.should be false
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
context "#update_attributes" do
|
225
|
+
|
226
|
+
let(:another_person) do
|
227
|
+
Person.new(:firstname => "max", :lastname => "Mustermann")
|
228
|
+
end
|
229
|
+
|
230
|
+
it "returns true if successful" do
|
231
|
+
another_person.save
|
232
|
+
result = another_person.update_attributes(:firstname => "William")
|
233
|
+
result.should be true
|
234
|
+
another_person.new_record?.should be false
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
context "#delete" do
|
240
|
+
|
241
|
+
let(:another_person) do
|
242
|
+
Person.new(:firstname => "max", :lastname => "Mustermann")
|
243
|
+
end
|
244
|
+
|
245
|
+
it "raise UndeletableError if it is a new record" do
|
246
|
+
expect{another_person.delete}.to raise_error Jason::Errors::UndeletableError
|
247
|
+
end
|
248
|
+
|
249
|
+
it "returns true if successful" do
|
250
|
+
another_person.save
|
251
|
+
person = Person.find(another_person.id)
|
252
|
+
person.delete.should be true
|
253
|
+
expect{Person.find(person.id)}.to raise_error Jason::Errors::DocumentNotFoundError
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|