risky 1.0.1 → 1.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/README.markdown +8 -4
- data/Rakefile.rb +31 -0
- data/lib/risky.rb +274 -265
- data/lib/risky/gzip.rb +28 -0
- data/lib/risky/indexes.rb +4 -4
- data/lib/risky/inflector.rb +337 -0
- data/lib/risky/list_keys.rb +58 -0
- data/lib/risky/paginated_collection.rb +11 -0
- data/lib/risky/secondary_indexes.rb +196 -0
- data/lib/risky/version.rb +1 -1
- data/risky.gemspec +22 -0
- data/spec/risky/cron_list_spec.rb +52 -0
- data/spec/risky/crud_spec.rb +69 -0
- data/spec/risky/enumerable_spec.rb +45 -0
- data/spec/risky/gzip_spec.rb +73 -0
- data/spec/risky/indexes_spec.rb +34 -0
- data/spec/risky/resolver_spec.rb +55 -0
- data/spec/risky/secondary_indexes_spec.rb +222 -0
- data/spec/risky/threads_spec.rb +57 -0
- data/spec/risky_spec.rb +100 -0
- data/spec/spec_helper.rb +40 -0
- metadata +87 -27
- data/lib/risky/all.rb +0 -4
- data/lib/risky/threadsafe.rb +0 -42
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Indexed < Risky
         | 
| 4 | 
            +
              include Risky::ListKeys
         | 
| 5 | 
            +
              include Risky::Indexes
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              bucket :risky_indexes
         | 
| 8 | 
            +
              value :value
         | 
| 9 | 
            +
              value :unique
         | 
| 10 | 
            +
              index :value
         | 
| 11 | 
            +
              index :unique, :unique => true
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
             | 
| 15 | 
            +
            describe 'indexes' do
         | 
| 16 | 
            +
              before :all do
         | 
| 17 | 
            +
                Indexed.delete_all
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              it 'can index a string' do
         | 
| 21 | 
            +
                o = Indexed.new 'test', 'value' => 'value'
         | 
| 22 | 
            +
                o.save.should_not be_false
         | 
| 23 | 
            +
                Indexed.by_value('value').should === o
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              it 'can keep values unique (mostly)' do
         | 
| 27 | 
            +
                o = Indexed.new '1', 'unique' => 'u'
         | 
| 28 | 
            +
                o.save.should_not be_false
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                o2 = Indexed.new '2', 'unique' => 'u'
         | 
| 31 | 
            +
                o2.save.should be_false
         | 
| 32 | 
            +
                o2.errors[:unique].should == 'taken'
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'risky/resolver'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Thread.abort_on_exception = true
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            class Multi < Risky
         | 
| 7 | 
            +
              include Risky::ListKeys
         | 
| 8 | 
            +
              include Risky::Resolver
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              bucket :risky_mult
         | 
| 11 | 
            +
              allow_mult
         | 
| 12 | 
            +
              value :users, :default => []
         | 
| 13 | 
            +
              value :union, :resolve => :union
         | 
| 14 | 
            +
              value :intersection, :resolve => Risky::Resolver::Resolvers.method(:intersection)
         | 
| 15 | 
            +
              value :max, :resolve => :max
         | 
| 16 | 
            +
              value :min, :resolve => :min
         | 
| 17 | 
            +
              value :merge, :resolve => :merge
         | 
| 18 | 
            +
              value :custom, :resolve => lambda { |xs|
         | 
| 19 | 
            +
                :custom
         | 
| 20 | 
            +
              }
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def self.merge(v)
         | 
| 23 | 
            +
                p = super v
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                p.users = v.map(&:users).min
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                p
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            def test(property, ins, out)
         | 
| 32 | 
            +
              it property do
         | 
| 33 | 
            +
                conflict(Multi, property, ins)[property].should == out
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            def set_test(property, ins, out)
         | 
| 38 | 
            +
              it property do
         | 
| 39 | 
            +
                conflict(Multi, property, ins)[property].to_set.should == out.to_set
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
             | 
| 44 | 
            +
            describe Risky::Resolver do
         | 
| 45 | 
            +
              set_test 'union', [[1], [2]], [1,2]
         | 
