ixtlan-datamapper 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/MIT-LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +29 -0
- data/ixtlan-datamapper.gemspec +32 -0
- data/lib/ixtlan-datamapper.rb +23 -0
- data/lib/ixtlan/datamapper/collection.rb +28 -0
- data/lib/ixtlan/datamapper/collection.rb~ +28 -0
- data/lib/ixtlan/datamapper/conditional_get.rb +46 -0
- data/lib/ixtlan/datamapper/conditional_get.rb~ +47 -0
- data/lib/ixtlan/datamapper/cuba_plugin.rb~ +74 -0
- data/lib/ixtlan/datamapper/immutable.rb +18 -0
- data/lib/ixtlan/datamapper/immutable.rb~ +83 -0
- data/lib/ixtlan/datamapper/modified.rb~ +83 -0
- data/lib/ixtlan/datamapper/modified_by.rb +39 -0
- data/lib/ixtlan/datamapper/modified_by.rb~ +39 -0
- data/lib/ixtlan/datamapper/optimistic.rb~ +53 -0
- data/lib/ixtlan/datamapper/optimistic_get.rb +71 -0
- data/lib/ixtlan/datamapper/optimistic_get.rb~ +72 -0
- data/lib/ixtlan/datamapper/stale_check.rb~ +49 -0
- data/lib/ixtlan/datamapper/stale_object_exception.rb +26 -0
- data/lib/ixtlan/datamapper/stale_object_exception.rb~ +26 -0
- data/lib/ixtlan/datamapper/use_utc.rb +4 -0
- data/lib/ixtlan/datamapper/use_utc.rb~ +5 -0
- data/lib/ixtlan/datamapper/validations_ext.rb +61 -0
- data/spec/collection_spec.rb +57 -0
- data/spec/collection_spec.rb~ +54 -0
- data/spec/conditional_get_spec.rb +69 -0
- data/spec/conditional_get_spec.rb~ +70 -0
- data/spec/datamapper_spec.rb~ +74 -0
- data/spec/immutable_spec.rb +33 -0
- data/spec/immutable_spec.rb~ +70 -0
- data/spec/modified_by_spec.rb +54 -0
- data/spec/modified_by_spec.rb~ +33 -0
- data/spec/optimistic_get_spec.rb +57 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/spec_helper.rb~ +12 -0
- metadata +199 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module DataMapper
|
3
|
+
module Immutable
|
4
|
+
def self.included( model )
|
5
|
+
model.class_eval do
|
6
|
+
validates_with_method :validate_immutable
|
7
|
+
end
|
8
|
+
end
|
9
|
+
def validate_immutable
|
10
|
+
if dirty? && ! new?
|
11
|
+
[ false, 'object is immutable' ]
|
12
|
+
else
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'dm-aggregates'
|
2
|
+
module Ixtlan
|
3
|
+
module ConditionalGet
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def _conditional_get( method, id, modified_since, attribute )
|
8
|
+
if self.count( attribute => modified_since,
|
9
|
+
:id => id ) == 0
|
10
|
+
self.send( method, id )
|
11
|
+
else
|
12
|
+
# return false when up-to-date
|
13
|
+
# that helps to distinguish nil from not-found
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
def conditional_get!( id, modified_since, attribute = :updated_at )
|
21
|
+
_conditional_get( :get!, id, modified_since, attribute )
|
22
|
+
end
|
23
|
+
|
24
|
+
def conditional_get( id, modified_since, attribute = :updated_at )
|
25
|
+
_conditional_get( :get, id, modified_since, attribute )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
DataMapper::Associations::OneToMany::Collection.send( :include,
|
29
|
+
ConditionalGet )
|
30
|
+
DataMapper::Associations::ManyToMany::Collection.send( :include,
|
31
|
+
ConditionalGet )
|
32
|
+
|
33
|
+
module Immutable
|
34
|
+
def self.included( model )
|
35
|
+
model.class_eval do
|
36
|
+
validates_with_method :validate_immutable
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def validate_immutable
|
40
|
+
if dirty? && ! new?
|
41
|
+
[ false, 'object is immutable' ]
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ModifiedBy
|
49
|
+
|
50
|
+
def current_user=( user )
|
51
|
+
@_current_user = user
|
52
|
+
end
|
53
|
+
|
54
|
+
def current_user
|
55
|
+
@_current_user
|
56
|
+
end
|
57
|
+
|
58
|
+
def _modified_by
|
59
|
+
self.modified_by = @_current_user if dirty? && @_current_user
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_modified_by
|
63
|
+
if @_current_user || ! dirty?
|
64
|
+
true
|
65
|
+
else
|
66
|
+
[ false, 'current_user was not set' ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module Methods
|
71
|
+
def modified_by( *args )
|
72
|
+
belongs_to :modified_by, *args
|
73
|
+
|
74
|
+
before :valid?, :_modified_by
|
75
|
+
|
76
|
+
validates_with_method :validate_modified_by
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
DataMapper::Model.append_inclusions self
|
81
|
+
DataMapper::Model.append_extensions Methods
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'dm-aggregates'
|
2
|
+
module Ixtlan
|
3
|
+
module ConditionalGet
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def _conditional_get( method, id, modified_since, attribute )
|
8
|
+
if self.count( attribute => modified_since,
|
9
|
+
:id => id ) == 0
|
10
|
+
self.send( method, id )
|
11
|
+
else
|
12
|
+
# return false when up-to-date
|
13
|
+
# that helps to distinguish nil from not-found
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
def conditional_get!( id, modified_since, attribute = :updated_at )
|
21
|
+
_conditional_get( :get!, id, modified_since, attribute )
|
22
|
+
end
|
23
|
+
|
24
|
+
def conditional_get( id, modified_since, attribute = :updated_at )
|
25
|
+
_conditional_get( :get, id, modified_since, attribute )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
DataMapper::Associations::OneToMany::Collection.send( :include,
|
29
|
+
ConditionalGet )
|
30
|
+
DataMapper::Associations::ManyToMany::Collection.send( :include,
|
31
|
+
ConditionalGet )
|
32
|
+
|
33
|
+
module Immutable
|
34
|
+
def self.included( model )
|
35
|
+
model.class_eval do
|
36
|
+
validates_with_method :validate_immutable
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def validate_immutable
|
40
|
+
if dirty? && ! new?
|
41
|
+
[ false, 'object is immutable' ]
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ModifiedBy
|
49
|
+
|
50
|
+
def current_user=( user )
|
51
|
+
@_current_user = user
|
52
|
+
end
|
53
|
+
|
54
|
+
def current_user
|
55
|
+
@_current_user
|
56
|
+
end
|
57
|
+
|
58
|
+
def _modified_by
|
59
|
+
self.modified_by = @_current_user if dirty? && @_current_user
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_modified_by
|
63
|
+
if @_current_user || ! dirty?
|
64
|
+
true
|
65
|
+
else
|
66
|
+
[ false, 'current_user was not set' ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module Methods
|
71
|
+
def modified_by( *args )
|
72
|
+
belongs_to :modified_by, *args
|
73
|
+
|
74
|
+
before :valid?, :_modified_by
|
75
|
+
|
76
|
+
validates_with_method :validate_modified_by
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
DataMapper::Model.append_inclusions self
|
81
|
+
DataMapper::Model.append_extensions Methods
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module DataMapper
|
3
|
+
module ModifiedBy
|
4
|
+
|
5
|
+
def current_user=( user )
|
6
|
+
@_current_user = user
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_user
|
10
|
+
@_current_user
|
11
|
+
end
|
12
|
+
|
13
|
+
def _modified_by
|
14
|
+
self.modified_by = @_current_user if dirty? && @_current_user
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_modified_by
|
18
|
+
if @_current_user || ! dirty?
|
19
|
+
true
|
20
|
+
else
|
21
|
+
[ false, 'current_user was not set' ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Methods
|
26
|
+
def modified_by( *args )
|
27
|
+
belongs_to :modified_by, *args
|
28
|
+
|
29
|
+
before :valid?, :_modified_by
|
30
|
+
|
31
|
+
validates_with_method :validate_modified_by
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
::DataMapper::Model.append_inclusions self
|
36
|
+
::DataMapper::Model.append_extensions Methods
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module DataMapper
|
3
|
+
module ModifiedBy
|
4
|
+
|
5
|
+
def current_user=( user )
|
6
|
+
@_current_user = user
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_user
|
10
|
+
@_current_user
|
11
|
+
end
|
12
|
+
|
13
|
+
def _modified_by
|
14
|
+
self.modified_by = @_current_user if dirty? && @_current_user
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_modified_by
|
18
|
+
if @_current_user || ! dirty?
|
19
|
+
true
|
20
|
+
else
|
21
|
+
[ false, 'current_user was not set' ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Methods
|
26
|
+
def modified_by( *args )
|
27
|
+
belongs_to :modified_by, *args
|
28
|
+
|
29
|
+
before :valid?, :_modified_by
|
30
|
+
|
31
|
+
validates_with_method :validate_modified_by
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
::DataMapper::Model.append_inclusions self
|
36
|
+
::DataMapper::Model.append_extensions Methods
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2012 mkristian
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require 'ixtlan/optimistic/stale_check'
|
22
|
+
module Ixtlan
|
23
|
+
module Optimistic
|
24
|
+
module DataMapper
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend StaleCheck
|
28
|
+
base.class_eval do
|
29
|
+
|
30
|
+
def self.optimistic_get(updated_at, *args)
|
31
|
+
result = get( *args )
|
32
|
+
if result
|
33
|
+
__check( updated_at )
|
34
|
+
__check_stale( updated_at, result )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.optimistic_get!(updated_at, *args)
|
39
|
+
result = get!( *args )
|
40
|
+
__check( updated_at )
|
41
|
+
__check_stale( updated_at, result )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
::DataMapper::Model.append_inclusions( Ixtlan::Optimistic::DataMapper )
|
47
|
+
::DataMapper::Associations::OneToMany::Collection.send( :include,
|
48
|
+
Ixtlan::Optimistic::DataMapper )
|
49
|
+
::DataMapper::Associations::ManyToMany::Collection.send( :include,
|
50
|
+
Ixtlan::Optimistic::DataMapper )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2012 mkristian
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require 'ixtlan/datamapper/stale_object_exception'
|
22
|
+
module Ixtlan
|
23
|
+
module DataMapper
|
24
|
+
module OptimisticGet
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend OptimisticGet
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
def optimistic_get(updated_at, *args)
|
33
|
+
result = get( *args )
|
34
|
+
if result
|
35
|
+
check_stale( updated_at, result )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def optimistic_get!(updated_at, *args)
|
40
|
+
result = get!( *args )
|
41
|
+
check_stale( updated_at, result )
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def check_stale( updated_at, result )
|
47
|
+
unless updated_at
|
48
|
+
raise StaleObjectException.new "no 'updated_at' given for #{self}."
|
49
|
+
end
|
50
|
+
if updated_at.is_a? String
|
51
|
+
updated_at = DateTime.parse( updated_at.sub(/[.][0-9]+/, '') )
|
52
|
+
end
|
53
|
+
# rails hacky-de-hack
|
54
|
+
if defined?( ActiveSupport::TimeWithZone ) && updated_at.is_a?( ActiveSupport::TimeWithZone )
|
55
|
+
updated_at = updated_at.to_datetime
|
56
|
+
end
|
57
|
+
if updated_at != result.updated_at && updated_at.strftime("%Y:%m:%d %H:%M:%S") != result.updated_at.strftime("%Y:%m:%d %H:%M:%S")
|
58
|
+
|
59
|
+
raise StaleObjectException.new "#{result.model} with key [#{result.id}] was updated at #{result.updated_at} is newer than #{updated_at}."
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
::DataMapper::Model.append_inclusions( OptimisticGet )
|
66
|
+
::DataMapper::Associations::OneToMany::Collection.send( :include,
|
67
|
+
OptimisticGet )
|
68
|
+
::DataMapper::Associations::ManyToMany::Collection.send( :include,
|
69
|
+
OptimisticGet )
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2012 mkristian
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require 'ixtlan/datamapper/stale_object_exception'
|
22
|
+
module Ixtlan
|
23
|
+
module DataMapper
|
24
|
+
module OptimisticGet
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend OptimisticGet
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
def optimistic_get(updated_at, *args)
|
33
|
+
result = get( *args )
|
34
|
+
if result
|
35
|
+
check_stale( updated_at, result )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def optimistic_get!(updated_at, *args)
|
40
|
+
result = get!( *args )
|
41
|
+
check_stale( updated_at, result )
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def check_stale( updated_at, result )
|
47
|
+
unless updated_at
|
48
|
+
raise StaleObjectException.new "no 'updated_at' given for #{self}."
|
49
|
+
end
|
50
|
+
if updated_at.is_a? String
|
51
|
+
updated_at = DateTime.parse( updated_at.sub(/[.][0-9]+/, '') )
|
52
|
+
end
|
53
|
+
# rails hacky-de-hack
|
54
|
+
if defined?( ActiveSupport::TimeWithZone ) && updated_at.is_a?( ActiveSupport::TimeWithZone )
|
55
|
+
updated_at = updated_at.to_datetime
|
56
|
+
end
|
57
|
+
updated_at = updated_at.new_offset(0)
|
58
|
+
if updated_at != result.updated_at && updated_at.strftime("%Y:%m:%d %H:%M:%S") != result.updated_at.strftime("%Y:%m:%d %H:%M:%S")
|
59
|
+
|
60
|
+
raise StaleObjectException.new "#{result.model} with key [#{result.id}] was updated at #{result.updated_at} is newer than #{updated_at}."
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
::DataMapper::Model.append_inclusions( OptimisticGet )
|
67
|
+
::DataMapper::Associations::OneToMany::Collection.send( :include,
|
68
|
+
OptimisticGet )
|
69
|
+
::DataMapper::Associations::ManyToMany::Collection.send( :include,
|
70
|
+
OptimisticGet )
|
71
|
+
end
|
72
|
+
end
|