riak-record 0.0.4 → 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.
@@ -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