| 46 | 
            +
              set_test 'union', [[1], nil], [1]
         | 
| 47 | 
            +
              set_test 'union', [[1,4,1], [2,3], [4,4]], [1,2,3,4]
         | 
| 48 | 
            +
              set_test 'intersection', [[1,2],[]], []
         | 
| 49 | 
            +
              set_test 'intersection', [[1,2,3,4], [1,2,3], [2,3,4]], [2,3]
         | 
| 50 | 
            +
              test 'min', [0,1,2,3], 0
         | 
| 51 | 
            +
              test 'max', [0,2,4,2], 4
         | 
| 52 | 
            +
              test 'max', [nil, nil], nil
         | 
| 53 | 
            +
              test 'max', [nil, 4], 4
         | 
| 54 | 
            +
              test 'custom', ['a', 'b', 'c'], :custom
         | 
| 55 | 
            +
            end
         | 
| @@ -0,0 +1,222 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Album < Risky
         | 
| 4 | 
            +
              include Risky::ListKeys
         | 
| 5 | 
            +
              include Risky::SecondaryIndexes
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              bucket :risky_albums
         | 
| 8 | 
            +
              allow_mult
         | 
| 9 | 
            +
              index2i :artist_id, :map => true
         | 
| 10 | 
            +
              index2i :label_key, :map => '_key', :finder => :find_by_id, :allow_nil => true
         | 
| 11 | 
            +
              index2i :genre, :type => :bin, :allow_nil => true
         | 
| 12 | 
            +
              index2i :tags, :type => :bin, :multi => true, :allow_nil => true
         | 
| 13 | 
            +
              value :name
         | 
| 14 | 
            +
              value :year
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            class Artist < Risky
         | 
| 18 | 
            +
              include Risky::ListKeys
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              bucket :risky_artists
         | 
| 21 | 
            +
              value :name
         | 
| 22 | 
            +
            end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            class Label < Risky
         | 
| 25 | 
            +
              include Risky::ListKeys
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              bucket :risky_labels
         | 
| 28 | 
            +
              value :name
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def self.find_by_id(id)
         | 
| 31 | 
            +
                find(id)
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            class City < Risky
         | 
| 36 | 
            +
              include Risky::SecondaryIndexes
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              bucket :risky_cities
         | 
| 39 | 
            +
              index2i :country_id, :type => :invalid, :allow_nil => true
         | 
| 40 | 
            +
              value :name
         | 
| 41 | 
            +
              value :details
         | 
| 42 | 
            +
            end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
             | 
| 45 | 
            +
            describe Risky::SecondaryIndexes do
         | 
| 46 | 
            +
              let(:artist) { Artist.create(1, :name => 'Motorhead') }
         | 
| 47 | 
            +
              let(:label) { Label.create(1, :name => 'Bronze Records') }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              before :each do
         | 
| 50 | 
            +
                Album.delete_all
         | 
| 51 | 
            +
                Artist.delete_all
         | 
| 52 | 
            +
                Label.delete_all
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              it "sets indexes on initialize" do
         | 
| 56 | 
            +
                album = Album.new(1, {:name => 'Bomber', :year => 1979}, {:artist_id => 2})
         | 
| 57 | 
            +
                album.indexes2i.should == {"artist_id" => 2 }
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              it "defines getter and setter methods" do
         | 
| 61 | 
            +
                album = Album.new(1)
         | 
| 62 | 
            +
                album.artist_id = 1
         | 
| 63 | 
            +
                album.artist_id.should == 1
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              it "defines association getter and setter methods" do
         | 
| 67 | 
            +
                album = Album.new(1)
         | 
| 68 | 
            +
                album.artist = artist
         | 
| 69 | 
            +
                album.artist.should == artist
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              it "defines association getter and setter methods when using suffix" do
         | 
| 73 | 
            +
                album = Album.new(1)
         | 
| 74 | 
            +
                album.label = label
         | 
| 75 | 
            +
                album.label.should == label
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              it "can use a custom finder" do
         | 
