cql-model 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ 1.9.3-p392
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Runtime dependencies
4
+ gem "cql-rb", "~> 1.0.0.pre4"
5
+
6
+ # Gems used during test and development
7
+ group :development do
8
+ gem "rake", "~> 0.9"
9
+ gem "rspec", "~> 2.0"
10
+ gem "cucumber", "~> 1.0"
11
+ gem "jeweler", "~> 1.8.3"
12
+ gem "debugger"
13
+ gem "rdoc", ">= 2.4.2"
14
+ gem "flexmock", "~> 0.8"
15
+ gem "syntax", "~> 1.0.0" #rspec will syntax-highlight code snippets if this gem is available
16
+ gem "nokogiri", "~> 1.5"
17
+ end
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ builder (3.1.4)
5
+ columnize (0.3.6)
6
+ cql-rb (1.0.0.pre4)
7
+ cucumber (1.2.1)
8
+ builder (>= 2.1.2)
9
+ diff-lcs (>= 1.1.3)
10
+ gherkin (~> 2.11.0)
11
+ json (>= 1.4.6)
12
+ debugger (1.5.0)
13
+ columnize (>= 0.3.1)
14
+ debugger-linecache (~> 1.2.0)
15
+ debugger-ruby_core_source (~> 1.2.0)
16
+ debugger-linecache (1.2.0)
17
+ debugger-ruby_core_source (1.2.0)
18
+ diff-lcs (1.1.3)
19
+ flexmock (0.9.0)
20
+ gherkin (2.11.6)
21
+ json (>= 1.7.6)
22
+ git (1.2.5)
23
+ jeweler (1.8.4)
24
+ bundler (~> 1.0)
25
+ git (>= 1.2.5)
26
+ rake
27
+ rdoc
28
+ json (1.7.7)
29
+ nokogiri (1.5.9)
30
+ rake (0.9.6)
31
+ rdoc (4.0.1)
32
+ json (~> 1.4)
33
+ rspec (2.12.0)
34
+ rspec-core (~> 2.12.0)
35
+ rspec-expectations (~> 2.12.0)
36
+ rspec-mocks (~> 2.12.0)
37
+ rspec-core (2.12.2)
38
+ rspec-expectations (2.12.1)
39
+ diff-lcs (~> 1.1.3)
40
+ rspec-mocks (2.12.2)
41
+ syntax (1.0.0)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ cql-rb (~> 1.0.0.pre4)
48
+ cucumber (~> 1.0)
49
+ debugger
50
+ flexmock (~> 0.8)
51
+ jeweler (~> 1.8.3)
52
+ nokogiri (~> 1.5)
53
+ rake (~> 0.9)
54
+ rdoc (>= 2.4.2)
55
+ rspec (~> 2.0)
56
+ syntax (~> 1.0.0)
@@ -0,0 +1,24 @@
1
+ Overview
2
+ ========
3
+
4
+ [![Build Status](https://travis-ci.org/xeger/cql-model.png?branch=master)](https://travis-ci.org/xeger/cql-model)
5
+
6
+ TODO
7
+
8
+ To-Do List
9
+ ==========
10
+
11
+ ## Features
12
+
13
+ * Add support for Time and other obvious Cassandra data types
14
+ * Using-keyspace override
15
+ * Declaration, read and write of set/list/map properties
16
+ * Batch mutations
17
+ * Notation for composite partition key (CompositeType), e.g. `primary_key [:id, :surname], :first_name`
18
+ * Prepared statement support for SELECT & named scopes
19
+ * Prepared statement support for INSERT/UPDATE
20
+
21
+ ## Usability / Correctness / Performance
22
+
23
+ * Better solution for using and switching namespaces -- thread/fiber safety
24
+ * Better use of cql-rb connection pooling
@@ -0,0 +1,54 @@
1
+ # -*-ruby-*-
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+
5
+ require 'rake'
6
+ require 'rdoc/task'
7
+ require 'rubygems/package_task'
8
+
9
+ require 'rake/clean'
10
+ require 'rspec/core/rake_task'
11
+ require 'cucumber/rake/task'
12
+
13
+ require 'jeweler'
14
+
15
+ task :default => [:cucumber, :spec]
16
+
17
+ desc "Run unit tests"
18
+ RSpec::Core::RakeTask.new do |t|
19
+ t.pattern = Dir['spec/**/*_spec.rb']
20
+ end
21
+
22
+ desc "Run functional tests"
23
+ Cucumber::Rake::Task.new do |t|
24
+ t.cucumber_opts = %w{--color --format pretty}
25
+ end
26
+
27
+ desc 'Generate documentation for the cql-model gem.'
28
+ Rake::RDocTask.new(:rdoc) do |rdoc|
29
+ rdoc.rdoc_dir = 'doc'
30
+ rdoc.title = 'Cql'
31
+ rdoc.options << '--line-numbers' << '--inline-source'
32
+ rdoc.rdoc_files.include('README.md')
33
+ rdoc.rdoc_files.include('lib/**/*.rb')
34
+ rdoc.rdoc_files.exclude('features/**/*')
35
+ rdoc.rdoc_files.exclude('spec/**/*')
36
+ end
37
+
38
+ Jeweler::Tasks.new do |gem|
39
+ # gem is a Gem::Specification; see http://docs.rubygems.org/read/chapter/20 for more options
40
+ gem.name = "cql-model"
41
+ gem.required_ruby_version = ">= 1.9.0"
42
+ gem.homepage = "https://github.com/xeger/cql-model"
43
+ gem.license = "MIT"
44
+ gem.summary = %Q{Cassandra CQL model.}
45
+ gem.description = %Q{Lightweight, performant OOP wrapper for Cassandra tables; inspired by DataMapper.}
46
+ gem.email = "gemspec@tracker.xeger.net"
47
+ gem.authors = ['Tony Spataro']
48
+ gem.files.exclude 'features/**/*'
49
+ gem.files.exclude 'spec/**/*'
50
+ end
51
+
52
+ Jeweler::RubygemsDotOrgTasks.new
53
+
54
+ CLEAN.include('pkg')
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,85 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "cql-model"
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tony Spataro"]
12
+ s.date = "2013-04-24"
13
+ s.description = "Lightweight, performant OOP wrapper for Cassandra tables; inspired by DataMapper."
14
+ s.email = "gemspec@tracker.xeger.net"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".ruby-version",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "cql-model.gemspec",
26
+ "cql_model.rconf",
27
+ "lib/cql/model.rb",
28
+ "lib/cql/model/dsl.rb",
29
+ "lib/cql/model/query.rb",
30
+ "lib/cql/model/query/comparison_expression.rb",
31
+ "lib/cql/model/query/expression.rb",
32
+ "lib/cql/model/query/insert_statement.rb",
33
+ "lib/cql/model/query/mutation_statement.rb",
34
+ "lib/cql/model/query/select_statement.rb",
35
+ "lib/cql/model/query/statement.rb",
36
+ "lib/cql/model/query/update_expression.rb",
37
+ "lib/cql/model/query/update_statement.rb"
38
+ ]
39
+ s.homepage = "https://github.com/xeger/cql-model"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.required_ruby_version = Gem::Requirement.new(">= 1.9.0")
43
+ s.rubygems_version = "1.8.23"
44
+ s.summary = "Cassandra CQL model."
45
+
46
+ if s.respond_to? :specification_version then
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<cql-rb>, ["~> 1.0.0.pre4"])
51
+ s.add_development_dependency(%q<rake>, ["~> 0.9"])
52
+ s.add_development_dependency(%q<rspec>, ["~> 2.0"])
53
+ s.add_development_dependency(%q<cucumber>, ["~> 1.0"])
54
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
55
+ s.add_development_dependency(%q<debugger>, [">= 0"])
56
+ s.add_development_dependency(%q<rdoc>, [">= 2.4.2"])
57
+ s.add_development_dependency(%q<flexmock>, ["~> 0.8"])
58
+ s.add_development_dependency(%q<syntax>, ["~> 1.0.0"])
59
+ s.add_development_dependency(%q<nokogiri>, ["~> 1.5"])
60
+ else
61
+ s.add_dependency(%q<cql-rb>, ["~> 1.0.0.pre4"])
62
+ s.add_dependency(%q<rake>, ["~> 0.9"])
63
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
64
+ s.add_dependency(%q<cucumber>, ["~> 1.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
66
+ s.add_dependency(%q<debugger>, [">= 0"])
67
+ s.add_dependency(%q<rdoc>, [">= 2.4.2"])
68
+ s.add_dependency(%q<flexmock>, ["~> 0.8"])
69
+ s.add_dependency(%q<syntax>, ["~> 1.0.0"])
70
+ s.add_dependency(%q<nokogiri>, ["~> 1.5"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<cql-rb>, ["~> 1.0.0.pre4"])
74
+ s.add_dependency(%q<rake>, ["~> 0.9"])
75
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
76
+ s.add_dependency(%q<cucumber>, ["~> 1.0"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
78
+ s.add_dependency(%q<debugger>, [">= 0"])
79
+ s.add_dependency(%q<rdoc>, [">= 2.4.2"])
80
+ s.add_dependency(%q<flexmock>, ["~> 0.8"])
81
+ s.add_dependency(%q<syntax>, ["~> 1.0.0"])
82
+ s.add_dependency(%q<nokogiri>, ["~> 1.5"])
83
+ end
84
+ end
85
+
@@ -0,0 +1,11 @@
1
+ # Configuration settings for Cql
2
+ ruby do
3
+ version 'ruby-1.9.3-p392'
4
+ end
5
+ bundler do
6
+ version '1.2.1'
7
+ end
8
+ cassandra do
9
+ version '1.1.10'
10
+ end
11
+
@@ -0,0 +1,49 @@
1
+ require 'thread'
2
+ require 'cql'
3
+
4
+ module Cql::Model
5
+ # Raised if the user calls DSL that cannot generate valid CQL
6
+ class SyntaxError < Exception; end
7
+
8
+ # Raised if an insert statement does not specify all the primary keys
9
+ # or if an update statement does not specify any key (part of a composite primary key or a primary key)
10
+ class MissingKey < Exception; end
11
+
12
+ # Type aliases for use with the property-declaration DSL.
13
+ Uuid = Cql::Uuid
14
+ UUID = Uuid
15
+
16
+ # Type alias for use with the property-declaration DSL.
17
+ Boolean = TrueClass
18
+
19
+ end
20
+
21
+ require 'cql/model/query'
22
+ require 'cql/model/class_methods'
23
+ require 'cql/model/instance_methods'
24
+
25
+ module Cql::Model
26
+ def self.included(klass)
27
+ klass.__send__(:extend, Cql::Model::ClassMethods)
28
+ klass.__send__(:include, Cql::Model::InstanceMethods)
29
+ end
30
+
31
+ # Get or set the "master" client connection shared by every model that doesn't bother to
32
+ # set its own. Defaults to a localhost connection with no default keyspace; every query
33
+ # must be wrapped in a "using_keyspace" method call.
34
+ #
35
+ # @param [optional, Cql::Client] new_client the new client to set
36
+ # @return [Cql::Client] the current client
37
+ def self.cql_client(new_client=nil)
38
+ if new_client
39
+ @@cql_model_mutex.synchronize do
40
+ @@cql_client = new_client
41
+ end
42
+ else
43
+ @@cql_client ||= Cql::Client.new
44
+ @@cql_client.start! unless @@cql_client.connected?
45
+ end
46
+
47
+ @@cql_client
48
+ end
49
+ end
@@ -0,0 +1,178 @@
1
+ module Cql::Model::ClassMethods
2
+ def self.extended(klass)
3
+ klass.instance_eval do
4
+ # The mutex is shared by all Cql::Model inheritors
5
+ @@cql_model_mutex ||= Mutex.new
6
+
7
+ # Other attributes are tracked per-class
8
+ @cql_table_name ||= klass.name.split('::').last
9
+ @cql_model_properties ||= {}
10
+ @cql_model_keys ||= []
11
+ @cql_model_read_consistency ||= 'LOCAL_QUORUM'
12
+ @cql_model_write_consistency ||= 'LOCAL_QUORUM'
13
+ end
14
+ end
15
+
16
+ # Get or set the client connection used by this class.
17
+ #
18
+ # @param [optional, Cql::Client] new_client the new client to set
19
+ # @return [Cql::Client] the current client
20
+ def cql_client(new_client=nil)
21
+ if new_client
22
+ @@cql_model_mutex.synchronize do
23
+ @cql_client = new_client
24
+ end
25
+ end
26
+
27
+ @cql_client || ::Cql::Model.cql_client
28
+ end
29
+
30
+ # @TODO docs
31
+ def table_name(new_name=nil)
32
+ if new_name
33
+ @@cql_model_mutex.synchronize do
34
+ # Set the table name
35
+ @cql_table_name = new_name
36
+ end
37
+ else
38
+ # Get the table name
39
+ @cql_table_name
40
+ end
41
+ end
42
+
43
+ # @TODO docs
44
+ def read_consistency(new_consistency=nil)
45
+ if new_consistency
46
+ @cql_model_read_consistency = new_consistency
47
+ else
48
+ @cql_model_read_consistency
49
+ end
50
+ end
51
+
52
+ # @TODO docs
53
+ def write_consistency(new_consistency=nil)
54
+ if new_consistency
55
+ @cql_model_write_consistency = new_consistency
56
+ else
57
+ @cql_model_write_consistency
58
+ end
59
+ end
60
+
61
+ # Specify or get a primary key or a composite primary key
62
+ #
63
+ # @param key_vals [Symbol|Array<Symbol>] single key name or composite key names
64
+ #
65
+ # @return [Cql::Model] self
66
+ def primary_key(*keys)
67
+ if keys.empty?
68
+ @cql_model_keys
69
+ else
70
+ @@cql_model_mutex.synchronize do
71
+ @cql_model_keys = keys
72
+ end
73
+ self
74
+ end
75
+ end
76
+
77
+ # @TODO docs
78
+ def property(name, type, opts={})
79
+ definition = {}
80
+
81
+ # If the user specified the name as a symbol, then they automatically get
82
+ # a reader and writer because the property has a predictable, fixed column
83
+ # name.
84
+ if name.is_a?(Symbol)
85
+ definition[:reader] = opts[:reader] || name
86
+ definition[:writer] = opts[:writer] || "#{definition[:reader]}=".to_sym
87
+ name = name.to_s
88
+ end
89
+
90
+ @@cql_model_mutex.synchronize do
91
+ definition[:type] = type
92
+
93
+ if @cql_model_properties.key?(name) && (@cql_model_properties[name] != definition)
94
+ raise ArgumentError, "Property #{name} is already defined"
95
+ end
96
+
97
+ unless @cql_model_properties.key?(name)
98
+ @cql_model_properties[name] = definition
99
+
100
+ __send__(:define_method, definition[:reader]) do
101
+ self[name]
102
+ end if definition[:reader]
103
+
104
+ __send__(:define_method, definition[:writer]) do |value|
105
+ self[name] = value
106
+ end if definition[:writer]
107
+ end
108
+ end
109
+
110
+ self
111
+ end
112
+
113
+ # @TODO docs
114
+ def scope(name, &block)
115
+ @@cql_model_mutex.synchronize do
116
+ eigenclass = class <<self
117
+ self
118
+ end
119
+
120
+ eigenclass.instance_eval do
121
+ define_method(name.to_sym) do |*params|
122
+ # @TODO use a prepared statement for speed
123
+ self.select.where(*params, &block)
124
+ end
125
+ end
126
+ end
127
+
128
+ self
129
+ end
130
+
131
+ # Begin building a CQL SELECT statement.
132
+ #
133
+ # @param [Object] *params list of yield parameters for the block
134
+ #
135
+ # @example tell us how old Joe is
136
+ # Person.select.where { name == 'Joe' }.each { |person| puts person.age }
137
+ def select(*params)
138
+ Cql::Model::Query::SelectStatement.new(self).select(*params)
139
+ end
140
+
141
+ # Begin building a CQL INSERT statement.
142
+ # @see Cql::Model::Query::InsertStatement
143
+ #
144
+ # @param [Hash] values Hash of column values indexed by column name
145
+ # @return [Cql::Model::Query::InsertStatement] a query object to customize (timestamp, ttl, etc) or execute
146
+ #
147
+ # @example
148
+ # Person.create(:name => 'Joe', :age => 25).ttl(3600).execute
149
+ def insert(values)
150
+ Cql::Model::Query::InsertStatement.new(self).insert(values)
151
+ end
152
+
153
+ alias create insert
154
+
155
+ # Start an UPDATE CQL statement
156
+ # The method #keys must be called on the result before #execute
157
+ # @see Cql::Model::Query::UpdateStatement
158
+ #
159
+ # @param [Hash] values Hash of column values indexed by column name, optional
160
+ # @return [Cql::Model::Query::UpdateStatement] a query object to customize (keys, ttl, timestamp etc) then execute
161
+ #
162
+ # @example
163
+ # Person.update(:updated_at => Time.now.utc).keys(:name => ['joe', 'john', 'jane'])
164
+ # Person.update.ttl(3600).keys(:name => 'joe')
165
+ def update(values={})
166
+ Cql::Model::Query::UpdateStatement.new(self).update(values)
167
+ end
168
+
169
+ # @TODO docs
170
+ def each_row(&block)
171
+ Cql::Model::Query::SelectStatement.new(self).each_row(&block)
172
+ end
173
+
174
+ # @TODO docs
175
+ def each(&block)
176
+ Cql::Model::Query::SelectStatement.new(self).each(&block)
177
+ end
178
+ end