You are viewing a plain text version of this content. The canonical link for it is here.
Posted to olio-commits@incubator.apache.org by ws...@apache.org on 2009/02/11 20:52:44 UTC
svn commit: r743503 - in /incubator/olio/webapp/rails/branches/hypertable:
./ vendor/plugins/hypertable_adapter/ vendor/plugins/hypertable_adapter/lib/
vendor/plugins/hypertable_adapter/lib/active_record/
vendor/plugins/hypertable_adapter/lib/active_re...
Author: wsobel
Date: Wed Feb 11 20:52:44 2009
New Revision: 743503
URL: http://svn.apache.org/viewvc?rev=743503&view=rev
Log:
Added hypertable branch
Added:
incubator/olio/webapp/rails/branches/hypertable/
- copied from r743484, incubator/olio/webapp/rails/trunk/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/LICENSE
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/README
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/Rakefile
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/TODO
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/VERSION.yml
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/hypertable_adapter.gemspec
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/hypertable_adapter.rb
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/qualified_column.rb
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/hypertable_adapter_spec.rb
incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/spec_helper.rb
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/LICENSE
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/LICENSE?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/LICENSE (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/LICENSE Wed Feb 11 20:52:44 2009
@@ -0,0 +1,20 @@
+Copyright (c) 2008 tylerkovacs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/README
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/README?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/README (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/README Wed Feb 11 20:52:44 2009
@@ -0,0 +1,49 @@
+hypertable_adapter
+==================
+
+Hypertable Adapter allows ActiveRecord to communicate with Hypertable.
+
+In ActiveRecord, each supported data store has an adapter that implements
+functionality specific to that store as well as providing metadata for
+data held within the store. Features implemented by adapters typically
+include:
+
+ * connection handling
+ * list of tables
+ * list of columns per data
+ * low-level support for qualified columns
+ * statement execution (selects, writes, etc.)
+ * latency measurement
+ * fixture handling
+
+The adapter provides low-level integration between Hypertable and
+ActiveRecord. A separate library called HyperRecord is required to fully
+integrate the two.
+
+Basic Hypertable Adapter Configuration
+======================================
+
+Before using the adapter, you must declare a hypertable connection in
+config/database.yml. The adapter communicates with Hypertable using a
+Thrift Broker (see http://hypertable.org/documentation.html for details)
+which is part of the main Hypertable installation. The connection record
+in config/database.yml must identify the host name and the port used by
+the Thrift Broker:
+
+ hypertable:
+ adapter: hypertable
+ host: localhost
+ port: 38080
+
+Or, if you need to do it in code outside of database.yml:
+
+ ActiveRecord::Base.configurations['hypertable'] = {
+ 'adapter' => 'hypertable',
+ 'host' => 'localhost',
+ 'port' => '38080'
+ }
+
+COPYRIGHT
+=========
+
+Copyright (c) 2008 tylerkovacs. See LICENSE for details.
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/Rakefile
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/Rakefile?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/Rakefile (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/Rakefile Wed Feb 11 20:52:44 2009
@@ -0,0 +1,40 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rcov/rcovtask'
+
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |s|
+ s.name = "hypertable_adapter"
+ s.summary = %Q{TODO}
+ s.email = "tyler.kovacs@gmail.com"
+ s.homepage = "http://github.com/tylerkovacs/hypertable_adapter"
+ s.description = "TODO"
+ s.authors = ["tylerkovacs"]
+ end
+rescue LoadError
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
+end
+
+Rake::TestTask.new do |t|
+ t.libs << 'lib'
+ t.pattern = 'spec/**/*_spec.rb'
+ t.verbose = false
+end
+
+Rake::RDocTask.new do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'hypertable_adapter'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+Rcov::RcovTask.new do |t|
+ t.libs << 'test'
+ t.test_files = FileList['test/**/*_test.rb']
+ t.verbose = true
+end
+
+task :default => :rcov
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/TODO
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/TODO?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/TODO (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/TODO Wed Feb 11 20:52:44 2009
@@ -0,0 +1,10 @@
+- test gem builds, remove plugins from zvents rails tree and freeze gems
+- ability to specific timeout in config/database.yml
+- documentation
+ - rewrite README for new config
+ - how to support hypertable fixtures in tests/specs in regular rails tests
+ - add INSTALL file
+ - :row_keys, :start_inclusive, :end_inclusive finder options
+ - Hash conditions not supported (explain why)
+ - update internal docs re: can't specify select list
+ - futures (efficient lookup outside of row key)
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/VERSION.yml
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/VERSION.yml?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/VERSION.yml (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/VERSION.yml Wed Feb 11 20:52:44 2009
@@ -0,0 +1,4 @@
+---
+:major: 0
+:minor: 1
+:patch: 0
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/hypertable_adapter.gemspec
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/hypertable_adapter.gemspec?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/hypertable_adapter.gemspec (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/hypertable_adapter.gemspec Wed Feb 11 20:52:44 2009
@@ -0,0 +1,29 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{hypertable_adapter}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["tylerkovacs"]
+ s.date = %q{2009-02-01}
+ s.description = %q{Hypertable Adapter allows ActiveRecord to communicate with Hypertable.}
+ s.email = %q{tyler.kovacs@gmail.com}
+ s.files = ["VERSION.yml", "lib/active_record", "lib/active_record/connection_adapters", "lib/active_record/connection_adapters/hypertable_adapter.rb", "lib/active_record/connection_adapters/qualified_column.rb", "spec/spec_helper.rb", "spec/lib", "spec/lib/hypertable_adapter_spec.rb"]
+ s.has_rdoc = true
+ s.homepage = %q{http://github.com/tylerkovacs/hypertable_adapter}
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.1}
+ s.summary = %q{See README}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 2
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ else
+ end
+ else
+ end
+end
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/hypertable_adapter.rb
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/hypertable_adapter.rb?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/hypertable_adapter.rb (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/hypertable_adapter.rb Wed Feb 11 20:52:44 2009
@@ -0,0 +1,334 @@
+require 'active_record/connection_adapters/abstract_adapter'
+require 'active_record/connection_adapters/qualified_column'
+
+module ActiveRecord
+ class Base
+ def self.require_hypertable_thrift_client
+ # Include the hypertools driver if one hasn't already been loaded
+ unless defined? Hypertable::ThriftClient
+ gem 'hypertable-thrift-client'
+ require_dependency 'thrift_client'
+ end
+ end
+
+ def self.hypertable_connection(config)
+ config = config.symbolize_keys
+ require_hypertable_thrift_client
+
+ raise "Hypertable/ThriftBroker config missing :host" if !config[:host]
+ connection = Hypertable::ThriftClient.new(config[:host], config[:port])
+
+ ConnectionAdapters::HypertableAdapter.new(connection, logger, config)
+ end
+ end
+
+ module ConnectionAdapters
+ class HypertableAdapter < AbstractAdapter
+ @@read_latency = 0.0
+ @@write_latency = 0.0
+ cattr_accessor :read_latency, :write_latency
+
+ CELL_FLAG_DELETE_ROW = 0
+ CELL_FLAG_DELETE_COLUMN_FAMILY = 1
+ CELL_FLAG_DELETE_CELL = 2
+ CELL_FLAG_INSERT = 255
+
+ def initialize(connection, logger, config)
+ super(connection, logger)
+ @config = config
+ @hypertable_column_names = {}
+ end
+
+ def self.reset_timing
+ @@read_latency = 0.0
+ @@write_latency = 0.0
+ end
+
+ def self.get_timing
+ [@@read_latency, @@write_latency]
+ end
+
+ def convert_select_columns_to_array_of_columns(s, columns=nil)
+ select_rows = s.class == String ? s.split(',').map{|s| s.strip} : s
+ select_rows = select_rows.reject{|s| s == '*'}
+
+ if select_rows.empty? and !columns.blank?
+ for c in columns
+ next if c.name == 'ROW' # skip over the ROW key, always included
+ if c.is_a?(QualifiedColumn)
+ for q in c.qualifiers
+ select_rows << qualified_column_name(c.name, q.to_s)
+ end
+ else
+ select_rows << c.name
+ end
+ end
+ end
+
+ select_rows
+ end
+
+ def adapter_name
+ 'Hypertable'
+ end
+
+ def supports_migrations?
+ true
+ end
+
+ def native_database_types
+ {
+ :string => { :name => "varchar", :limit => 255 }
+ }
+ end
+
+ def sanitize_conditions(options)
+ case options[:conditions]
+ when Hash
+ # requires Hypertable API to support query by arbitrary cell value
+ raise "HyperRecord does not support specifying conditions by Hash"
+ when NilClass
+ # do nothing
+ else
+ raise "Only hash conditions are supported"
+ end
+ end
+
+ def execute_with_options(options)
+ # Rows can be specified using a number of different options:
+ # row ranges (start_row and end_row)
+ options[:row_intervals] ||= []
+
+ if options[:row_keys]
+ options[:row_keys].flatten.each do |rk|
+ row_interval = Hypertable::ThriftGen::RowInterval.new
+ row_interval.start_row = rk
+ row_interval.start_inclusive = true
+ row_interval.end_row = rk
+ row_interval.end_inclusive = true
+ options[:row_intervals] << row_interval
+ end
+ elsif options[:start_row]
+ raise "missing :end_row" if !options[:end_row]
+
+ options[:start_inclusive] = options.has_key?(:start_inclusive) ? options[:start_inclusive] : true
+ options[:end_inclusive] = options.has_key?(:end_inclusive) ? options[:end_inclusive] : true
+
+ row_interval = Hypertable::ThriftGen::RowInterval.new
+ row_interval.start_row = options[:start_row]
+ row_interval.start_inclusive = options[:start_inclusive]
+ row_interval.end_row = options[:end_row]
+ row_interval.end_inclusive = options[:end_inclusive]
+ options[:row_intervals] << row_interval
+ end
+
+ sanitize_conditions(options)
+
+ select_rows = convert_select_columns_to_array_of_columns(options[:select], options[:columns])
+
+ t1 = Time.now
+ table_name = options[:table_name]
+ scan_spec = convert_options_to_scan_spec(options)
+ cells = @connection.get_cells(table_name, scan_spec)
+ @@read_latency += Time.now - t1
+
+ cells
+ end
+
+ def convert_options_to_scan_spec(options={})
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
+ options[:revs] ||= 1
+ options[:return_deletes] ||= false
+
+ for key in options.keys
+ case key.to_sym
+ when :row_intervals
+ scan_spec.row_intervals = options[key]
+ when :cell_intervals
+ scan_spec.cell_intervals = options[key]
+ when :start_time
+ scan_spec.start_time = options[key]
+ when :end_time
+ scan_spec.end_time = options[key]
+ when :limit
+ scan_spec.row_limit = options[key]
+ when :revs
+ scan_spec.revs = options[key]
+ when :return_deletes
+ scan_spec.return_deletes = options[key]
+ when :table_name, :start_row, :end_row, :start_inclusive, :end_inclusive, :select, :columns, :row_keys, :conditions, :include
+ # ignore
+ else
+ raise "Unrecognized scan spec option: #{key}"
+ end
+ end
+
+ scan_spec
+ end
+
+ def execute(hql, name=nil)
+ log(hql, name) { @connection.hql_query(hql) }
+ end
+
+ # Returns array of column objects for table associated with this class.
+ # Hypertable allows columns to include dashes in the name. This doesn't
+ # play well with Ruby (can't have dashes in method names), so we must
+ # maintain a mapping of original column names to Ruby-safe names.
+ def columns(table_name, name = nil)#:nodoc:
+ # Each table always has a row key called 'ROW'
+ columns = [
+ Column.new('ROW', '')
+ ]
+ schema = describe_table(table_name)
+ doc = REXML::Document.new(schema)
+ column_families = doc.elements['Schema/AccessGroup[@name="default"]'].elements.to_a
+
+ @hypertable_column_names[table_name] ||= {}
+ for cf in column_families
+ column_name = cf.elements['Name'].text
+ rubified_name = rubify_column_name(column_name)
+ @hypertable_column_names[table_name][rubified_name] = column_name
+ columns << new_column(rubified_name, '')
+ end
+
+ columns
+ end
+
+ def remove_column_from_name_map(table_name, name)
+ @hypertable_column_names[table_name].delete(rubify_column_name(name))
+ end
+
+ def add_column_to_name_map(table_name, name)
+ @hypertable_column_names[table_name][rubify_column_name(name)] = name
+ end
+
+ def add_qualified_column(table_name, column_family, qualifiers=[], default='', sql_type=nil, null=true)
+ qc = QualifiedColumn.new(column_family, default, sql_type, null)
+ qc.qualifiers = qualifiers
+ qualifiers.each{|q| add_column_to_name_map(table_name, qualified_column_name(column_family, q))}
+ qc
+ end
+
+ def new_column(column_name, default_value='')
+ Column.new(rubify_column_name(column_name), default_value)
+ end
+
+ def qualified_column_name(column_family, qualifier=nil)
+ [column_family, qualifier].compact.join(':')
+ end
+
+ def rubify_column_name(column_name)
+ column_name.to_s.gsub(/-+/, '_')
+ end
+
+ def is_qualified_column_name?(column_name)
+ column_family, qualifier = column_name.split(':', 2)
+ if qualifier
+ [true, column_family, qualifier]
+ else
+ [false, nil, nil]
+ end
+ end
+
+ def quote(value, column = nil)
+ case value
+ when NilClass then ''
+ when String then value
+ else super(value, column)
+ end
+ end
+
+ def quote_column_name(name)
+ "'#{name}'"
+ end
+
+ def quote_column_name_for_table(name, table_name)
+ quote_column_name(hypertable_column_name(name, table_name))
+ end
+
+ def hypertable_column_name(name, table_name, declared_columns_only=false)
+ n = @hypertable_column_names[table_name][name]
+ n ||= name if !declared_columns_only
+ n
+ end
+
+ def describe_table(table_name)
+ @connection.get_schema(table_name)
+ end
+
+ def tables(name=nil)
+ @connection.get_tables
+ end
+
+ def drop_table(table_name, options = {})
+ @connection.drop_table(table_name, options[:if_exists] || false)
+ end
+
+ def write_cells(table_name, cells)
+ return if cells.blank?
+
+ @connection.with_mutator(table_name) do |mutator|
+ t1 = Time.now
+ @connection.set_cells(mutator, cells.map{|c| cell_from_array(c)})
+ @@write_latency += Time.now - t1
+ end
+ end
+
+ # Cell passed in as [row_key, column_name, value]
+ def cell_from_array(array)
+ cell = Hypertable::ThriftGen::Cell.new
+ cell.row_key = array[0]
+ column_family, column_qualifier = array[1].split(':')
+ cell.column_family = column_family
+ cell.column_qualifier = column_qualifier if column_qualifier
+ cell.value = array[2] if array[2]
+ cell
+ end
+
+ def delete_cells(table_name, cells)
+ t1 = Time.now
+
+ @connection.with_mutator(table_name) do |mutator|
+ @connection.set_cells(mutator, cells.map{|c|
+ cell = cell_from_array(c)
+ cell.flag = CELL_FLAG_DELETE_CELL
+ cell
+ })
+ end
+
+ @@write_latency += Time.now - t1
+ end
+
+ def delete_rows(table_name, row_keys)
+ t1 = Time.now
+ cells = row_keys.map do |row_key|
+ cell = Hypertable::ThriftGen::Cell.new
+ cell.row_key = row_key
+ cell.flag = CELL_FLAG_DELETE_ROW
+ cell
+ end
+
+ @connection.with_mutator(table_name) do |mutator|
+ @connection.set_cells(mutator, cells)
+ end
+
+ @@write_latency += Time.now - t1
+ end
+
+ def insert_fixture(fixture, table_name)
+ fixture_hash = fixture.to_hash
+ row_key = fixture_hash.delete('ROW')
+ cells = []
+ fixture_hash.keys.each{|k| cells << [row_key, k, fixture_hash[k]]}
+ write_cells(table_name, cells)
+ end
+
+ private
+
+ def select(hql, name=nil)
+ # TODO: need hypertools run_hql to return result set
+ raise "not yet implemented"
+ end
+ end
+ end
+end
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/qualified_column.rb
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/qualified_column.rb?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/qualified_column.rb (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/lib/active_record/connection_adapters/qualified_column.rb Wed Feb 11 20:52:44 2009
@@ -0,0 +1,59 @@
+module ActiveRecord
+ module ConnectionAdapters
+ # Like a regular database, each table in Hypertable has a fixed list
+ # of columns. However, Hypertable allows flexible schemas through the
+ # use of column qualifiers. Suppose a table is defined to have a single
+ # column called misc.
+ #
+ # CREATE TABLE pages (
+ # 'misc'
+ # )
+ #
+ # In Hypertable, each traditional database column is referred to as
+ # a column family. Each column family can have a theoretically infinite
+ # number of qualified instances. An instance of a qualified column
+ # is referred to using the column_family:qualifer notation. e.g.,
+ #
+ # misc:red
+ # misc:green
+ # misc:blue
+ #
+ # These qualified column instances do not need to be declared as part
+ # of the table schema. The table schema itself does not provide
+ # an indication of whether a column family has been used with qualifiers.
+ # As a results, we must explicitly declare intent to use a column family
+ # in a qualified manner in our class definition. The resulting AR
+ # object models the column family as a Hash.
+ #
+ # class Page < ActiveRecord::HyperBase
+ # qualified_column :misc
+ # end
+ #
+ # p = Page.new
+ # p.ROW = 'page_1'
+ # p.misc['url'] = 'http://www.zvents.com/'
+ # p.misc['hits'] = 127
+ # p.save
+
+ class QualifiedColumn < Column
+ attr_accessor :qualifiers
+
+ def initialize(name, default, sql_type = nil, null = true)
+ @qualifiers ||= []
+ super
+ end
+
+ def klass
+ Hash
+ end
+
+ def default
+ # Unlike regular AR objects, the default value for a column must
+ # be cloned. This is to avoid copy-by-reference issues with {}
+ # objects. Without clone, all instances of the class will share
+ # a reference to the same object.
+ @default.clone
+ end
+ end
+ end
+end
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/hypertable_adapter_spec.rb
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/hypertable_adapter_spec.rb?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/hypertable_adapter_spec.rb (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/lib/hypertable_adapter_spec.rb Wed Feb 11 20:52:44 2009
@@ -0,0 +1,117 @@
+require File.join(File.dirname(__FILE__), '../spec_helper.rb')
+
+module ActiveRecord
+ module ConnectionAdapters
+ describe HypertableAdapter do
+ before do
+ @h = HypertableAdapter.new(nil, nil, {})
+ end
+
+ describe HypertableAdapter, '.describe_table' do
+ before do
+ @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'
+ end
+
+ it "should return a string describing a table" do
+ @h.should_receive(:describe_table).with('name').and_return(@describe_table_text)
+ @h.describe_table('name').should == @describe_table_text
+ end
+ end
+
+ describe HypertableAdapter, '.column' do
+ before do
+ @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'
+ end
+
+ it "should return an array of columns representing the table schema" do
+ @h.stub!(:describe_table).with('name').and_return(@describe_table_text)
+ columns = @h.columns('name')
+ columns.should be_is_a(Array)
+ columns.should have_exactly(3).columns
+ # The first column within a Hypertable is always the row key.
+ columns[0].name.should == "ROW"
+ columns[1].name.should == "message"
+ # notice that the original column name "date-time" is converted
+ # to a Ruby-friendly column name "date_time"
+ columns[2].name.should == "date_time"
+ end
+
+ it "should set up the name mappings between ruby and hypertable" do
+ @h.stub!(:describe_table).with('name').and_return(@describe_table_text)
+ columns = @h.columns('name')
+ @h.hypertable_column_name('date_time', 'name').should == 'date-time'
+ end
+ end
+
+ describe HypertableAdapter, '.quote_column_name' do
+ it "should surround column name in single quotes" do
+ @h.quote_column_name("date_time").should == "'date_time'"
+ end
+ end
+
+ describe HypertableAdapter, '.rubify_column_name' do
+ it "should change dashes to underscores in column names" do
+ @h.rubify_column_name("date-time").should == "date_time"
+ end
+ end
+
+ describe HypertableAdapter, '.tables' do
+ before do
+ @tables = ["table1", "table2"]
+ end
+
+ it "should return an array of table names" do
+ @h.should_receive(:tables).and_return(@tables)
+ @h.tables.should == @tables
+ end
+ end
+
+ describe HypertableAdapter, '.quote' do
+ it "should return empty string for nil values" do
+ @h.quote(nil).should == ''
+ end
+ end
+
+ describe HypertableAdapter, '.quote' do
+ it "should return a quoted string for all non-nil values" do
+ @h.quote(1).should == "1"
+ @h.quote('happy').should == "happy"
+ end
+ end
+
+ describe HypertableAdapter, '.is_qualified_column_name?' do
+ it "should return false for regular columns" do
+ status, family, qualifier = @h.is_qualified_column_name?("col1")
+ status.should be_false
+ family.should be_nil
+ qualifier.should be_nil
+ end
+
+ it "should return true for qualified columns" do
+ status, family, qualifier = @h.is_qualified_column_name?("col1:red")
+ status.should be_true
+ family.should == 'col1'
+ qualifier.should == 'red'
+ end
+ end
+
+ describe HypertableAdapter, '.convert_select_columns_to_array_of_columns(' do
+ it "should accept an array as input" do
+ @h.convert_select_columns_to_array_of_columns(["one", "two", "three"]).should == ["one", "two", "three"]
+ end
+
+ it "should accept a string as input and split the results on commas" do
+ @h.convert_select_columns_to_array_of_columns("one,two,three").should == ["one", "two", "three"]
+ end
+
+ it "should strip whitespace from column names" do
+ @h.convert_select_columns_to_array_of_columns(" one,two , three ").should == ["one", "two", "three"]
+ end
+
+ it "should return [] for a request on * columns" do
+ @h.convert_select_columns_to_array_of_columns("*").should == []
+ end
+ end
+ end
+ end
+end
Added: incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/spec_helper.rb
URL: http://svn.apache.org/viewvc/incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/spec_helper.rb?rev=743503&view=auto
==============================================================================
--- incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/spec_helper.rb (added)
+++ incubator/olio/webapp/rails/branches/hypertable/vendor/plugins/hypertable_adapter/spec/spec_helper.rb Wed Feb 11 20:52:44 2009
@@ -0,0 +1,38 @@
+ENV["RAILS_ENV"] = "test"
+require File.expand_path(File.join(File.dirname(__FILE__), "../../../../config/environment"))
+require 'spec'
+require 'spec/rails'
+
+Spec::Runner.configure do |config|
+ # If you're not using ActiveRecord you should remove these
+ # lines, delete config/database.yml and disable :active_record
+ # in your config/boot.rb
+ config.use_transactional_fixtures = true
+ config.use_instantiated_fixtures = false
+ config.fixture_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
+
+ # == Fixtures
+ #
+ # You can declare fixtures for each example_group like this:
+ # describe "...." do
+ # fixtures :table_a, :table_b
+ #
+ # Alternatively, if you prefer to declare them only once, you can
+ # do so right here. Just uncomment the next line and replace the fixture
+ # names with your fixtures.
+ #
+ config.global_fixtures = []
+
+ #
+ # If you declare global fixtures, be aware that they will be declared
+ # for all of your examples, even those that don't use them.
+ #
+ # == Mock Framework
+ #
+ # RSpec uses it's own mocking framework by default. If you prefer to
+ # use mocha, flexmock or RR, uncomment the appropriate line:
+ #
+ # config.mock_with :mocha
+ # config.mock_with :flexmock
+ # config.mock_with :rr
+end