| 79 | 
            +
                album = Album.create(1, {:name => 'Bomber', :year => 1979},
         | 
| 80 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                Label.should_receive(:find_by_id).with(label.id).and_return(label)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                album.label.should == label
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "resets association if associated object is not saved" do
         | 
| 88 | 
            +
                artist = Artist.new('new_key')
         | 
| 89 | 
            +
                album = Album.new('new_key')
         | 
| 90 | 
            +
                album.artist = artist
         | 
| 91 | 
            +
                album.artist.should be_nil
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              it "assigns attributs after association assignment" do
         | 
| 95 | 
            +
                album = Album.new(1)
         | 
| 96 | 
            +
                album.artist = artist
         | 
| 97 | 
            +
                album.artist_id.should == artist.id
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              it "assigns association after attribute assignment" do
         | 
| 101 | 
            +
                album = Album.new(1)
         | 
| 102 | 
            +
                album.artist_id = artist.id
         | 
| 103 | 
            +
                album.artist.should == artist
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              it "saves a model with indexes" do
         | 
| 107 | 
            +
                album = Album.new(1, {:name => 'Ace of Spades' }, { :artist_id => 1 }).save
         | 
| 108 | 
            +
                album.artist_id.should == 1
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              it "creates a model with indexes" do
         | 
| 112 | 
            +
                album = Album.create(1, {:name => 'Ace of Spades' }, { :artist_id => 1 })
         | 
| 113 | 
            +
                album.artist_id.should == 1
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
              it "persists association after save" do
         | 
| 117 | 
            +
                album = Album.new('persist_key')
         | 
| 118 | 
            +
                album.name = 'Ace of Spades'
         | 
| 119 | 
            +
                album.artist_id = artist.id
         | 
| 120 | 
            +
                album.save
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                album.artist.should == artist
         | 
| 123 | 
            +
                album.artist_id.should == artist.id
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                album.reload
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                album.artist.should == artist
         | 
| 128 | 
            +
                album.artist_id.should == artist.id
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                album = Album.find(album.key)
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                album.artist.should == artist
         | 
| 133 | 
            +
                album.artist_id.should == artist.id
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              it "finds first by int secondary index" do
         | 
| 137 | 
            +
                album = Album.create(1, {:name => 'Bomber', :year => 1979},
         | 
| 138 | 
            +
                  {:artist_id => artist.id})
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                albums = Album.find_by_index(:artist_id, artist.id)
         | 
| 141 | 
            +
                albums.should == album
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              it "finds all by int secondary index" do
         | 
| 145 | 
            +
                album1 = Album.create(1, {:name => 'Bomber', :year => 1979},
         | 
| 146 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 147 | 
            +
                album2 = Album.create(2, {:name => 'Ace Of Spaces', :year => 1980},
         | 
| 148 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                albums = Album.find_all_by_index(:artist_id, artist.id)
         | 
| 151 | 
            +
                albums.should include(album1)
         | 
| 152 | 
            +
                albums.should include(album2)
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              it "finds all by binary secondary index" do
         | 
| 156 | 
            +
                album = Album.create(1, {:name => 'Bomber', :year => 1979},
         | 
| 157 | 
            +
                  {:artist_id => artist.id, :label_key => label.id, :genre => 'heavy'})
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                Album.find_all_by_index(:genre, 'heavy').should == [album]
         | 
| 160 | 
            +
              end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
              it "finds all by multi binary secondary index" do
         | 
| 163 | 
            +
                album = Album.create(1, {:name => 'Bomber', :year => 1979},
         | 
| 164 | 
            +
                  {:artist_id => artist.id, :label_key => label.id,
         | 
| 165 | 
            +
                   :tags => ['rock', 'heavy']})
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                Album.find_all_by_index(:tags, 'heavy').should == [album]
         | 
| 168 | 
            +
                Album.find_all_by_index(:tags, 'rock').should == [album]
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
              it "paginates keys" do
         | 
| 172 | 
            +
                album1 = Album.create('1', {:name => 'Bomber', :year => 1979},
         | 
| 173 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 174 | 
            +
                album2 = Album.create('2', {:name => 'Ace Of Spaces', :year => 1980},
         | 
| 175 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 176 | 
            +
                album3 = Album.create('3', {:name => 'Overkill', :year => 1979},
         | 
| 177 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                page1 = Album.paginate_keys_by_index(:artist_id, artist.id, :max_results => 2)
         | 
| 180 | 
            +
                page1.should == ['1', '2']
         | 
| 181 | 
            +
                page1.continuation.should_not be_blank
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                page2 = Album.paginate_keys_by_index(:artist_id, artist.id, :max_results => 2, :continuation => page1.continuation)
         | 
| 184 | 
            +
                page2.should == ['3']
         | 
| 185 | 
            +
                page2.continuation.should be_blank
         | 
| 186 | 
            +
              end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
              it "paginates risky objects" do
         | 
| 189 | 
            +
                album1 = Album.create('1', {:name => 'Bomber', :year => 1979},
         | 
| 190 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 191 | 
            +
                album2 = Album.create('2', {:name => 'Ace Of Spaces', :year => 1980},
         | 
| 192 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 193 | 
            +
                album3 = Album.create('3', {:name => 'Overkill', :year => 1979},
         | 
| 194 | 
            +
                  {:artist_id => artist.id, :label_key => label.id})
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                page1 = Album.paginate_by_index(:artist_id, artist.id, :max_results => 2)
         | 
| 197 | 
            +
                page1.should == [album1, album2]
         | 
| 198 | 
            +
                page1.continuation.should_not be_blank
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                page2 = Album.paginate_by_index(:artist_id, artist.id, :max_results => 2, :continuation => page1.continuation)
         | 
| 201 | 
            +
                page2.should == [album3]
         | 
| 202 | 
            +
                page2.continuation.should be_blank
         | 
| 203 | 
            +
              end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
              it "raises an exception when index is nil" do
         | 
| 206 | 
            +
                album = Album.new(1)
         | 
| 207 | 
            +
                expect { album.save }.to raise_error(ArgumentError)
         | 
| 208 | 
            +
              end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
              it "raises an exception when type is invalid" do
         | 
| 211 | 
            +
                city = City.new(1)
         | 
| 212 | 
            +
                expect { city.save }.to raise_error(TypeError)
         | 
| 213 | 
            +
              end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
              it "can inspect a model" do
         | 
| 216 | 
            +
                album = Album.new(1, { :name => 'Bomber' }, { :artist_id => 2 })
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                album.inspect.should match(/Album 1/)
         | 
| 219 | 
            +
                album.inspect.should match(/"name"=>"Bomber"/)
         | 
| 220 | 
            +
                album.inspect.should match(/"artist_id"=>2/)
         | 
| 221 | 
            +
              end
         | 
| 222 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Crud < Risky
         | 
| 4 | 
            +
              include Risky::ListKeys
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              bucket :risky_crud
         | 
| 7 | 
            +
              value :value
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            class Concurrent < Risky
         | 
| 11 | 
            +
              include Risky::ListKeys
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              bucket :risky_concurrent
         | 
| 14 | 
            +
              allow_mult
         | 
| 15 | 
            +
              value :v
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # Merge value v together as a list
         | 
| 18 | 
            +
              def self.merge(versions)
         | 
| 19 | 
            +
                p = super versions
         | 
| 20 | 
            +
                p.v = versions.inject([]) do |merged, version|
         | 
| 21 | 
            +
                  merged + [*version.v]
         | 
| 22 | 
            +
                end.uniq
         | 
| 23 | 
            +
                p
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
            describe 'Threads' do
         | 
| 29 | 
            +
              it 'supports concurrent modification' do
         | 
| 30 | 
            +
                Concurrent.bucket.props['allow_mult'].should be_true
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # Riak doesn't do well with concurrent *new* writes, so get an existing
         | 
| 33 | 
            +
                # value in there first.
         | 
| 34 | 
            +
                c = Concurrent.get_or_new('c')
         | 
| 35 | 
            +
                c.v = []
         | 
| 36 | 
            +
                c.save(:w => :all)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                workers = 10
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                # Make a bunch of concurrent writes
         | 
| 41 | 
            +
                (0...workers).map do |i|
         | 
| 42 | 
            +
                  Thread.new do
         | 
| 43 | 
            +
                    # Give them a little bit of jitter, just to make the vclocks interesting
         | 
| 44 | 
            +
                    sleep rand/6
         | 
| 45 | 
            +
                    c = Concurrent.get_or_new('c')
         | 
| 46 | 
            +
                    c.v << i
         | 
| 47 | 
            +
                    c.save or raise
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end.each do |thread|
         | 
| 50 | 
            +
                  thread.join
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                # Check to ensure we obsoleted or have an extant write for every thread.
         | 
| 54 | 
            +
                final = Concurrent['c', {:r => :all}]
         | 
| 55 | 
            +
                final.v.compact.sort.should == (0...workers).to_a
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
    
        data/spec/risky_spec.rb
    ADDED
    
    | @@ -0,0 +1,100 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class User < Risky
         | 
| 4 | 
            +
              include Risky::ListKeys
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              bucket 'risky_users'
         | 
| 7 | 
            +
              allow_mult
         | 
| 8 | 
            +
              value :admin, :default => false
         | 
| 9 | 
            +
              value :age
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            describe 'Risky' do
         | 
| 13 | 
            +
              before :each do
         | 
| 14 | 
            +
                User.delete_all
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              it 'has a bucket' do
         | 
| 18 | 
            +
                User.bucket.should be_kind_of Riak::Bucket
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              it "can store a value and retrieve it" do
         | 
| 22 | 
            +
                user = User.new('test', 'admin' => true)
         | 
| 23 | 
            +
                user.save.should_not be_false
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                user.key.should == 'test'
         | 
| 26 | 
            +
                user.admin.should == true
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              it "can find" do
         | 
| 30 | 
            +
                user = User.create('test')
         | 
| 31 | 
            +
                User.find('test').should == user
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              it "can find all by key" do
         | 
| 35 | 
            +
                user = User.create('test')
         | 
| 36 | 
            +
                User.find_all_by_key(['test']).should == [user]
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              it "returns id as integer" do
         | 
| 40 | 
            +
                user = User.new
         | 
| 41 | 
            +
                user.id = 1
         | 
| 42 | 
            +
                user.save
         | 
| 43 | 
            +
                user.id.should == 1
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              it "returns id as string" do
         | 
| 47 | 
            +
                user = User.new
         | 
| 48 | 
            +
                user.id = 'test'
         | 
| 49 | 
            +
                user.save
         | 
| 50 | 
            +
                user.id.should == 'test'
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              it "can update attribute" do
         | 
| 54 | 
            +
                user = User.new('test', 'admin' => true)
         | 
| 55 | 
            +
                user.update_attribute(:admin, false)
         | 
| 56 | 
            +
                user.admin.should be_false
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              it "can update attributes" do
         | 
| 60 | 
            +
                user = User.new('test', 'admin' => true)
         | 
| 61 | 
            +
                user.update_attributes({:admin => false})
         | 
| 62 | 
            +
                user.admin.should be_false
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              context "conflict resolution" do
         | 
| 66 | 
            +
                let(:key) { 'siblings' }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                before :each do
         | 
| 69 | 
            +
                  User.new(key, 'age' => 20).save
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  user1 = User[key]
         | 
| 72 | 
            +
                  user2 = User[key]
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  user1.age = 21
         | 
| 75 | 
            +
                  user1.save
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  # no conflict
         | 
| 78 | 
            +
                  User.bucket.get(key).siblings.length.should == 1
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  user2.age = 22
         | 
| 81 | 
            +
                  user2.save
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  # it creates a new sibling because of conflict
         | 
| 84 | 
            +
                  User.bucket.get(key).siblings.length.should == 2
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                it 'it resolves the conflict on risky level' do
         | 
| 88 | 
            +
                  user = User[key]
         | 
| 89 | 
            +
                  User.bucket.get(key).siblings.length.should == 2
         | 
| 90 | 
            +
                  user.riak_object.siblings.length.should == 1
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                it 'resolves the conflict on riak data level' do
         | 
| 94 | 
            +
                  user = User[key]
         | 
| 95 | 
            +
                  user.save
         | 
| 96 | 
            +
                  user.riak_object.siblings.length.should == 1
         | 
| 97 | 
            +
                  User.bucket.get(key).siblings.length.should == 1
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
            end
         |