ixtlan-datamapper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +94 -0
  5. data/Rakefile +29 -0
  6. data/ixtlan-datamapper.gemspec +32 -0
  7. data/lib/ixtlan-datamapper.rb +23 -0
  8. data/lib/ixtlan/datamapper/collection.rb +28 -0
  9. data/lib/ixtlan/datamapper/collection.rb~ +28 -0
  10. data/lib/ixtlan/datamapper/conditional_get.rb +46 -0
  11. data/lib/ixtlan/datamapper/conditional_get.rb~ +47 -0
  12. data/lib/ixtlan/datamapper/cuba_plugin.rb~ +74 -0
  13. data/lib/ixtlan/datamapper/immutable.rb +18 -0
  14. data/lib/ixtlan/datamapper/immutable.rb~ +83 -0
  15. data/lib/ixtlan/datamapper/modified.rb~ +83 -0
  16. data/lib/ixtlan/datamapper/modified_by.rb +39 -0
  17. data/lib/ixtlan/datamapper/modified_by.rb~ +39 -0
  18. data/lib/ixtlan/datamapper/optimistic.rb~ +53 -0
  19. data/lib/ixtlan/datamapper/optimistic_get.rb +71 -0
  20. data/lib/ixtlan/datamapper/optimistic_get.rb~ +72 -0
  21. data/lib/ixtlan/datamapper/stale_check.rb~ +49 -0
  22. data/lib/ixtlan/datamapper/stale_object_exception.rb +26 -0
  23. data/lib/ixtlan/datamapper/stale_object_exception.rb~ +26 -0
  24. data/lib/ixtlan/datamapper/use_utc.rb +4 -0
  25. data/lib/ixtlan/datamapper/use_utc.rb~ +5 -0
  26. data/lib/ixtlan/datamapper/validations_ext.rb +61 -0
  27. data/spec/collection_spec.rb +57 -0
  28. data/spec/collection_spec.rb~ +54 -0
  29. data/spec/conditional_get_spec.rb +69 -0
  30. data/spec/conditional_get_spec.rb~ +70 -0
  31. data/spec/datamapper_spec.rb~ +74 -0
  32. data/spec/immutable_spec.rb +33 -0
  33. data/spec/immutable_spec.rb~ +70 -0
  34. data/spec/modified_by_spec.rb +54 -0
  35. data/spec/modified_by_spec.rb~ +33 -0
  36. data/spec/optimistic_get_spec.rb +57 -0
  37. data/spec/spec_helper.rb +15 -0
  38. data/spec/spec_helper.rb~ +12 -0
  39. metadata +199 -0
