riak-record 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,93 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class Author < RiakRecord::Base
4
+ bucket_name "authors"
5
+ data_attributes :name
6
+ end
7
+
8
+ class Post #< ActiveRecord
9
+ include RiakRecord::Associations
10
+
11
+ belongs_to_riak :author, :class_name => 'Author', :foreign_key => :author_id
12
+ has_many_riak :comments, :class_name => 'Comment', :foreign_key => :post_id
13
+
14
+ def id
15
+ 1
16
+ end
17
+
18
+ def author_id
19
+ @author_id ||= "12"
20
+ end
21
+
22
+ def author_id=(v)
23
+ @author_id = v
24
+ end
25
+
26
+ end
27
+
28
+ class Comment < RiakRecord::Base
29
+ bucket_name "comments"
30
+ data_attributes :comment
31
+
32
+ index_int_attributes :post_id
33
+ end
34
+
35
+ describe RiakRecord::Associations do
36
+ let(:post){ post = Post.new }
37
+
38
+ describe "belongs_to_riak" do
39
+ before :each do
40
+ @author = Author.new("12")
41
+ @author.name = 'Dr.Seuss'
42
+ @author.save
43
+
44
+ @real_author = Author.new("13")
45
+ @real_author.name = 'T. Geisel'
46
+ @real_author.save
47
+ end
48
+
49
+ it "should return the riak object" do
50
+ expect(post.author.name).to eq(@author.name)
51
+ end
52
+
53
+ it "should change the results when you change the foreign key value" do
54
+ expect{
55
+ post.author_id = @real_author.id
56
+ }.to change{ post.author.name }.from(@author.name).to(@real_author.name)
57
+ end
58
+
59
+ it "should set the foreign_key" do
60
+ expect{
61
+ post.author = @real_author
62
+ }.to change{ post.author_id }.from(@author.id).to(@real_author.id)
63
+ end
64
+
65
+ end
66
+
67
+ describe "has_many_riak" do
68
+ before :each do
69
+ @comment = Comment.new("1")
70
+ @comment.comment = "first!"
71
+ @comment.post_id = post.id
72
+ @comment.save
73
+
74
+ @other_comment = Comment.new("2")
75
+ @other_comment.comment = "+2"
76
+ @other_comment.post_id = post.id
77
+ @other_comment.save
78
+
79
+ @comment_for_other_post = Comment.new("3")
80
+ @comment_for_other_post.comment = "first!"
81
+ @comment_for_other_post.post_id = post.id + 1
82
+ @comment_for_other_post.save
83
+ end
84
+ it "should return a RiakRecord::Finder" do
85
+ expect(post.comments).to be_an_instance_of(RiakRecord::Finder)
86
+ end
87
+
88
+ it "should include the associated records" do
89
+ expect(post.comments.all.map(&:id)).to include(@comment.id, @other_comment.id)
90
+ expect(post.comments.all.map(&:id)).to_not include(@comment_for_other_post.id)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class ExampleA < RiakRecord::Base
4
+ bucket_name 'example_a'
5
+ data_attributes :attribute1, :attribute2
6
+ index_int_attributes :index1, :index2
7
+ index_bin_attributes :index3, :index4
8
+ end
9
+
10
+ class ExampleB < RiakRecord::Base
11
+ bucket_name :example_b
12
+ index_int_attributes :example_a_id
13
+
14
+ belongs_to :example_a # optional :class_name => 'ExampleA', :key => :example_a_id || :example_a_key
15
+ end
16
+
17
+ describe RiakRecord::Base do
18
+ it "should set the bucket name on each instance of RiakRecord class" do
19
+ expect(ExampleA.bucket_name).to eq(RiakRecord::Base.namespace+":-:example_a")
20
+ expect(ExampleB.bucket_name).to eq(RiakRecord::Base.namespace+':-:example_b')
21
+ end
22
+
23
+ it "should share a client among all classes" do
24
+ expect(ExampleA.client).to_not eq(nil)
25
+ expect(ExampleA.client).to eq(ExampleB.client)
26
+ end
27
+
28
+ describe "index_names" do
29
+ it "should look up the index by symbol" do
30
+ expect( ExampleA.index_names[:index1] ).to eq("index1_int")
31
+ expect( ExampleA.index_names[:index3] ).to eq("index3_bin")
32
+ end
33
+ end
34
+
35
+ describe "reload" do
36
+ let(:record) { ExampleA.new("1234").tap{|p| p.attribute1 = 'bye'}.save }
37
+ it "should return the same obj" do
38
+ expect( record.reload ).to eq(record)
39
+ end
40
+ it "should discard unsaved changes" do
41
+ record.attribute1 = 'hello' # not saved
42
+ expect{
43
+ record.reload
44
+ }.to change{ record.attribute1 }.from('hello').to('bye')
45
+ end
46
+ end
47
+
48
+ describe "save" do
49
+ let(:record) { ExampleA.new("1234") }
50
+ it "should store the object" do
51
+ record.attribute1 = 'hello'
52
+ record.save
53
+ expect(record.reload.attribute1).to eq('hello')
54
+ end
55
+ end
56
+
57
+ describe "class methods" do
58
+ describe "new" do
59
+ context "when passed in an RObject" do
60
+ let(:robject) { Riak::RObject.new("a-key") }
61
+ let(:record) { ExampleA.new(robject) }
62
+ it "should wrap it" do
63
+ expect(record.riak_object).to eq(robject)
64
+ expect(record).to be_a(ExampleA)
65
+ end
66
+ end
67
+ context "when passed in a string" do
68
+ let(:key) { 'judy' }
69
+ let(:record) { ExampleA.new(key) }
70
+ it "should create an RObject with that id" do
71
+ expect(record).to be_a(ExampleA)
72
+ expect(record.riak_object).to be_a(Riak::RObject)
73
+ expect(record.riak_object.key).to eq(key)
74
+ end
75
+ end
76
+ context "when passed in nil" do
77
+ let(:record) { ExampleA.new() }
78
+ it "should create an RObject with no id" do
79
+ expect(record).to be_a(ExampleA)
80
+ expect(record.riak_object).to be_a(Riak::RObject)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "find" do
86
+ let(:bucket){ double }
87
+ let(:array){ Array.new }
88
+ before :each do
89
+ allow(ExampleA).to receive(:bucket).and_return(bucket)
90
+ end
91
+
92
+ it "should take an array and return an array" do
93
+ expect(bucket).to receive(:get_many).with(array).and_return([])
94
+ ExampleA.find(array)
95
+ end
96
+
97
+ it "should take a string and return nil if not found" do
98
+ e = Riak::FailedRequest.new("not found")
99
+ allow(e).to receive(:not_found?).and_return(true)
100
+ allow(bucket).to receive(:get).with("a-key").and_raise(e)
101
+ expect( ExampleA.find("a-key") ).to be_nil
102
+ end
103
+
104
+ it "should take a string and return an instance of Example" do
105
+ allow(bucket).to receive(:get).with("a-key").and_return(Riak::RObject.new("a-key"))
106
+ expect( ExampleA.find("a-key") ).to be_an_instance_of(ExampleA)
107
+ end
108
+ end
109
+
110
+ describe "where" do
111
+ it "should return an instance of RiakRecord::Finder" do
112
+ expect( ExampleA.where(:index1 => 'hello') ).to be_an_instance_of(RiakRecord::Finder)
113
+ end
114
+ end
115
+
116
+ describe "data_attributes" do
117
+ let(:riak_object) { Riak::RObject.new("obj").tap{|r| r.data = data } }
118
+ let(:data) { {'attribute1' => "1"} }
119
+ let(:record) { ExampleA.new(riak_object) }
120
+ it "should read and write each attribute" do
121
+ expect{
122
+ record.attribute1=('b')
123
+ }.to change{record.attribute1}.from("1").to('b')
124
+
125
+ expect{
126
+ record.attribute2 = 'c'
127
+ }.to change{record.attribute2}.from(nil).to('c')
128
+
129
+ end
130
+
131
+ it "should return attribute from riak_object data" do
132
+ expect( ExampleA.new(riak_object).attribute1 ).to eq("1")
133
+ end
134
+
135
+ end
136
+
137
+ describe "index_int_attributes" do
138
+ let(:riak_object) { Riak::RObject.new("obj").tap{|r| r.indexes["index1_int"] = [1] } }
139
+ let(:record) { ExampleA.new(riak_object) }
140
+ it "should read and write each index" do
141
+ expect{
142
+ record.index1=[2]
143
+ }.to change{record.riak_object.indexes["index1_int"]}.from([1]).to([2])
144
+ end
145
+
146
+ it "should handle non arrays" do
147
+ expect{
148
+ record.index2=2
149
+ }.to change{record.index2}.from([]).to([2])
150
+ end
151
+
152
+ it "should handle nil" do
153
+ expect{
154
+ record.index1 = nil
155
+ }.to change{record.index1}.from([1]).to([])
156
+ end
157
+ end
158
+
159
+ describe "index_bin_attributes" do
160
+ let(:riak_object) { Riak::RObject.new("obj").tap{|r| r.indexes["index3_bin"] = ['apple'] } }
161
+ let(:record) { ExampleA.new(riak_object) }
162
+ it "should read and write each index" do
163
+ expect{
164
+ record.index3=['mac']
165
+ }.to change{record.riak_object.indexes["index3_bin"]}.from(['apple']).to(['mac'])
166
+ end
167
+
168
+ it "should handle non arrays" do
169
+ expect{
170
+ record.index4='mac'
171
+ }.to change{record.index4}.from([]).to(['mac'])
172
+ end
173
+
174
+ it "should handle nil" do
175
+ expect{
176
+ record.index3 = nil
177
+ }.to change{record.index3}.from(['apple']).to([])
178
+ end
179
+ end
180
+
181
+ describe "namespacing buckets" do
182
+ it "should prepend namespace to bucket name" do
183
+ RiakRecord::Base.namespace = "namespace_test"
184
+ expect(ExampleA.bucket_name).to eq("namespace_test:-:example_a")
185
+ end
186
+ end
187
+
188
+ describe "belongs to" do
189
+ let(:record){ ExampleB.new(456).save }
190
+ let(:related_record){ ExampleA.new(123).save }
191
+ it "should set the relation" do
192
+ expect{
193
+ record.example_a = related_record
194
+ }.to change{ record.example_a_id }.from([]).to([related_record.id.to_i])
195
+ end
196
+
197
+ it "should find the relation" do
198
+ record.example_a = related_record
199
+ record.save
200
+ expect(record.reload.example_a).to eq(related_record)
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,163 @@
1
+ require 'pry'
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ class Artist < RiakRecord::Base
5
+ bucket_name 'artist'
6
+ data_attributes :name, :bin_number
7
+ index_int_attributes :sales
8
+ index_bin_attributes :category
9
+ end
10
+
11
+ describe RiakRecord::Finder do
12
+ before :each do
13
+ @pop_artists = Array(1..155).map do |c|
14
+ a = Artist.new(c.to_s)
15
+ a.name = "Pop Artist #{c}"
16
+ a.category = "pop"
17
+ a.bin_number = c % 10
18
+ a.sales = (c % 10) * 100
19
+ a.save
20
+ end
21
+
22
+ @rock_artists = Array(156..203).map do |c|
23
+ a = Artist.new(c.to_s)
24
+ a.name = "Rock Artist #{c}"
25
+ a.category = "rock"
26
+ a.bin_number = c % 10
27
+ a.sales = (c % 10) * 100
28
+ a.save
29
+ end
30
+ end
31
+
32
+ let(:pop_finder){ RiakRecord::Finder.new(Artist, :category => 'pop') }
33
+ let(:country_finder){ RiakRecord::Finder.new(Artist, :category => 'country') }
34
+
35
+ describe "all" do
36
+
37
+ it "should return all the records that match the conditions" do
38
+ expect( pop_finder.all.map(&:id).sort ).to eq(@pop_artists.map(&:id).sort)
39
+ end
40
+
41
+ it "should not return all the record that do not match the conditions" do
42
+ expect( pop_finder.all.map(&:id) ).to_not include(@rock_artists.map(&:id))
43
+ end
44
+
45
+ end
46
+
47
+ describe "count" do
48
+ it "should return the count by map reduce" do
49
+ expect(pop_finder).to receive(:count_map_reduce).and_call_original
50
+ expect(pop_finder.count).to eq(155)
51
+ end
52
+
53
+ context "all results in memory" do
54
+ before :each do
55
+ pop_finder.all # load complete
56
+ end
57
+
58
+ it "should not count by map reduce if load complete" do
59
+ expect(pop_finder).to_not receive(:count_map_reduce)
60
+ expect(pop_finder.count).to eq(155)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "enumberable methods" do
66
+ it "should yield once per block" do
67
+ expect( pop_finder.all?{|o| o.category == 'rock'} ).to eq(true)
68
+ end
69
+ end
70
+
71
+ describe "any?" do
72
+ it "should return true if there are any" do
73
+ expect(pop_finder.any?).to eq(true)
74
+ end
75
+ it "should return false if there aren't any" do
76
+ expect(country_finder.any?).to eq(false)
77
+ end
78
+ it "should handle a block" do
79
+ expect(pop_finder.any?{|o| o.id == "none"}).to eq(false)
80
+ expect(pop_finder.any?{|o| o.id == @pop_artists.last.id}).to eq(true)
81
+ end
82
+ end
83
+
84
+ describe "empty? / none" do
85
+ it "should return true if there aren't any" do
86
+ expect(pop_finder.empty?).to eq(false)
87
+ end
88
+ it "should return false if there are any" do
89
+ expect(country_finder.empty?).to eq(true)
90
+ end
91
+ it "should return true if there aren't any" do
92
+ expect(pop_finder.none?).to eq(false)
93
+ end
94
+ it "should return false if there are any" do
95
+ expect(country_finder.none?).to eq(true)
96
+ end
97
+ it "should handle a block" do
98
+ expect(pop_finder.none?{|o| o.id == 'none'}).to eq(true)
99
+ expect(pop_finder.none?{|o| o.id == @pop_artists.last.id}).to eq(false)
100
+ end
101
+ end
102
+
103
+ describe "first" do
104
+ it "should return something if not empty" do
105
+ expect(pop_finder.first).to_not be_nil
106
+ end
107
+ it "should return nil if empty" do
108
+ expect(country_finder.first).to be_nil
109
+ end
110
+ it "should handle a quantity" do
111
+ expect(pop_finder.first(71).count).to eq(71)
112
+ end
113
+ end
114
+
115
+ describe "find" do
116
+ it "should return the first object to match block" do
117
+ expect(pop_finder.find{|o| o.id =~ /^4/}.id).to match(/^4/)
118
+ end
119
+ end
120
+
121
+ describe "detect" do
122
+ it "should return the first object to match block" do
123
+ expect(pop_finder.detect{|o| o.id =~ /^3/}.id).to match(/^3/)
124
+ end
125
+ end
126
+
127
+ describe "count_by(attribute)" do
128
+ let(:bin_number_counts){
129
+ { "0" => 15, "1" => 16, "2" => 16, "3" => 16, "4" => 16, "5" => 16, "6" => 15, "7" => 15, "8" => 15, "9" => 15}
130
+ }
131
+
132
+ let(:sales_counts){
133
+ { "0" => 15, "100" => 16, "200" => 16, "300" => 16, "400" => 16, "500" => 16, "600" => 15, "700" => 15, "800" => 15, "900" => 15}
134
+ }
135
+
136
+ it "should count_by" do
137
+ expect(pop_finder).to receive(:count_by_map_reduce).with(:bin_number).and_call_original
138
+ expect(pop_finder.count_by(:bin_number)).to eq(bin_number_counts)
139
+ end
140
+
141
+ context "everything loaded" do
142
+ before :each do
143
+ pop_finder.all
144
+ end
145
+
146
+ it "should count_by without map reduce" do
147
+ expect(pop_finder).to_not receive(:count_by_map_reduce).with(:bin_number)
148
+ expect(pop_finder.count_by(:bin_number)).to eq(bin_number_counts)
149
+ end
150
+
151
+ end
152
+
153
+ context "attribute is index" do
154
+ it "should count_by" do
155
+ expect(pop_finder).to receive(:count_by_map_reduce).with(:sales).and_call_original
156
+ expect(pop_finder.count_by(:sales)).to eq(sales_counts)
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe RiakRecord do
4
+ end
data/spec/spec_helper.rb CHANGED
@@ -18,14 +18,20 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
18
  $LOAD_PATH.unshift(File.dirname(__FILE__))
