tylerkovacs-hypertable_adapter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/qualified_column'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
class Base
|
6
|
+
def self.require_hypertable_thrift_client
|
7
|
+
# Include the hypertools driver if one hasn't already been loaded
|
8
|
+
unless defined? Hypertable::ThriftClient
|
9
|
+
gem 'hypertable-thrift-client'
|
10
|
+
require_dependency 'thrift_client'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.hypertable_connection(config)
|
15
|
+
config = config.symbolize_keys
|
16
|
+
require_hypertable_thrift_client
|
17
|
+
|
18
|
+
raise "Hypertable/ThriftBroker config missing :host" if !config[:host]
|
19
|
+
connection = Hypertable::ThriftClient.new(config[:host], config[:port])
|
20
|
+
|
21
|
+
ConnectionAdapters::HypertableAdapter.new(connection, logger, config)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ConnectionAdapters
|
26
|
+
class HypertableAdapter < AbstractAdapter
|
27
|
+
@@read_latency = 0.0
|
28
|
+
@@write_latency = 0.0
|
29
|
+
cattr_accessor :read_latency, :write_latency
|
30
|
+
|
31
|
+
CELL_FLAG_DELETE_ROW = 0
|
32
|
+
CELL_FLAG_DELETE_COLUMN_FAMILY = 1
|
33
|
+
CELL_FLAG_DELETE_CELL = 2
|
34
|
+
CELL_FLAG_INSERT = 255
|
35
|
+
|
36
|
+
def initialize(connection, logger, config)
|
37
|
+
super(connection, logger)
|
38
|
+
@config = config
|
39
|
+
@hypertable_column_names = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.reset_timing
|
43
|
+
@@read_latency = 0.0
|
44
|
+
@@write_latency = 0.0
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.get_timing
|
48
|
+
[@@read_latency, @@write_latency]
|
49
|
+
end
|
50
|
+
|
51
|
+
def convert_select_columns_to_array_of_columns(s, columns=nil)
|
52
|
+
select_rows = s.class == String ? s.split(',').map{|s| s.strip} : s
|
53
|
+
select_rows = select_rows.reject{|s| s == '*'}
|
54
|
+
|
55
|
+
if select_rows.empty? and !columns.blank?
|
56
|
+
for c in columns
|
57
|
+
next if c.name == 'ROW' # skip over the ROW key, always included
|
58
|
+
if c.is_a?(QualifiedColumn)
|
59
|
+
for q in c.qualifiers
|
60
|
+
select_rows << qualified_column_name(c.name, q.to_s)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
select_rows << c.name
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
select_rows
|
69
|
+
end
|
70
|
+
|
71
|
+
def adapter_name
|
72
|
+
'Hypertable'
|
73
|
+
end
|
74
|
+
|
75
|
+
def supports_migrations?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def native_database_types
|
80
|
+
{
|
81
|
+
:string => { :name => "varchar", :limit => 255 }
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def sanitize_conditions(options)
|
86
|
+
case options[:conditions]
|
87
|
+
when Hash
|
88
|
+
# requires Hypertable API to support query by arbitrary cell value
|
89
|
+
raise "HyperRecord does not support specifying conditions by Hash"
|
90
|
+
when NilClass
|
91
|
+
# do nothing
|
92
|
+
else
|
93
|
+
raise "Only hash conditions are supported"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute_with_options(options)
|
98
|
+
# Rows can be specified using a number of different options:
|
99
|
+
# row ranges (start_row and end_row)
|
100
|
+
options[:row_intervals] ||= []
|
101
|
+
|
102
|
+
if options[:row_keys]
|
103
|
+
options[:row_keys].flatten.each do |rk|
|
104
|
+
row_interval = Hypertable::ThriftGen::RowInterval.new
|
105
|
+
row_interval.start_row = rk
|
106
|
+
row_interval.start_inclusive = true
|
107
|
+
row_interval.end_row = rk
|
108
|
+
row_interval.end_inclusive = true
|
109
|
+
options[:row_intervals] << row_interval
|
110
|
+
end
|
111
|
+
elsif options[:start_row]
|
112
|
+
raise "missing :end_row" if !options[:end_row]
|
113
|
+
|
114
|
+
options[:start_inclusive] = options.has_key?(:start_inclusive) ? options[:start_inclusive] : true
|
115
|
+
options[:end_inclusive] = options.has_key?(:end_inclusive) ? options[:end_inclusive] : true
|
116
|
+
|
117
|
+
row_interval = Hypertable::ThriftGen::RowInterval.new
|
118
|
+
row_interval.start_row = options[:start_row]
|
119
|
+
row_interval.start_inclusive = options[:start_inclusive]
|
120
|
+
row_interval.end_row = options[:end_row]
|
121
|
+
row_interval.end_inclusive = options[:end_inclusive]
|
122
|
+
options[:row_intervals] << row_interval
|
123
|
+
end
|
124
|
+
|
125
|
+
sanitize_conditions(options)
|
126
|
+
|
127
|
+
select_rows = convert_select_columns_to_array_of_columns(options[:select], options[:columns])
|
128
|
+
|
129
|
+
t1 = Time.now
|
130
|
+
table_name = options[:table_name]
|
131
|
+
scan_spec = convert_options_to_scan_spec(options)
|
132
|
+
cells = @connection.get_cells(table_name, scan_spec)
|
133
|
+
@@read_latency += Time.now - t1
|
134
|
+
|
135
|
+
cells
|
136
|
+
end
|
137
|
+
|
138
|
+
def convert_options_to_scan_spec(options={})
|
139
|
+
scan_spec = Hypertable::ThriftGen::ScanSpec.new
|
140
|
+
options[:revs] ||= 1
|
141
|
+
options[:return_deletes] ||= false
|
142
|
+
|
143
|
+
for key in options.keys
|
144
|
+
case key.to_sym
|
145
|
+
when :row_intervals
|
146
|
+
scan_spec.row_intervals = options[key]
|
147
|
+
when :cell_intervals
|
148
|
+
scan_spec.cell_intervals = options[key]
|
149
|
+
when :start_time
|
150
|
+
scan_spec.start_time = options[key]
|
151
|
+
when :end_time
|
152
|
+
scan_spec.end_time = options[key]
|
153
|
+
when :limit
|
154
|
+
scan_spec.row_limit = options[key]
|
155
|
+
when :revs
|
156
|
+
scan_spec.revs = options[key]
|
157
|
+
when :return_deletes
|
158
|
+
scan_spec.return_deletes = options[key]
|
159
|
+
when :table_name, :start_row, :end_row, :start_inclusive, :end_inclusive, :select, :columns, :row_keys, :conditions, :include
|
160
|
+
# ignore
|
161
|
+
else
|
162
|
+
raise "Unrecognized scan spec option: #{key}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
scan_spec
|
167
|
+
end
|
168
|
+
|
169
|
+
def execute(hql, name=nil)
|
170
|
+
log(hql, name) { @connection.hql_query(hql) }
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns array of column objects for table associated with this class.
|
174
|
+
# Hypertable allows columns to include dashes in the name. This doesn't
|
175
|
+
# play well with Ruby (can't have dashes in method names), so we must
|
176
|
+
# maintain a mapping of original column names to Ruby-safe names.
|
177
|
+
def columns(table_name, name = nil)#:nodoc:
|
178
|
+
# Each table always has a row key called 'ROW'
|
179
|
+
columns = [
|
180
|
+
Column.new('ROW', '')
|
181
|
+
]
|
182
|
+
schema = describe_table(table_name)
|
183
|
+
doc = REXML::Document.new(schema)
|
184
|
+
column_families = doc.elements['Schema/AccessGroup[@name="default"]'].elements.to_a
|
185
|
+
|
186
|
+
@hypertable_column_names[table_name] ||= {}
|
187
|
+
for cf in column_families
|
188
|
+
column_name = cf.elements['Name'].text
|
189
|
+
rubified_name = rubify_column_name(column_name)
|
190
|
+
@hypertable_column_names[table_name][rubified_name] = column_name
|
191
|
+
columns << new_column(rubified_name, '')
|
192
|
+
end
|
193
|
+
|
194
|
+
columns
|
195
|
+
end
|
196
|
+
|
197
|
+
def remove_column_from_name_map(table_name, name)
|
198
|
+
@hypertable_column_names[table_name].delete(rubify_column_name(name))
|
199
|
+
end
|
200
|
+
|
201
|
+
def add_column_to_name_map(table_name, name)
|
202
|
+
@hypertable_column_names[table_name][rubify_column_name(name)] = name
|
203
|
+
end
|
204
|
+
|
205
|
+
def add_qualified_column(table_name, column_family, qualifiers=[], default='', sql_type=nil, null=true)
|
206
|
+
qc = QualifiedColumn.new(column_family, default, sql_type, null)
|
207
|
+
qc.qualifiers = qualifiers
|
208
|
+
qualifiers.each{|q| add_column_to_name_map(table_name, qualified_column_name(column_family, q))}
|
209
|
+
qc
|
210
|
+
end
|
211
|
+
|
212
|
+
def new_column(column_name, default_value='')
|
213
|
+
Column.new(rubify_column_name(column_name), default_value)
|
214
|
+
end
|
215
|
+
|
216
|
+
def qualified_column_name(column_family, qualifier=nil)
|
217
|
+
[column_family, qualifier].compact.join(':')
|
218
|
+
end
|
219
|
+
|
220
|
+
def rubify_column_name(column_name)
|
221
|
+
column_name.to_s.gsub(/-+/, '_')
|
222
|
+
end
|
223
|
+
|
224
|
+
def is_qualified_column_name?(column_name)
|
225
|
+
column_family, qualifier = column_name.split(':', 2)
|
226
|
+
if qualifier
|
227
|
+
[true, column_family, qualifier]
|
228
|
+
else
|
229
|
+
[false, nil, nil]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def quote(value, column = nil)
|
234
|
+
case value
|
235
|
+
when NilClass then ''
|
236
|
+
when String then value
|
237
|
+
else super(value, column)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def quote_column_name(name)
|
242
|
+
"'#{name}'"
|
243
|
+
end
|
244
|
+
|
245
|
+
def quote_column_name_for_table(name, table_name)
|
246
|
+
quote_column_name(hypertable_column_name(name, table_name))
|
247
|
+
end
|
248
|
+
|
249
|
+
def hypertable_column_name(name, table_name, declared_columns_only=false)
|
250
|
+
n = @hypertable_column_names[table_name][name]
|
251
|
+
n ||= name if !declared_columns_only
|
252
|
+
n
|
253
|
+
end
|
254
|
+
|
255
|
+
def describe_table(table_name)
|
256
|
+
@connection.get_schema(table_name)
|
257
|
+
end
|
258
|
+
|
259
|
+
def tables(name=nil)
|
260
|
+
@connection.get_tables
|
261
|
+
end
|
262
|
+
|
263
|
+
def drop_table(table_name, options = {})
|
264
|
+
@connection.drop_table(table_name, options[:if_exists] || false)
|
265
|
+
end
|
266
|
+
|
267
|
+
def write_cells(table_name, cells)
|
268
|
+
return if cells.blank?
|
269
|
+
|
270
|
+
@connection.with_mutator(table_name) do |mutator|
|
271
|
+
t1 = Time.now
|
272
|
+
@connection.set_cells(mutator, cells.map{|c| cell_from_array(c)})
|
273
|
+
@@write_latency += Time.now - t1
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Cell passed in as [row_key, column_name, value]
|
278
|
+
def cell_from_array(array)
|
279
|
+
cell = Hypertable::ThriftGen::Cell.new
|
280
|
+
cell.row_key = array[0]
|
281
|
+
column_family, column_qualifier = array[1].split(':')
|
282
|
+
cell.column_family = column_family
|
283
|
+
cell.column_qualifier = column_qualifier if column_qualifier
|
284
|
+
cell.value = array[2] if array[2]
|
285
|
+
cell
|
286
|
+
end
|
287
|
+
|
288
|
+
def delete_cells(table_name, cells)
|
289
|
+
t1 = Time.now
|
290
|
+
|
291
|
+
@connection.with_mutator(table_name) do |mutator|
|
292
|
+
@connection.set_cells(mutator, cells.map{|c|
|
293
|
+
cell = cell_from_array(c)
|
294
|
+
cell.flag = CELL_FLAG_DELETE_CELL
|
295
|
+
cell
|
296
|
+
})
|
297
|
+
end
|
298
|
+
|
299
|
+
@@write_latency += Time.now - t1
|
300
|
+
end
|
301
|
+
|
302
|
+
def delete_rows(table_name, row_keys)
|
303
|
+
t1 = Time.now
|
304
|
+
cells = row_keys.map do |row_key|
|
305
|
+
cell = Hypertable::ThriftGen::Cell.new
|
306
|
+
cell.row_key = row_key
|
307
|
+
cell.flag = CELL_FLAG_DELETE_ROW
|
308
|
+
cell
|
309
|
+
end
|
310
|
+
|
311
|
+
@connection.with_mutator(table_name) do |mutator|
|
312
|
+
@connection.set_cells(mutator, cells)
|
313
|
+
end
|
314
|
+
|
315
|
+
@@write_latency += Time.now - t1
|
316
|
+
end
|
317
|
+
|
318
|
+
def insert_fixture(fixture, table_name)
|
319
|
+
fixture_hash = fixture.to_hash
|
320
|
+
row_key = fixture_hash.delete('ROW')
|
321
|
+
cells = []
|
322
|
+
fixture_hash.keys.each{|k| cells << [row_key, k, fixture_hash[k]]}
|
323
|
+
write_cells(table_name, cells)
|
324
|
+
end
|
325
|
+
|
326
|
+
private
|
327
|
+
|
328
|
+
def select(hql, name=nil)
|
329
|
+
# TODO: need hypertools run_hql to return result set
|
330
|
+
raise "not yet implemented"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
# Like a regular database, each table in Hypertable has a fixed list
|
4
|
+
# of columns. However, Hypertable allows flexible schemas through the
|
5
|
+
# use of column qualifiers. Suppose a table is defined to have a single
|
6
|
+
# column called misc.
|
7
|
+
#
|
8
|
+
# CREATE TABLE pages (
|
9
|
+
# 'misc'
|
10
|
+
# )
|
11
|
+
#
|
12
|
+
# In Hypertable, each traditional database column is referred to as
|
13
|
+
# a column family. Each column family can have a theoretically infinite
|
14
|
+
# number of qualified instances. An instance of a qualified column
|
15
|
+
# is referred to using the column_family:qualifer notation. e.g.,
|
16
|
+
#
|
17
|
+
# misc:red
|
18
|
+
# misc:green
|
19
|
+
# misc:blue
|
20
|
+
#
|
21
|
+
# These qualified column instances do not need to be declared as part
|
22
|
+
# of the table schema. The table schema itself does not provide
|
23
|
+
# an indication of whether a column family has been used with qualifiers.
|
24
|
+
# As a results, we must explicitly declare intent to use a column family
|
25
|
+
# in a qualified manner in our class definition. The resulting AR
|
26
|
+
# object models the column family as a Hash.
|
27
|
+
#
|
28
|
+
# class Page < ActiveRecord::HyperBase
|
29
|
+
# qualified_column :misc
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# p = Page.new
|
33
|
+
# p.ROW = 'page_1'
|
34
|
+
# p.misc['url'] = 'http://www.zvents.com/'
|
35
|
+
# p.misc['hits'] = 127
|
36
|
+
# p.save
|
37
|
+
|
38
|
+
class QualifiedColumn < Column
|
39
|
+
attr_accessor :qualifiers
|
40
|
+
|
41
|
+
def initialize(name, default, sql_type = nil, null = true)
|
42
|
+
@qualifiers ||= []
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def klass
|
47
|
+
Hash
|
48
|
+
end
|
49
|
+
|
50
|
+
def default
|
51
|
+
# Unlike regular AR objects, the default value for a column must
|
52
|
+
# be cloned. This is to avoid copy-by-reference issues with {}
|
53
|
+
# objects. Without clone, all instances of the class will share
|
54
|
+
# a reference to the same object.
|
55
|
+
@default.clone
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../spec_helper.rb')
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
describe HypertableAdapter do
|
6
|
+
before do
|
7
|
+
@h = HypertableAdapter.new(nil, nil, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
describe HypertableAdapter, '.describe_table' do
|
11
|
+
before do
|
12
|
+
@describe_table_text = '<Schema generation="1">\n <AccessGroup name="default">\n <ColumnFamily id="1">\n <Name>message</Name> </ColumnFamily>\n <ColumnFamily id="2">\n <Name>date-time</Name>\n </ColumnFamily>\n </AccessGroup>\n </Schema>\n'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return a string describing a table" do
|
16
|
+
@h.should_receive(:describe_table).with('name').and_return(@describe_table_text)
|
17
|
+
@h.describe_table('name').should == @describe_table_text
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe HypertableAdapter, '.column' do
|
22
|
+
before do
|
23
|
+
@describe_table_text = '<Schema generation="1">\n <AccessGroup name="default">\n <ColumnFamily id="1">\n <Name>message</Name> </ColumnFamily>\n <ColumnFamily id="2">\n <Name>date-time</Name>\n </ColumnFamily>\n </AccessGroup>\n </Schema>\n'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return an array of columns representing the table schema" do
|
27
|
+
@h.stub!(:describe_table).with('name').and_return(@describe_table_text)
|
28
|
+
columns = @h.columns('name')
|
29
|
+
columns.should be_is_a(Array)
|
30
|
+
columns.should have_exactly(3).columns
|
31
|
+
# The first column within a Hypertable is always the row key.
|
32
|
+
columns[0].name.should == "ROW"
|
33
|
+
columns[1].name.should == "message"
|
34
|
+
# notice that the original column name "date-time" is converted
|
35
|
+
# to a Ruby-friendly column name "date_time"
|
36
|
+
columns[2].name.should == "date_time"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should set up the name mappings between ruby and hypertable" do
|
40
|
+
@h.stub!(:describe_table).with('name').and_return(@describe_table_text)
|
41
|
+
columns = @h.columns('name')
|
42
|
+
@h.hypertable_column_name('date_time', 'name').should == 'date-time'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe HypertableAdapter, '.quote_column_name' do
|
47
|
+
it "should surround column name in single quotes" do
|
48
|
+
@h.quote_column_name("date_time").should == "'date_time'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe HypertableAdapter, '.rubify_column_name' do
|
53
|
+
it "should change dashes to underscores in column names" do
|
54
|
+
@h.rubify_column_name("date-time").should == "date_time"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe HypertableAdapter, '.tables' do
|
59
|
+
before do
|
60
|
+
@tables = ["table1", "table2"]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should return an array of table names" do
|
64
|
+
@h.should_receive(:tables).and_return(@tables)
|
65
|
+
@h.tables.should == @tables
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe HypertableAdapter, '.quote' do
|
70
|
+
it "should return empty string for nil values" do
|
71
|
+
@h.quote(nil).should == ''
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe HypertableAdapter, '.quote' do
|
76
|
+
it "should return a quoted string for all non-nil values" do
|
77
|
+
@h.quote(1).should == "1"
|
78
|
+
@h.quote('happy').should == "happy"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe HypertableAdapter, '.is_qualified_column_name?' do
|
83
|
+
it "should return false for regular columns" do
|
84
|
+
status, family, qualifier = @h.is_qualified_column_name?("col1")
|
85
|
+
status.should be_false
|
86
|
+
family.should be_nil
|
87
|
+
qualifier.should be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should return true for qualified columns" do
|
91
|
+
status, family, qualifier = @h.is_qualified_column_name?("col1:red")
|
92
|
+
status.should be_true
|
93
|
+
family.should == 'col1'
|
94
|
+
qualifier.should == 'red'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe HypertableAdapter, '.convert_select_columns_to_array_of_columns(' do
|
99
|
+
it "should accept an array as input" do
|
100
|
+
@h.convert_select_columns_to_array_of_columns(["one", "two", "three"]).should == ["one", "two", "three"]
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should accept a string as input and split the results on commas" do
|
104
|
+
@h.convert_select_columns_to_array_of_columns("one,two,three").should == ["one", "two", "three"]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should strip whitespace from column names" do
|
108
|
+
@h.convert_select_columns_to_array_of_columns(" one,two , three ").should == ["one", "two", "three"]
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should return [] for a request on * columns" do
|
112
|
+
@h.convert_select_columns_to_array_of_columns("*").should == []
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "../../../../config/environment"))
|
3
|
+
require 'spec'
|
4
|
+
require 'spec/rails'
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
# If you're not using ActiveRecord you should remove these
|
8
|
+
# lines, delete config/database.yml and disable :active_record
|
9
|
+
# in your config/boot.rb
|
10
|
+
config.use_transactional_fixtures = true
|
11
|
+
config.use_instantiated_fixtures = false
|
12
|
+
config.fixture_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
|
13
|
+
|
14
|
+
# == Fixtures
|
15
|
+
#
|
16
|
+
# You can declare fixtures for each example_group like this:
|
17
|
+
# describe "...." do
|
18
|
+
# fixtures :table_a, :table_b
|
19
|
+
#
|
20
|
+
# Alternatively, if you prefer to declare them only once, you can
|
21
|
+
# do so right here. Just uncomment the next line and replace the fixture
|
22
|
+
# names with your fixtures.
|
23
|
+
#
|
24
|
+
config.global_fixtures = []
|
25
|
+
|
26
|
+
#
|
27
|
+
# If you declare global fixtures, be aware that they will be declared
|
28
|
+
# for all of your examples, even those that don't use them.
|
29
|
+
#
|
30
|
+
# == Mock Framework
|
31
|
+
#
|
32
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
33
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
34
|
+
#
|
35
|
+
# config.mock_with :mocha
|
36
|
+
# config.mock_with :flexmock
|
37
|
+
# config.mock_with :rr
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tylerkovacs-hypertable_adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- tylerkovacs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-01 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Hypertable Adapter allows ActiveRecord to communicate with Hypertable.
|
17
|
+
email: tyler.kovacs@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/active_record
|
27
|
+
- lib/active_record/connection_adapters
|
28
|
+
- lib/active_record/connection_adapters/hypertable_adapter.rb
|
29
|
+
- lib/active_record/connection_adapters/qualified_column.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
- spec/lib
|
32
|
+
- spec/lib/hypertable_adapter_spec.rb
|
33
|
+
has_rdoc: true
|
34
|
+
homepage: http://github.com/tylerkovacs/hypertable_adapter
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- --inline-source
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.2.0
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: See README
|
60
|
+
test_files: []
|
61
|
+
|