jason-orm 0.1
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 +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
|