19
19
 
20
20
  require 'rspec'
21
- require 'riak-record'
21
+ require 'riak_record'
22
22
 
23
- RiakRecord.client = Riak::Client.new(:host => 'localhost', :pb_port => '8087')
23
+ RiakRecord::Base.client = Riak::Client.new(:host => 'localhost', :pb_port => '8087')
24
+ Riak.disable_list_keys_warnings = true
24
25
 
25
26
  # Requires supporting files with custom matchers and macros, etc,
26
27
  # in ./support/ and its subdirectories.
27
28
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
28
29
 
29
30
  RSpec.configure do |config|
30
-
31
+ config.before do
32
+ RiakRecord::Base.namespace = 'test'
33
+ RiakRecord::Base.all_buckets_in_namespace do |bucket|
34
+ bucket.keys.each{|k| bucket.delete(k) }
35
+ end
36
+ end
31
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riak-record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Graff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-18 00:00:00.000000000 Z
11
+ date: 2014-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: riak-client
@@ -94,6 +94,48 @@ dependencies:
94
94
  - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 4.3.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 4.3.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: terminal-notifier-guard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  description: |-
98
140
  RiakRecord is a thin and immature wrapper around riak-ruby-client. It creates a bucket for
99
141
  each class, provides a simple finder, and creates attribute reader.