@@ -0,0 +1,49 @@
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/object_stale_exception'
22
+ module Ixtlan
23
+ module Optimistic
24
+ module StaleCheck
25
+
26
+ def __check( updated_at )
27
+ unless updated_at
28
+ raise ObjectStaleException.new "no 'updated_at' given for #{self}."
29
+ end
30
+ end
31
+
32
+ def __check_stale( updated_at, result )
33
+ if updated_at.is_a? String
34
+ updated_at = DateTime.parse( updated_at.sub(/[.][0-9]+/, '') )
35
+ end
36
+ # rails hack-de-hack
37
+ if defined?( ActiveSupport::TimeWithZone ) && updated_at.is_a?( ActiveSupport::TimeWithZone )
38
+ updated_at = updated_at.to_datetime
39
+ end
40
+ updated_at = updated_at.new_offset(0)
41
+ 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")
42
+
43
+ raise ObjectStaleException.new "#{result.model} with key [#{result.id}] was updated at #{result.updated_at} is newer than the given one which was updated at #{updated_at}."
44
+ end
45
+ result
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
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
+ module Ixtlan
22
+ module DataMapper
23
+ class StaleObjectException < StandardError
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
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
+ module Ixtlan
22
+ module Optimistic
23
+ class ObjectStaleException < StandardError
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ ENV['TZ'] = 'UTC'
2
+ if defined? JRUBY_VERSION
3
+ org.joda.time.DateTimeZone.setDefault(org.joda.time.DateTimeZone.forID('UTC'))
4
+ end
@@ -0,0 +1,5 @@
1
+
2
+ ENV['TZ'] = 'UTC'
3
+ if defined? JRUBY_VERSION
4
+ org.joda.time.DateTimeZone.setDefault(org.joda.time.DateTimeZone.forID('UTC'))
5
+ end
@@ -0,0 +1,61 @@
1
+ require 'dm-core'
2
+ module DataMapper
3
+ module ValidationsExt
4
+ def _save(execute_hooks = true)
5
+ result = super
6
+
7
+ # TODO: should we wrap it with run_once?
8
+ unless result
9
+ validate if dirty_self?
10
+ validate_parents if dirty_parents?
11
+ validate_children if dirty_children?
12
+ end
13
+
14
+ result
15
+ end
16
+
17
+ # Run validations on the resource
18
+ #
19
+ # @return [Boolean]
20
+ # true if the resource is valid
21
+ #
22
+ # @api public
23
+ def validate
24
+ valid?
25
+ end
26
+
27
+ # Run validations on the associated parent resources
28
+ #
29
+ # @api semipublic
30
+ def validate_parents
31
+ parent_relationships.each do |relationship|
32
+ parent = relationship.get(self)
33
+ unless parent.valid?
34
+ unless errors[relationship.name].include?(parent.errors)
35
+ errors[relationship.name] = parent.errors
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ # Run validations on the associated child resources
42
+ #
43
+ # @api semipublic
44
+ def validate_children
45
+ child_associations.each do |collection|
46
+ if collection.dirty?
47
+ collection.each do |child|
48
+ unless child.valid?
49
+ relationship_errors = (errors[collection.relationship.name] ||= [])
50
+ unless relationship_errors.include?(child.errors)
51
+ relationship_errors << child.errors
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ Model.append_inclusions self
60
+ end # ValidationsExt
61
+ end # DataMapper
@@ -0,0 +1,57 @@
1
+ require_relative 'spec_helper'
2
+
3
+ require 'ixtlan/datamapper/collection'
4
+
5
+ class E
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+ property :name, String
10
+
11
+ end
12
+
13
+ DataMapper.finalize
14
+ DataMapper.auto_migrate!
15
+
16
+ class ECollection < Ixtlan::DataMapper::Collection
17
+ attribute :list, Array[E]
18
+ def data=( d )
19
+ self.list = d
20
+ end
21
+ end
22
+
23
+ describe Ixtlan::DataMapper::Collection do
24
+
25
+ before do
26
+ (1..3).each do |i|
27
+ unless d = E.get( i )
28
+ d = E.new :name => 'huffalump', :id => i
29
+ d.save!
30
+ end
31
+ end
32
+ end
33
+
34
+ it 'defaults to the complete set' do
35
+ c = ECollection.new( E.all )
36
+ c.total_count.must_equal E.count
37
+ c.offset.must_equal 0
38
+ c.list.must_equal E.all
39
+ end
40
+
41
+ it 'skips with given offset' do
42
+ c = ECollection.new( E.all, offset = E.count )
43
+ c.total_count.must_equal E.count
44
+ c.offset.must_equal E.count
45
+ c.list.must_equal []
46
+ end
47
+
48
+ (1..3).each do |i|
49
+ it 'has only one element with offset and limit' do
50
+ c = ECollection.new( E.all, offset = i - 1, 1 )
51
+ c.total_count.must_equal E.count
52
+ c.offset.must_equal i - 1
53
+ c.list.must_equal [E.get( i )]
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,54 @@
1
+ require_relative 'spec_helper'
2
+
3
+ require 'ixtlan/datamapper/modified_by'
4
+
5
+ class D
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+ property :name, String
10
+
11
+ modified_by D
12
+ end
13
+
14
+ DataMapper.finalize
15
+ DataMapper.auto_migrate!
16
+
17
+ describe Ixtlan::DataMapper::ModifiedBy do
18
+
19
+ subject do
20
+ unless d = D.get( 1 )
21
+ d = D.new :name => 'huffalump', :id => 1, :modified_by_id => 1
22
+ d.save!
23
+ end
24
+ d
25
+ end
26
+
27
+ it 'is valid per default' do
28
+ subject.current_user.must_be_nil
29
+ subject.persistence_state?.must_equal true
30
+ subject.dirty?.must_equal false
31
+ subject.valid?.must_equal true
32
+ end
33
+
34
+ it 'is not valid when dirty' do
35
+ subject.name = 'asd'
36
+ subject.current_user.must_be_nil
37
+ subject.dirty?.must_equal true
38
+ subject.valid?.must_equal false
39
+ end
40
+
41
+ it 'is valid when dirty and current_user set' do
42
+ subject.name = 'asd'
43
+ subject.current_user = subject
44
+ subject.dirty?.must_equal true
45
+ subject.valid?.must_equal true
46
+ end
47
+
48
+ it 'is valid after setting current user' do
49
+ subject.current_user = subject
50
+ subject.dirty?.must_equal false
51
+ subject.valid?.must_equal true
52
+ end
53
+
54
+ end
@@ -0,0 +1,69 @@
1
+ require_relative 'spec_helper'
2
+
3
+ require 'ixtlan/datamapper/conditional_get'
4
+
5
+ class B
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+ property :name, String
10
+
11
+ timestamps :at
12
+ end
13
+
14
+ DataMapper.finalize
15
+ DataMapper.auto_migrate!
16
+
17
+ describe Ixtlan::DataMapper::ConditionalGet do
18
+
19
+ subject { B.create :name => 'huffalump' }
20
+
21
+ describe "#conditional_get" do
22
+
23
+ it 'returns false if everything is up-to-date' do
24
+ B.conditional_get(subject.updated_at.to_s, subject.id).must_equal false
25
+ end
26
+
27
+ it 'returns false if everything is up-to-date with DateTime parameter' do
28
+ B.conditional_get!(subject.updated_at, subject.id).must_equal false
29
+ end
30
+
31
+ it 'returns object for given id when there is no modified date' do
32
+ B.conditional_get(nil, subject.id).must_equal subject
33
+ end
34
+
35
+ it 'returns nil on stale modified date' do
36
+ B.conditional_get(subject.updated_at + 1, subject.id).must_equal subject
37
+ end
38
+
39
+ it 'returns nil on non-existing id' do
40
+ B.conditional_get(subject.updated_at.to_s, subject.id + 987).must_be_nil
41
+ end
42
+
43
+ end
44
+
45
+ describe "#conditional_get!" do
46
+
47
+ it 'returns false if everything is up-to-date' do
48
+ B.conditional_get!(subject.updated_at.to_s, subject.id).must_equal false
49
+ end
50
+
51
+ it 'returns false if everything is up-to-date with DateTime parameter' do
52
+ B.conditional_get!(subject.updated_at, subject.id).must_equal false
53
+ end
54
+
55
+ it 'fails with not-found exception with non-existing id' do
56
+ lambda { B.conditional_get!(subject.updated_at.to_s, subject.id + 987) }.must_raise DataMapper::ObjectNotFoundError
57
+ end
58
+
59
+ it 'retuens object with stale modified parameter' do
60
+ B.conditional_get!((subject.updated_at - 1000).to_s, subject.id).must_equal subject
61
+ end
62
+
63
+ it 'returns object without modified parameter given' do
64
+ B.conditional_get!(nil, subject.id).must_equal subject
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,70 @@
1
+ require_relative 'spec_helper'
2
+
3
+ require 'ixtlan/datamapper/use_utc'
4
+ require 'ixtlan/datamapper/conditional_get'
5
+
6
+ class B
7
+ include DataMapper::Resource
8
+
9
+ property :id, Serial
10
+ property :name, String
11
+
12
+ timestamps :at
13
+ end
14
+
15
+ DataMapper.finalize
16
+ DataMapper.auto_migrate!
17
+
18
+ describe Ixtlan::DataMapper::ConditionalGet do
19
+
20
+ subject { B.create :name => 'huffalump' }
21
+
22
+ describe "#conditional_get" do
23
+
24
+ it 'returns false if everything is up-to-date' do
25
+ B.conditional_get(subject.updated_at.to_s, subject.id).must_equal false
26
+ end
27
+
28
+ it 'returns false if everything is up-to-date with DateTime parameter' do
29
+ B.conditional_get!(subject.updated_at, subject.id).must_equal false
30
+ end
31
+
32
+ it 'returns object for given id when there is no modified date' do
33
+ B.conditional_get(nil, subject.id).must_equal subject
34
+ end
35
+
36
+ it 'returns nil on stale modified date' do
37
+ B.conditional_get(subject.updated_at + 1, subject.id).must_equal subject
38
+ end
39
+
40
+ it 'returns nil on non-existing id' do
41
+ B.conditional_get(subject.updated_at.to_s, subject.id + 987).must_be_nil
42
+ end
43
+
44
+ end
45
+
46
+ describe "#conditional_get!" do
47
+
48
+ it 'returns false if everything is up-to-date' do
49
+ B.conditional_get!(subject.updated_at.to_s, subject.id).must_equal false
50
+ end
51
+
52
+ it 'returns false if everything is up-to-date with DateTime parameter' do
53
+ B.conditional_get!(subject.updated_at, subject.id).must_equal false
54
+ end
55
+
56
+ it 'fails with not-found exception with non-existing id' do
57
+ lambda { B.conditional_get!(subject.updated_at.to_s, subject.id + 987) }.must_raise DataMapper::ObjectNotFoundError
58
+ end
59
+
60
+ it 'retuens object with stale modified parameter' do
61
+ B.conditional_get!((subject.updated_at - 1000).to_s, subject.id).must_equal subject
62
+ end
63
+
64
+ it 'returns object without modified parameter given' do
65
+ B.conditional_get!(nil, subject.id).must_equal subject
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,74 @@
1
+ require 'controller'
2
+
3
+ shared_examples 'a X-Headers' do
4
+
5
+ it 'should be able to switch off' do
6
+ subject.send method, :inline => "asd", :x_frame_headers => :off, :x_content_type_headers => :off, :x_xss_protection_headers => :off
7
+ subject.response.headers.should == {}
8
+ end
9
+
10
+ end
11
+
12
+ class MyController < Controller
13
+ x_frame_headers :sameorigin
14
+ x_content_type_headers :off
15
+ x_xss_protection_headers :disabled
16
+ end
17
+
18
+ [:render, :send_file, :send_data].each do |method|
19
+ describe "x-headers using controller method #{method}" do
20
+ context "with simple controller" do
21
+ before do
22
+ Rails.configuration.x_frame_headers = nil
23
+ Rails.configuration.x_content_type_headers = nil
24
+ Rails.configuration.x_xss_protection_headers = nil
25
+ end
26
+ subject { Controller.new }
27
+
28
+ it 'should use default' do
29
+ subject.send method, :inline => "asd"
30
+ subject.response.headers.should == {"X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
31
+ end
32
+
33
+ it_behaves_like "a X-Headers" do
34
+ let(:method) { method }
35
+ end
36
+ end
37
+
38
+ context "with controller with header configuration" do
39
+ before do
40
+ Rails.configuration.x_frame_headers = nil
41
+ Rails.configuration.x_content_type_headers = nil
42
+ Rails.configuration.x_xss_protection_headers = nil
43
+ end
44
+ subject { MyController.new }
45
+
46
+ it 'should use configuration' do
47
+ subject.send method, :inline => "asd"
48
+ subject.response.headers.should == {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0"}
49
+ end
50
+
51
+ it_behaves_like "a X-Headers" do
52
+ let(:method) { method }
53
+ end
54
+ end
55
+
56
+ context "with simple controller with rails configuration" do
57
+ before do
58
+ Rails.configuration.x_frame_headers = :sameorigin
59
+ Rails.configuration.x_content_type_headers = :off
60
+ Rails.configuration.x_xss_protection_headers = :disabled
61
+ end
62
+ subject { Controller.new }
63
+
64
+ it 'should use configuration' do
65
+ subject.send method, :inline => "asd"
66
+ subject.response.headers.should == {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0"}
67
+ end
68
+
69
+ it_behaves_like "a X-Headers" do
70
+ let(:method) { method }
71
+ end
72
+ end
73
+ end
74
+ end