From 7635ffc33401672eb853e15edcd774e8d97d497d Mon Sep 17 00:00:00 2001 From: Robert Crim Date: Thu, 1 Aug 2013 09:47:16 +0100 Subject: [PATCH 1/6] Replace cassandra-cql with cql-rb. Use binary protocol. --- Gemfile | 2 +- lib/virsandra.rb | 3 ++- lib/virsandra/configuration.rb | 4 ---- lib/virsandra/connection.rb | 10 ++++++---- lib/virsandra/query.rb | 2 +- spec/lib/virsandra/connection_spec.rb | 2 +- spec/lib/virsandra/query_spec.rb | 2 +- spec/lib/virsandra_spec.rb | 6 +----- spec/spec_helper.rb | 2 +- virsandra.gemspec | 1 + 10 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Gemfile b/Gemfile index 227e77b..cf7cce0 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,6 @@ source 'https://rubygems.org' # Specify your gem's dependencies in virsandra.gemspec gem 'coveralls', require: false -gem "cassandra-cql", :git => "git://github.com/ottbot/cassandra-cql", :branch => "1.2-set-consistency" + gemspec diff --git a/lib/virsandra.rb b/lib/virsandra.rb index f46baf9..5a1f4c1 100644 --- a/lib/virsandra.rb +++ b/lib/virsandra.rb @@ -1,5 +1,6 @@ require "virtus" -require "cassandra-cql/1.2" +require "cql" +require "simple_uuid" require "virsandra/version" require "virsandra/configuration" diff --git a/lib/virsandra/configuration.rb b/lib/virsandra/configuration.rb index 11c2eac..fa8c5c8 100644 --- a/lib/virsandra/configuration.rb +++ b/lib/virsandra/configuration.rb @@ -6,9 +6,7 @@ module Configuration OPTIONS = [ :keyspace, :servers, - :thrift_options, :consistency, - :cql_version ] attr_accessor *OPTIONS @@ -19,9 +17,7 @@ def self.extended(base) def reset! self.servers = '127.0.0.1:9160' - self.cql_version = '3.0.0' self.consistency = :quorum - self.thrift_options = {retries: 5, connect_timeout: 10, timeout: 10} self.keyspace = nil end diff --git a/lib/virsandra/connection.rb b/lib/virsandra/connection.rb index a6e5066..41ebe2c 100644 --- a/lib/virsandra/connection.rb +++ b/lib/virsandra/connection.rb @@ -13,11 +13,13 @@ def initialize(options) end def connect! - cql_options = @options.select {|k| [:keyspace, :cql_version, :consistency].include?(k) } + @handle = Cql::Client.connect(hosts: @options[:servers]) + @handle.use(@options[:keyspace]) + @handle + end - @handle = CassandraCQL::Database.new(@options[:servers], - cql_options, - @options[:thrift_options]) + def disconnect! + @handle.close end # Delegate to CassandraCQL::Database handle diff --git a/lib/virsandra/query.rb b/lib/virsandra/query.rb index 8363961..9c4b441 100644 --- a/lib/virsandra/query.rb +++ b/lib/virsandra/query.rb @@ -72,7 +72,7 @@ def raw_query end def fetch_with_symbolized_keys - row_hash = @row && @row.fetch_hash + row_hash = @row && @row.first return {} unless row_hash Hash[row_hash.map{|(k,v)| [k.to_sym,v]}] diff --git a/spec/lib/virsandra/connection_spec.rb b/spec/lib/virsandra/connection_spec.rb index 4fbcf5e..513dcb1 100644 --- a/spec/lib/virsandra/connection_spec.rb +++ b/spec/lib/virsandra/connection_spec.rb @@ -11,7 +11,7 @@ end it "obtains a db connection" do - connection.handle.should be_a CassandraCQL::Database + connection.handle.should be_a Cql::Client::SynchronousClient end it "delegates to the cassandra handle" do diff --git a/spec/lib/virsandra/query_spec.rb b/spec/lib/virsandra/query_spec.rb index 1fcd51f..79d803e 100644 --- a/spec/lib/virsandra/query_spec.rb +++ b/spec/lib/virsandra/query_spec.rb @@ -9,7 +9,7 @@ end it "should return empty hash when can't fetch hash from results" do - Virsandra.stub(:execute => double("row", :fetch_hash => nil)) + Virsandra.stub(:execute => double("row", :first => nil)) described_class.new.fetch.should eq({}) end diff --git a/spec/lib/virsandra_spec.rb b/spec/lib/virsandra_spec.rb index bc5721a..e8808b6 100644 --- a/spec/lib/virsandra_spec.rb +++ b/spec/lib/virsandra_spec.rb @@ -43,9 +43,7 @@ defaults = { servers: '127.0.0.1:9160', - cql_version: '3.0.0', consistency: :quorum, - thrift_options: {retries: 5, connect_timeout: 10, timeout: 10}, keyspace: nil } @@ -65,12 +63,10 @@ end it "reconnects if dirty" do - CassandraCQL::Database.stub(:new) - Virsandra::Connection.should_receive(:new).twice.and_call_original Virsandra.connection - Virsandra.keyspace = 'foo' + Virsandra.keyspace = 'system' Virsandra.connection Virsandra.connection end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ea83eb4..3ec9c16 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -42,7 +42,7 @@ def build_up begin create_keyspace create_companies_table - rescue CassandraCQL::Error::InvalidRequestException + rescue Cql::QueryError drop_keyspace if defined?(retried) diff --git a/virsandra.gemspec b/virsandra.gemspec index 7832f45..79a4d72 100644 --- a/virsandra.gemspec +++ b/virsandra.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |gem| #gem.add_dependency "cassandra-cql", #WHEN IT SUPPORTS CQL3 gem.add_dependency "virtus", ">= 0.5.4" + gem.add_dependency "cql-rb", ">= 1.0.2" gem.add_dependency "simple_uuid", ">= 0.3.0" gem.add_development_dependency "rake", ">= 0.9.2" From 9b113c4736981c6db69201cd21c138bf7ec81340 Mon Sep 17 00:00:00 2001 From: Robert Crim Date: Thu, 1 Aug 2013 16:11:40 +0100 Subject: [PATCH 2/6] Change default server string to connect to port 9042 --- lib/virsandra/configuration.rb | 2 +- spec/lib/virsandra_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/virsandra/configuration.rb b/lib/virsandra/configuration.rb index fa8c5c8..e8ce49d 100644 --- a/lib/virsandra/configuration.rb +++ b/lib/virsandra/configuration.rb @@ -16,7 +16,7 @@ def self.extended(base) end def reset! - self.servers = '127.0.0.1:9160' + self.servers = '127.0.0.1:9042' self.consistency = :quorum self.keyspace = nil end diff --git a/spec/lib/virsandra_spec.rb b/spec/lib/virsandra_spec.rb index e8808b6..c4527b7 100644 --- a/spec/lib/virsandra_spec.rb +++ b/spec/lib/virsandra_spec.rb @@ -42,7 +42,7 @@ Virsandra.reset! defaults = { - servers: '127.0.0.1:9160', + servers: '127.0.0.1:9042', consistency: :quorum, keyspace: nil } From ef20e1e5d7bdef1e3af186b42d09ed5e8eca46a1 Mon Sep 17 00:00:00 2001 From: Arturs Meisters Date: Thu, 1 Aug 2013 16:46:00 +0100 Subject: [PATCH 3/6] configuration and connection changed to make it more thread safe --- lib/virsandra.rb | 56 +++++++----- lib/virsandra/configuration.rb | 55 ++++++++---- lib/virsandra/connection.rb | 17 ++-- lib/virsandra/version.rb | 2 +- spec/lib/virsandra/connection_spec.rb | 38 +++++++-- spec/lib/virsandra/model_spec.rb | 4 +- spec/lib/virsandra_spec.rb | 118 ++++++++++++++------------ spec/spec_helper.rb | 60 +------------ 8 files changed, 182 insertions(+), 168 deletions(-) diff --git a/lib/virsandra.rb b/lib/virsandra.rb index 5a1f4c1..da09b01 100644 --- a/lib/virsandra.rb +++ b/lib/virsandra.rb @@ -1,38 +1,56 @@ -require "virtus" -require "cql" -require "simple_uuid" +require 'virtus' +require 'cql' +require 'simple_uuid' +require 'forwardable' require "virsandra/version" +require 'virsandra/errors' require "virsandra/configuration" -require "virsandra/connection" -require "virsandra/cql_value" -require "virsandra/query" -require "virsandra/model_query" -require "virsandra/model" module Virsandra - extend Configuration - class << self extend Forwardable def_delegator :connection, :execute + def_delegators :configuration, :reset!, *Virsandra::Configuration::OPTIONS.map{ |method_name| [method_name, :"#{method_name}="] }.flatten - def connection - @connection = Connection.new(self) if dirty? - @connection + def configuration + Thread.current[:configuration] ||= Virsandra::Configuration.new end - def dirty? - return true if @connection.nil? - @connection.options != self.to_hash + def configure + yield configuration + end + + def connection + if dirty? + Thread.current[:connection] = Virsandra::Connection.new(configuration) + configuration.accept_changes + end + Thread.current[:connection] end def disconnect! - if @connection && @connection.handle - @connection.disconnect! + if Thread.current[:connection].respond_to?(:dissconnect!) + Thread.current[:connection].disconnect! end - @connection = nil + Thread.current[:connection] = nil + Thread.current[:configuration] = nil end + + private + + def dirty? + Thread.current[:connection].nil? || configuration.changed? + end + end end + + + +require "virsandra/connection" +require "virsandra/cql_value" +require "virsandra/query" +require "virsandra/model_query" +require "virsandra/model" \ No newline at end of file diff --git a/lib/virsandra/configuration.rb b/lib/virsandra/configuration.rb index e8ce49d..4a3e1ca 100644 --- a/lib/virsandra/configuration.rb +++ b/lib/virsandra/configuration.rb @@ -1,28 +1,26 @@ module Virsandra - class ConfigurationError < Exception; ; end - - module Configuration - + class Configuration OPTIONS = [ + :consistency, :keyspace, :servers, - :consistency, - ] + ].freeze + + DEFAULT_OPTION_VALUES = { + servers: "127.0.0.1", + consistency: :quorum + }.freeze attr_accessor *OPTIONS - def self.extended(base) - base.reset! + def initialize(options = {}) + reset! + use_options(options || {}) + accept_changes end def reset! - self.servers = '127.0.0.1:9042' - self.consistency = :quorum - self.keyspace = nil - end - - def configure - yield self + use_options(DEFAULT_OPTION_VALUES) end def validate! @@ -31,12 +29,33 @@ def validate! end end + def accept_changes + @old_hash = hash + end + def to_hash - OPTIONS.reduce({}) do |settings, option| - settings[option] = self.send(option) - settings + OPTIONS.each_with_object({}) do |attr, settings| + settings[attr] = send(attr) end end + def changed? + hash != @old_hash + end + + def hash + to_hash.hash + end + + private + + def use_options(options) + options.each do |key, value| + if OPTIONS.include?(key) + send(:"#{key}=", value) + end + end + end end + end diff --git a/lib/virsandra/connection.rb b/lib/virsandra/connection.rb index 41ebe2c..dc158f0 100644 --- a/lib/virsandra/connection.rb +++ b/lib/virsandra/connection.rb @@ -3,18 +3,17 @@ class Connection extend Forwardable - attr_reader :handle, :options - - def initialize(options) - options.validate! - @options = options.to_hash + attr_reader :handle, :config + def initialize(config) + @config = config + config.validate! connect! end def connect! - @handle = Cql::Client.connect(hosts: @options[:servers]) - @handle.use(@options[:keyspace]) + @handle = Cql::Client.connect(hosts: @config.servers) + @handle.use(@config.keyspace) @handle end @@ -22,6 +21,10 @@ def disconnect! @handle.close end + def execute(query, consistency = nil) + @handle.execute(query, consistency || config.consistency) + end + # Delegate to CassandraCQL::Database handle def method_missing(method, *args, &block) return super unless @handle.respond_to?(method) diff --git a/lib/virsandra/version.rb b/lib/virsandra/version.rb index dfb3b21..54785de 100644 --- a/lib/virsandra/version.rb +++ b/lib/virsandra/version.rb @@ -1,3 +1,3 @@ module Virsandra - VERSION = "0.0.1" + VERSION = "0.5.0" end diff --git a/spec/lib/virsandra/connection_spec.rb b/spec/lib/virsandra/connection_spec.rb index 513dcb1..125f01c 100644 --- a/spec/lib/virsandra/connection_spec.rb +++ b/spec/lib/virsandra/connection_spec.rb @@ -1,25 +1,47 @@ require 'spec_helper' describe Virsandra::Connection do - before { Virsandra.disconnect! } + let(:config){ Virsandra::Configuration.new(keyspace: :my_keyspace) } + let(:handle){ double("handle", use: nil) } + subject(:connection){ described_class.new(config) } - let(:connection) { Virsandra::Connection.new(Virsandra)} + before do + Cql::Client.stub(:connect).and_return(handle) + end + + its(:config){ should eq(config) } - it "raises an exception if settings are invalid" do - Virsandra.keyspace = nil - expect { connection }.to raise_error(Virsandra::ConfigurationError) + context "invalid configuration" do + it "raises an exception if settings are invalid" do + config.keyspace = nil + expect { connection }.to raise_error(Virsandra::ConfigurationError) + end end it "obtains a db connection" do - connection.handle.should be_a Cql::Client::SynchronousClient + connection.handle.should eq(handle) end it "delegates to the cassandra handle" do - connection.handle.should_receive(:execute) - connection.execute("SELECT * FROM some_table") + connection.handle.should_receive(:keyspace) + connection.keyspace end it "checks if a cassandra connection will respond to a method" do + handle.stub(:respond_to? => true) connection.should respond_to :execute end + + it "should disconnect" do + handle.should_receive(:close) + connection.disconnect! + end + + describe "#execute" do + it "should use configuration consistency when none is given" do + config.consistency = :one + handle.should_receive(:execute).with("query", :one) + connection.execute("query") + end + end end diff --git a/spec/lib/virsandra/model_spec.rb b/spec/lib/virsandra/model_spec.rb index 1fc6860..e92b540 100644 --- a/spec/lib/virsandra/model_spec.rb +++ b/spec/lib/virsandra/model_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'feature_helper' class Company include Virsandra::Model @@ -14,7 +14,7 @@ class Company end -describe Virsandra::Model do +describe Virsandra::Model, integration: true do let(:id) { SimpleUUID::UUID.new } diff --git a/spec/lib/virsandra_spec.rb b/spec/lib/virsandra_spec.rb index c4527b7..fb1a018 100644 --- a/spec/lib/virsandra_spec.rb +++ b/spec/lib/virsandra_spec.rb @@ -1,80 +1,90 @@ require 'spec_helper' describe Virsandra do - - before { Virsandra.disconnect! } - - it "delegates execute to the connection handle" do - Virsandra.should respond_to :execute + let(:config){ double("config", :changed? => false, :accept_changes => nil) } + let(:connection){ double("connection") } + subject{ described_class } + + before do + described_class::Connection.stub(new: connection) + described_class::Configuration.stub(new: config) + described_class.disconnect! end - it "has a connection" do - Virsandra::Connection.should_receive(:new).with(Virsandra).and_call_original - Virsandra.connection.should be_a Virsandra::Connection + after do + described_class::Connection.unstub(:new) + described_class::Configuration.unstub(:new) + described_class.disconnect! end - it "can be configured via block" do - Virsandra.configure do |c| - c.keyspace = 'foo' + describe "#execute" do + it "should be delegated to connection" do + connection.should_receive(:execute) + described_class.execute end - - Virsandra.keyspace.should == 'foo' end - it "resets to default settings" do - Virsandra.keyspace = 'foo' - Virsandra.reset! - Virsandra.keyspace.should be_nil - end + describe "#connection" do + it "should pass configuration to connection" do + described_class::Connection.should_receive(:new).with(config).once + described_class.connection + described_class.connection.should eq(connection) + end - it "is invalid without a keyspace" do - Virsandra.keyspace = nil - expect { Virsandra.validate! }.to raise_error(Virsandra::ConfigurationError) - end + it "should create new connection when configuration has been changed" do + described_class::Connection.should_receive(:new).twice + described_class.connection + config.stub(:changed? => true) + described_class.connection + end - it "is invalid without a server" do - Virsandra.keyspace = 'foo' - Virsandra.servers = nil - expect { Virsandra.validate! }.to raise_error(Virsandra::ConfigurationError) + it "should have connection for each thread" do + described_class::Connection.should_receive(:new).twice + threads = [] + threads << Thread.new{ described_class.connection } + threads << Thread.new{ described_class.connection } + threads.map(&:join) + end end - it "hashifies the settings" do - Virsandra.reset! - defaults = { - servers: '127.0.0.1:9042', - consistency: :quorum, - keyspace: nil - } + describe "#configuration" do + it "returns configuration" do + described_class.configuration.should eq(config) + end - Virsandra.to_hash.should == defaults + it "should have configuration for every thread" do + described_class::Configuration.should_receive(:new).twice + threads = [] + threads << Thread.new{ described_class.configuration } + threads << Thread.new{ described_class.configuration } + threads.map(&:join) + end end - it "can tell if connection settings are dirty" do - Virsandra.connection - Virsandra.keyspace = 'funky' - Virsandra.should be_dirty + describe "#configure" do + it "should yield to block if block given" do + expect{|b| described_class.configure(&b) }.to yield_with_args(config) + end end - it "only connects once" do - Virsandra::Connection.should_receive(:new).once.and_call_original - Virsandra.connection - Virsandra.connection + describe "delegation to configuration" do + [:keyspace, :keyspace=, :servers, :servers=, :consistency, :consistency=, :reset!].each do |method_name| + it "should deletege #{method_name} to configuration" do + config.should_receive(method_name).any_number_of_times + described_class.send(method_name) + end + end end - it "reconnects if dirty" do - Virsandra::Connection.should_receive(:new).twice.and_call_original - Virsandra.connection - - Virsandra.keyspace = 'system' - Virsandra.connection - Virsandra.connection + describe "#dissconnect!" do + it "should dissconnect" do + described_class.connection + connection.stub(respond_to?: true) + connection.should_receive(:disconnect!) + described_class.disconnect! + end end - it "disconnects cassandra" do - Virsandra.connection #reestablish conn for the test - Virsandra.connection.should_receive(:disconnect!) - Virsandra.disconnect! - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3ec9c16..919675d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,69 +9,11 @@ require 'rspec' require 'virsandra' - -TEST_KEYSPACE = "virtest" - -def create_keyspace - Virsandra.keyspace = 'system' - Virsandra.execute("CREATE KEYSPACE #{TEST_KEYSPACE} WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}") - Virsandra.reset! -end - -def create_companies_table - cql = <<-CQL - CREATE TABLE companies ( - id uuid, - name text, - score int, - founder text, - founded int, - PRIMARY KEY (id, score)) - CQL - Virsandra.keyspace = TEST_KEYSPACE - Virsandra.execute(cql) -end - -def drop_keyspace - Virsandra.reset! - Virsandra.keyspace = 'system' - Virsandra.execute("DROP KEYSPACE #{TEST_KEYSPACE}") -end - -def build_up - begin - create_keyspace - create_companies_table - rescue Cql::QueryError - drop_keyspace - - if defined?(retried) - raise $! - else - retried = true - retry - end - end -end - RSpec.configure do |config| config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true config.filter_run :focus - config.before(:suite) do - build_up - end - - config.before do - Virsandra.reset! - Virsandra.keyspace = TEST_KEYSPACE - end - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 config.order = 'random' - end + From 7764d9c35700ec6540fb68a30929a9033636f065 Mon Sep 17 00:00:00 2001 From: Arturs Meisters Date: Thu, 1 Aug 2013 16:54:08 +0100 Subject: [PATCH 4/6] integration tests for virsandara created --- lib/virsandra/errors.rb | 3 ++ spec/feature_helper.rb | 62 ++++++++++++++++++++++ spec/integration/virsandra_spec.rb | 12 +++++ spec/lib/virsandra/configuration_spec.rb | 66 ++++++++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 lib/virsandra/errors.rb create mode 100644 spec/feature_helper.rb create mode 100644 spec/integration/virsandra_spec.rb create mode 100644 spec/lib/virsandra/configuration_spec.rb diff --git a/lib/virsandra/errors.rb b/lib/virsandra/errors.rb new file mode 100644 index 0000000..07d466b --- /dev/null +++ b/lib/virsandra/errors.rb @@ -0,0 +1,3 @@ +module Virsandra + class ConfigurationError < ArgumentError; ; end +end diff --git a/spec/feature_helper.rb b/spec/feature_helper.rb new file mode 100644 index 0000000..1960ade --- /dev/null +++ b/spec/feature_helper.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +TEST_KEYSPACE = "virtest" + +module IntegrationTestHelper + def create_keyspace + Virsandra.keyspace = 'system' + Virsandra.execute("CREATE KEYSPACE #{TEST_KEYSPACE} WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}") + Virsandra.reset! + end + + def create_companies_table + cql = <<-CQL + CREATE TABLE companies ( + id uuid, + name text, + score int, + founder text, + founded int, + PRIMARY KEY (id, score)) + CQL + Virsandra.keyspace = TEST_KEYSPACE + Virsandra.execute(cql) + end + + def drop_keyspace + Virsandra.reset! + Virsandra.keyspace = 'system' + Virsandra.execute("DROP KEYSPACE #{TEST_KEYSPACE}") + end + + def build_up + begin + create_keyspace + create_companies_table + rescue Cql::QueryError + drop_keyspace + + if defined?(retried) + raise $! + else + retried = true + retry + end + end + end +end + + + +RSpec.configure do |config| + + config.include(IntegrationTestHelper) + + config.before do + if example.metadata[:integration] + build_up + Virsandra.reset! + Virsandra.keyspace = TEST_KEYSPACE + end + end +end \ No newline at end of file diff --git a/spec/integration/virsandra_spec.rb b/spec/integration/virsandra_spec.rb new file mode 100644 index 0000000..419848f --- /dev/null +++ b/spec/integration/virsandra_spec.rb @@ -0,0 +1,12 @@ +require 'feature_helper' + +describe "Virsandra", integration: true do + + it "return connection to server" do + Virsandra.connection.should_not be_nil + end + + it "allows to disconnect" do + expect{ Virsandra.disconnect! }.not_to raise_error + end +end \ No newline at end of file diff --git a/spec/lib/virsandra/configuration_spec.rb b/spec/lib/virsandra/configuration_spec.rb new file mode 100644 index 0000000..e83a294 --- /dev/null +++ b/spec/lib/virsandra/configuration_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Virsandra::Configuration do + subject(:config){ described_class.new(given_options) } + let(:given_options){ {} } + + its(:servers){ should eq("127.0.0.1") } + its(:consistency){ should eq(:quorum) } + its(:keyspace){ should be_nil } + + it "should usge given values over default ones" do + described_class.new(consistency: :one).consistency.should eq(:one) + end + + describe "validate!" do + context "no keyspace" do + it "should raise an error" do + expect{ config.validate! }.to raise_error(Virsandra::ConfigurationError) + end + end + + context "no servers" do + let(:given_options){ {servers: nil} } + + it "should rase an error" do + expect{ config.validate! }.to raise_error(Virsandra::ConfigurationError) + end + end + end + + describe "#reset!" do + let(:given_options){ {keyspace: :my_keyspace} } + + it "reset options to default ones" do + config.reset! + config.keyspace.should == :my_keyspace + end + end + + describe "to_hash" do + it "returns all options as a hash" do + config.to_hash.should eq({ + consistency: :quorum, + keyspace: nil, + servers: "127.0.0.1" + }) + end + end + + describe "changed?" do + it "should be changed when option attribute value changes" do + config.changed?.should be_false + config.keyspace = :other + config.changed?.should be_true + end + + it "stops being changed when changes are accepted" do + config.changed?.should be_false + config.keyspace = :other + config.changed?.should be_true + config.accept_changes + config.changed?.should be_false + end + end + +end \ No newline at end of file From b09dcafd8e6e78eb6345f6a8ab64908747a0d92d Mon Sep 17 00:00:00 2001 From: Arturs Meisters Date: Tue, 6 Aug 2013 11:40:25 +0100 Subject: [PATCH 5/6] issues with cql-rb fixed --- lib/virsandra.rb | 43 +++++++++++++++++++++--- lib/virsandra/cql_value.rb | 15 ++++++--- lib/virsandra/model_query.rb | 3 +- lib/virsandra/query.rb | 1 + spec/integration/virsandra_spec.rb | 3 +- spec/lib/virsandra/configuration_spec.rb | 4 +-- spec/lib/virsandra_spec.rb | 30 +++++++++++++---- virsandra.gemspec | 12 +++---- 8 files changed, 85 insertions(+), 26 deletions(-) diff --git a/lib/virsandra.rb b/lib/virsandra.rb index da09b01..0838b8b 100644 --- a/lib/virsandra.rb +++ b/lib/virsandra.rb @@ -3,6 +3,7 @@ require 'simple_uuid' require 'forwardable' + require "virsandra/version" require 'virsandra/errors' require "virsandra/configuration" @@ -10,10 +11,6 @@ module Virsandra class << self - extend Forwardable - def_delegator :connection, :execute - def_delegators :configuration, :reset!, *Virsandra::Configuration::OPTIONS.map{ |method_name| [method_name, :"#{method_name}="] }.flatten - def configuration Thread.current[:configuration] ||= Virsandra::Configuration.new end @@ -24,6 +21,7 @@ def configure def connection if dirty? + disconnect! Thread.current[:connection] = Virsandra::Connection.new(configuration) configuration.accept_changes end @@ -31,13 +29,48 @@ def connection end def disconnect! - if Thread.current[:connection].respond_to?(:dissconnect!) + if Thread.current[:connection].respond_to?(:disconnect!) Thread.current[:connection].disconnect! end Thread.current[:connection] = nil + end + + def reset! + configuration.reset! + end + + def reset_configuration! Thread.current[:configuration] = nil end + def consistency + configuration.consistency + end + + def keyspace + configuration.keyspace + end + + def servers + configuration.servers + end + + def consistency=(value) + configuration.consistency = value + end + + def keyspace=(value) + configuration.keyspace = value + end + + def servers=(value) + configuration.servers = value + end + + def execute(query) + connection.execute(query) + end + private def dirty? diff --git a/lib/virsandra/cql_value.rb b/lib/virsandra/cql_value.rb index 086bb13..c3a6c73 100644 --- a/lib/virsandra/cql_value.rb +++ b/lib/virsandra/cql_value.rb @@ -12,18 +12,23 @@ def initialize(value) end def to_cql - case value - when Numeric - value.to_s - when SimpleUUID::UUID + if value.respond_to?(:to_guid) value.to_guid - else + elsif should_escape?(value) "'#{escape(value)}'" + else + value.to_s end end private + def should_escape?(value) + !![String, Symbol, Time, Date].detect do |klass| + value.is_a?(klass) + end + end + def escape(str) str = str.to_s.gsub(/'/,"''") str.force_encoding('ASCII-8BIT') diff --git a/lib/virsandra/model_query.rb b/lib/virsandra/model_query.rb index af780ed..42c0c3f 100644 --- a/lib/virsandra/model_query.rb +++ b/lib/virsandra/model_query.rb @@ -40,7 +40,8 @@ def valid_search_params?(params) def query_enumerator(query) Enumerator.new do |yielder| - query.execute.each do |row| + rows = query.execute + rows.each do |row| record = @model.new(row.to_hash) yielder.yield record end diff --git a/lib/virsandra/query.rb b/lib/virsandra/query.rb index 9c4b441..e2614a8 100644 --- a/lib/virsandra/query.rb +++ b/lib/virsandra/query.rb @@ -56,6 +56,7 @@ def values *args end def execute + @row = Virsandra.execute(self.to_s) end diff --git a/spec/integration/virsandra_spec.rb b/spec/integration/virsandra_spec.rb index 419848f..6e1bfd8 100644 --- a/spec/integration/virsandra_spec.rb +++ b/spec/integration/virsandra_spec.rb @@ -7,6 +7,7 @@ end it "allows to disconnect" do - expect{ Virsandra.disconnect! }.not_to raise_error + Virsandra.connection.should_receive(:disconnect!).and_call_original + Virsandra.disconnect! end end \ No newline at end of file diff --git a/spec/lib/virsandra/configuration_spec.rb b/spec/lib/virsandra/configuration_spec.rb index e83a294..b516f84 100644 --- a/spec/lib/virsandra/configuration_spec.rb +++ b/spec/lib/virsandra/configuration_spec.rb @@ -8,7 +8,7 @@ its(:consistency){ should eq(:quorum) } its(:keyspace){ should be_nil } - it "should usge given values over default ones" do + it "uses given values over default ones" do described_class.new(consistency: :one).consistency.should eq(:one) end @@ -22,7 +22,7 @@ context "no servers" do let(:given_options){ {servers: nil} } - it "should rase an error" do + it "should raise an error" do expect{ config.validate! }.to raise_error(Virsandra::ConfigurationError) end end diff --git a/spec/lib/virsandra_spec.rb b/spec/lib/virsandra_spec.rb index fb1a018..49bf88a 100644 --- a/spec/lib/virsandra_spec.rb +++ b/spec/lib/virsandra_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Virsandra do - let(:config){ double("config", :changed? => false, :accept_changes => nil) } + let(:config){ double("config", :changed? => false, :accept_changes => true) } let(:connection){ double("connection") } subject{ described_class } @@ -9,18 +9,20 @@ described_class::Connection.stub(new: connection) described_class::Configuration.stub(new: config) described_class.disconnect! + described_class.reset_configuration! end after do described_class::Connection.unstub(:new) described_class::Configuration.unstub(:new) described_class.disconnect! + described_class.reset_configuration! end describe "#execute" do it "should be delegated to connection" do - connection.should_receive(:execute) - described_class.execute + connection.should_receive(:execute).with("query") + described_class.execute("query") end end @@ -69,10 +71,17 @@ end describe "delegation to configuration" do - [:keyspace, :keyspace=, :servers, :servers=, :consistency, :consistency=, :reset!].each do |method_name| + [:keyspace, :servers, :consistency, :reset!].each do |method_name| it "should deletege #{method_name} to configuration" do - config.should_receive(method_name).any_number_of_times - described_class.send(method_name) + config.should_receive(method_name).any_number_of_times.and_return("value") + described_class.send(method_name).should eq("value") + end + end + + [:keyspace=, :servers=, :consistency=].each do |method_name| + it "should deletege #{method_name} to configuration" do + config.should_receive(method_name).with("value").any_number_of_times + described_class.send(method_name, "value") end end end @@ -86,5 +95,14 @@ end end + describe "#reset_configuration!" do + it "should reset configuration" do + described_class::Configuration.should_receive(:new).twice + described_class.configuration + described_class.reset_configuration! + described_class.configuration + end + end + end diff --git a/virsandra.gemspec b/virsandra.gemspec index 79a4d72..71dcf6b 100644 --- a/virsandra.gemspec +++ b/virsandra.gemspec @@ -19,11 +19,11 @@ Gem::Specification.new do |gem| #gem.add_dependency "cassandra-cql", #WHEN IT SUPPORTS CQL3 - gem.add_dependency "virtus", ">= 0.5.4" - gem.add_dependency "cql-rb", ">= 1.0.2" - gem.add_dependency "simple_uuid", ">= 0.3.0" + gem.add_dependency "virtus", "~> 0.5.4" + gem.add_dependency "cql-rb", "~> 1.0.4" + gem.add_dependency "simple_uuid", "~> 0.3.0" - gem.add_development_dependency "rake", ">= 0.9.2" - gem.add_development_dependency "rspec", ">= 2.10.0" - gem.add_development_dependency "simplecov" + gem.add_development_dependency "rake", "~>10.0.4" + gem.add_development_dependency "rspec", "~>2.13.0" + gem.add_development_dependency "simplecov", "~>0.7.1" end From 477035c84fcefb57aee1294e59cdb26413bb35cc Mon Sep 17 00:00:00 2001 From: Arturs Meisters Date: Tue, 6 Aug 2013 12:54:24 +0100 Subject: [PATCH 6/6] gemspec updated --- virsandra.gemspec | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/virsandra.gemspec b/virsandra.gemspec index 71dcf6b..af4b016 100644 --- a/virsandra.gemspec +++ b/virsandra.gemspec @@ -10,16 +10,21 @@ Gem::Specification.new do |gem| gem.email = ["rob@servermilk.com"] gem.description = %q{Cassandra CQL3 persistence for Virtus extended classes} gem.summary = %q{Easily store models defined with Virtus in Cassandra using CQL3} - gem.homepage = "" - - gem.files = `git ls-files`.split($/) - gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } - gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) + gem.homepage = "https://github.com/ottbot/virsandra" + + gem.licenses = ["MIT"] + gem.files = Dir["{lib,vendor}/**/*"] + ["Rakefile", "README.md"] + gem.test_files = Dir["{spec}/**/*"] + + gem.extra_rdoc_files = [ + "LICENSE.txt", + "README.md" + ] gem.require_paths = ["lib"] - + #gem.add_dependency "cassandra-cql", #WHEN IT SUPPORTS CQL3 - gem.add_dependency "virtus", "~> 0.5.4" + gem.add_dependency "virtus", "~> 0.5.5" gem.add_dependency "cql-rb", "~> 1.0.4" gem.add_dependency "simple_uuid", "~> 0.3.0"