tylerkovacs-hypertable_adapter 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.
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
|
+
|