hyper_record 0.2.8

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,94 @@
1
+ # include path hack
2
+ $:.push(File.dirname(__FILE__) + '/gen-rb')
3
+
4
+ require 'thrift'
5
+ require 'thrift/protocol/binary_protocol_accelerated'
6
+ require 'hql_service'
7
+ require File.dirname(__FILE__) + '/thrift_transport_monkey_patch'
8
+
9
+ module Hypertable
10
+ class ThriftClient < ThriftGen::HqlService::Client
11
+ def initialize(host, port = 38080, timeout_ms = 20000, do_open = true)
12
+ socket = Thrift::Socket.new(host, port, timeout_ms)
13
+ @transport = Thrift::FramedTransport.new(socket)
14
+ protocol = Thrift::BinaryProtocolAccelerated.new(@transport)
15
+ super(protocol)
16
+ open() if do_open
17
+ end
18
+
19
+ def open()
20
+ @transport.open()
21
+ @do_close = true
22
+ end
23
+
24
+ def close()
25
+ @transport.close() if @do_close
26
+ end
27
+
28
+ # more convenience methods
29
+
30
+ def with_scanner(table, scan_spec)
31
+ scanner = open_scanner(table, scan_spec, true)
32
+ begin
33
+ yield scanner
34
+ ensure
35
+ close_scanner(scanner)
36
+ end
37
+ end
38
+
39
+ def with_mutator(table, flags=0, flush_interval=0)
40
+ mutator = open_mutator(table, flags, flush_interval);
41
+ begin
42
+ yield mutator
43
+ ensure
44
+ close_mutator(mutator, 0)
45
+ end
46
+ end
47
+
48
+ # scanner iterator
49
+ def each_cell(scanner)
50
+ cells = next_cells(scanner);
51
+
52
+ while (cells.size > 0)
53
+ cells.each {|cell| yield cell}
54
+ cells = next_cells(scanner);
55
+ end
56
+ end
57
+
58
+ def each_cell_as_arrays(scanner)
59
+ cells = next_cells_as_arrays(scanner);
60
+
61
+ while (cells.size > 0)
62
+ cells.each {|cell| yield cell}
63
+ cells = next_cells_as_arrays(scanner);
64
+ end
65
+ end
66
+
67
+ def each_row(scanner)
68
+ row = next_row(scanner);
69
+
70
+ while (row && row.size > 0)
71
+ yield row
72
+ row = next_row(scanner);
73
+ end
74
+ end
75
+
76
+ def each_row_as_arrays(scanner)
77
+ row = next_row_as_arrays(scanner);
78
+
79
+ while (row && row.size > 0)
80
+ yield row
81
+ row = next_row_as_arrays(scanner);
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.with_thrift_client(host, port, timeout_ms = 20000)
87
+ client = ThriftClient.new(host, port, timeout_ms)
88
+ begin
89
+ yield client
90
+ ensure
91
+ client.close()
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,29 @@
1
+ # Monkey patch for Thrift::FramedTransport typically found in:
2
+ # /usr/local/lib/ruby/site_ruby/1.8/thrift/transport/framed_transport.rb
3
+ #
4
+ # Rev 765279 (current recommended Thrift rev for Hypertable) does not
5
+ # reset buffer state when the connection is reopened. This patch will
6
+ # be submitted to Thrift once it has been tested for a few weeks in
7
+ # production.
8
+
9
+ module Thrift
10
+ class FramedTransport < BaseTransport
11
+ def initialize(transport, read=true, write=true)
12
+ reset_state
13
+ @transport = transport
14
+ @read = read
15
+ @write = write
16
+ end
17
+
18
+ def reset_state
19
+ @rbuf = ''
20
+ @wbuf = ''
21
+ @index = 0
22
+ end
23
+
24
+ def open
25
+ reset_state
26
+ @transport.open
27
+ end
28
+ end
29
+ end
Binary file
@@ -0,0 +1,8 @@
1
+ page_1:
2
+ ROW: page_1
3
+ name: LOLcats and more
4
+ url: http://www.icanhascheezburger.com
5
+ page_2:
6
+ ROW: page_2
7
+ name: ESPN
8
+ url: http://espn.go.com
@@ -0,0 +1 @@
1
+ # intentionally left blank
@@ -0,0 +1,235 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ class Book < ActiveRecord::HyperBase
4
+ has_many :chapters
5
+ has_and_belongs_to_many :authors
6
+ qualified_column :author_id, :qualifiers => ['.*']
7
+ qualified_column :chapter_id, :qualifiers => ['.*']
8
+
9
+ def self.create_table
10
+ hql = "CREATE TABLE #{table_name} (
11
+ 'title',
12
+ 'author_id',
13
+ 'chapter_id'
14
+ )"
15
+ connection.execute(hql)
16
+ end
17
+ end
18
+
19
+ class Chapter < ActiveRecord::HyperBase
20
+ belongs_to :book
21
+
22
+ def self.create_table
23
+ hql = "CREATE TABLE #{table_name} (
24
+ 'title',
25
+ 'book_id' MAX_VERSIONS=1
26
+ )"
27
+ connection.execute(hql)
28
+ end
29
+ end
30
+
31
+ class Author < ActiveRecord::HyperBase
32
+ has_and_belongs_to_many :books
33
+ qualified_column :book_id, :qualifiers => ['.*']
34
+
35
+ def self.create_table
36
+ hql = "CREATE TABLE #{table_name} (
37
+ 'name',
38
+ 'book_id'
39
+ )"
40
+ connection.execute(hql)
41
+ end
42
+ end
43
+
44
+ module ActiveRecord
45
+ module HyperRecord
46
+ describe HyperBase, '.has_and_belongs_to_many' do
47
+ before(:each) do
48
+ Book.drop_table
49
+ Author.drop_table
50
+ Book.create_table
51
+ Author.create_table
52
+
53
+ @b = Book.new({:title => "Curious George and the Electric Fence"})
54
+ @b.ROW = 'curious_george'
55
+ @b.save!
56
+
57
+ @a1 = Author.new({:name => 'Irvine Welsh', :ROW => 'irvine_welsh'})
58
+ @a1.save!
59
+ @a2 = Author.new({:name => 'Douglas Adams', :ROW => 'douglas_adams'})
60
+ @a2.save!
61
+ end
62
+
63
+ it "should support addition of association elements using <<" do
64
+ @b.authors.should be_empty
65
+ @b.authors << @a1
66
+ @b.authors.should == [@a1]
67
+ @b.reload
68
+ @b.authors.should == [@a1]
69
+ @a1.books.should == [@b]
70
+ end
71
+
72
+ it "should allow multiple objects to be associated through HABTM" do
73
+ @b.authors.should be_empty
74
+ @b.authors << @a1
75
+ @b.authors << @a2
76
+ @b.authors.map{|x| x.ROW}.sort.should == [@a1, @a2].map{|x| x.ROW}.sort
77
+ @b.reload
78
+ @b.authors.map{|x| x.ROW}.sort.should == [@a1, @a2].map{|x| x.ROW}.sort
79
+ @a1.books.map{|x| x.ROW}.sort.should == [@b].map{|x| x.ROW}.sort
80
+ @a2.books.map{|x| x.ROW}.sort.should == [@b].map{|x| x.ROW}.sort
81
+ end
82
+
83
+ it "should allow removal of association elements using clear" do
84
+ @b.authors.should be_empty
85
+ @b.authors << @a1
86
+ @b.authors.should == [@a1]
87
+ @b.authors.clear
88
+ @b.reload
89
+ @b.authors.should be_empty
90
+ end
91
+
92
+ it "should allow an object to be created through the association" do
93
+ a = @b.authors.create({:name => 'Harper Lee', :ROW => 'harper_lee'})
94
+ a.new_record?.should be_false
95
+ a.reload
96
+ a.books.should == [@b]
97
+ end
98
+
99
+ it "should allow an object to be newed through the association" do
100
+ @b.authors.should be_empty
101
+ a = @b.authors.new({:name => 'Harper Lee', :ROW => 'harper_lee'})
102
+ a.new_record?.should be_true
103
+ a.save!
104
+ a.reload
105
+ a.books.should be_empty
106
+ end
107
+
108
+ it "should allow removal of association elements using delete" do
109
+ @b.authors.should be_empty
110
+ @b.authors << @a1
111
+ @b.authors << @a2
112
+ @b.authors.delete(@a2)
113
+ @b.reload
114
+ @b.authors.should == [@a1]
115
+ end
116
+
117
+ it "should clean up association cells when an object is destroyed" do
118
+ @b.authors.should be_empty
119
+ @b.authors << @a1
120
+ @b.author_id.should == {@a1.ROW => 1}
121
+ @a1.destroy
122
+ @b.reload
123
+ @b.authors.should be_empty
124
+ @b.author_id.should == {}
125
+ end
126
+ end
127
+
128
+ describe HyperBase, '.belongs_to_and_has_many' do
129
+ before(:each) do
130
+ Book.drop_table
131
+ Chapter.drop_table
132
+ Book.create_table
133
+ Chapter.create_table
134
+
135
+ @b = Book.new({:title => "Curious George and the Electric Fence"})
136
+ @b.ROW = 'curious_george'
137
+ @b.save!
138
+
139
+ @c1 = Chapter.new({:title => 'Ch 1', :ROW => 'c1'})
140
+ @c1.save!
141
+ @b.chapters << @c1
142
+ @c2 = Chapter.new({:title => 'Ch 2', :ROW => 'c2'})
143
+ @c2.save!
144
+ end
145
+
146
+ it "should allow belongs_to assocs between two hyperrecord objects" do
147
+ @c1.book.should == @b
148
+ @c2.book.should == nil
149
+ @b.chapters.to_a.should == [@c1]
150
+ @b.chapters << @c2
151
+ @b.reload
152
+ @b.chapters.to_a.should == [@c1, @c2]
153
+ @c2.book_id.should == @b.ROW
154
+ end
155
+
156
+ it "should clear has_many associations when requested" do
157
+ @b.chapters.to_a.should == [@c1]
158
+ @b.chapters.clear
159
+ @b.reload
160
+ @b.chapters.to_a.should be_empty
161
+ @b.chapter_id.should == {}
162
+ end
163
+
164
+ it "should allow new records through has_many but note that the association cells are not written, so this method is to be avoided" do
165
+ @b.chapters.to_a.should == [@c1]
166
+ c = @b.chapters.new({:ROW => 'c3', :title => 'Ch 3'})
167
+ c.new_record?.should be_true
168
+ c.save!
169
+ @b.reload
170
+ @b.chapters.length.should == 1
171
+ @b.chapters.should == [@c1]
172
+ @b.chapter_id.should == {@c1.ROW => "1"}
173
+ @b.chapters << c
174
+ @b.reload
175
+ @b.chapters.should == [@c1, c]
176
+ @b.chapter_id.should == {@c1.ROW => "1", c.ROW => "1"}
177
+ end
178
+
179
+ it "should allow create records through has_many" do
180
+ @b.chapters.to_a.should == [@c1]
181
+ c = @b.chapters.create({:ROW => 'c3', :title => 'Ch 3'})
182
+ c.new_record?.should be_false
183
+ @b.reload
184
+ @b.chapters.length.should == 2
185
+ @b.chapters.should == [@c1, c]
186
+ end
187
+
188
+ it "should allow new records using << has_many" do
189
+ @b.chapters.to_a.should == [@c1]
190
+ c = Chapter.new({:ROW => 'c3', :title => 'Ch 3'})
191
+ c.new_record?.should be_true
192
+ @b.chapters << c
193
+ @b.reload
194
+ @b.chapters.length.should == 2
195
+ @b.chapters.should == [@c1, c]
196
+ c.reload
197
+ c.book.should == @b
198
+ end
199
+
200
+ it "should support remove of has_many records through delete" do
201
+ @b.chapters.to_a.should == [@c1]
202
+ c = @b.chapters.create({:ROW => 'c3', :title => 'Ch 3'})
203
+ @b.reload
204
+ @b.chapters.should == [@c1, c]
205
+ @b.chapter_id.should == {@c1.ROW => "1", c.ROW => "1"}
206
+ @b.chapters.delete(@c1)
207
+ @b.reload
208
+ @b.chapters.should == [c]
209
+ @b.chapter_id.should == {c.ROW => "1"}
210
+ end
211
+
212
+ it "should update belongs_to id value on assignment" do
213
+ @c2.book_id.should be_blank
214
+ @c2.book = @b
215
+ @c2.save!
216
+ @c2.reload
217
+ @c2.book_id.should == @b.id
218
+ end
219
+
220
+ it "should silently ignore eager loading of belongs_to associations" do
221
+ c1 = Chapter.find(@c1.ROW, :include => [:book])
222
+ # note: no exception, loaded? is marked as true and assoc still works
223
+ c1.book.loaded?.should be_true
224
+ c1.book.should == @b
225
+ end
226
+
227
+ it "should silently ignore eager loading of has_many associations" do
228
+ b = Book.find(@b.ROW, :include => [:chapters])
229
+ # note: no exception, loaded? is marked as false and assoc still works
230
+ b.chapters.loaded?.should be_false
231
+ b.chapters.to_a.should == [@c1]
232
+ end
233
+ end
234
+ end
235
+ end