ixtlan-datamapper 0.1.0

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.
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