@@ -108,13 +150,21 @@ files:
108
150
  - .rspec
109
151
  - Gemfile
110
152
  - Gemfile.lock
153
+ - Guardfile
111
154
  - LICENSE.txt
112
155
  - README.md
113
156
  - Rakefile
157
+ - TODO.md
114
158
  - VERSION
115
- - lib/riak-record.rb
159
+ - lib/riak_record.rb
160
+ - lib/riak_record/associations.rb
161
+ - lib/riak_record/base.rb
162
+ - lib/riak_record/finder.rb
116
163
  - riak-record.gemspec
117
- - spec/riak-record_spec.rb
164
+ - spec/riak_record/associations_spec.rb
165
+ - spec/riak_record/base_spec.rb
166
+ - spec/riak_record/finder_spec.rb
167
+ - spec/riak_record_spec.rb
118
168
  - spec/spec_helper.rb
119
169
  homepage: http://github.com/rgraff/riak-record
120
170
  licenses:
data/lib/riak-record.rb DELETED
@@ -1,77 +0,0 @@
1
- require 'riak'
2
-
3
- class RiakRecord
4
- attr_reader :riak_object
5
-
6
- def initialize(r = nil)
7
- r = self.class.bucket.new(r) if r.nil? || r.is_a?(String)
8
- raise ArgumentError unless r.is_a? Riak::RObject
9
- @riak_object = r
10
- end
11
-
12
- def data
13
- riak_object.data
14
- end
15
-
16
- def save
17
- riak_object.store(:returnbody => false)
18
- end
19
-
20
- def self.bucket_name(name = :not_a_name)
21
- @bucket_name = name unless name == :not_a_name
22
- namespace.present? ? namespace+":-:"+@bucket_name : @bucket_name
23
- end
24
-
25
- def self.bucket
26
- @bucket ||= client.bucket(bucket_name)
27
- end
28
-
29
- def self.record_attributes(*attributes)
30
- attributes.each do |method_name|
31
- define_method(method_name) do
32
- data[method_name]
33
- end
34
-
35
- define_method("#{method_name}=".to_sym) do |value|
36
- data[method_name] = value
37
- end
38
- end
39
- end
40
-
41
- def self.find(key_or_keys)
42
- return find_many(key_or_keys) if key_or_keys.is_a?(Array)
43
-
44
- begin
45
- self.new(bucket.get(key_or_keys.to_s))
46
- rescue Riak::FailedRequest => e
47
- if e.not_found?
48
- nil
49
- else
50
- raise e
51
- end
52
- end
53
- end
54
-
55
- def self.find_many(keys)
56
- hash = bucket.get_many(keys.map(&:to_s))
57
- keys.map{ |k| hash[k] }
58
- end
59
-
60
- @@namespace = nil
61
- def self.namespace=(namespace)
62
- @@namespace = namespace
63
- end
64
-
65
- def self.namespace
66
- @@namespace
67
- end
68
-
69
- def self.client=(client)
70
- @@client = client
71
- end
72
-
73
- def self.client
74
- @@client
75
- end
76
-
77
- end