believer 0.1.4 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +168 -53
- data/lib/believer.rb +29 -26
- data/lib/believer/base.rb +2 -0
- data/lib/believer/column.rb +65 -0
- data/lib/believer/columns.rb +3 -62
- data/lib/believer/command.rb +14 -16
- data/lib/believer/connection.rb +25 -6
- data/lib/believer/cql_helper.rb +20 -0
- data/lib/believer/ddl.rb +7 -5
- data/lib/believer/delete.rb +4 -2
- data/lib/believer/environment.rb +43 -13
- data/lib/believer/environment/merb_env.rb +14 -0
- data/lib/believer/environment/rails_env.rb +3 -4
- data/lib/believer/insert.rb +1 -1
- data/lib/believer/model_schema.rb +10 -44
- data/lib/believer/order_by.rb +8 -1
- data/lib/believer/persistence.rb +6 -1
- data/lib/believer/query.rb +3 -3
- data/lib/believer/relation.rb +90 -0
- data/lib/believer/scoped_command.rb +11 -4
- data/lib/believer/test/test_run_life_cycle.rb +49 -0
- data/lib/believer/values.rb +2 -11
- data/lib/believer/version.rb +1 -1
- data/lib/believer/where_clause.rb +1 -1
- data/spec/believer/delete_spec.rb +2 -2
- data/spec/believer/environment_spec.rb +32 -0
- data/spec/believer/insert_spec.rb +2 -2
- data/spec/believer/query_spec.rb +14 -14
- data/spec/believer/relation_spec.rb +33 -0
- data/spec/spec_helper.rb +6 -20
- data/spec/support/setup_database.rb +1 -1
- data/spec/support/test_classes.rb +34 -21
- metadata +27 -7
- data/lib/believer/owner.rb +0 -48
- data/lib/believer/primary_key.rb +0 -5
- data/lib/believer/test/rspec/test_run_life_cycle.rb +0 -51
- data/spec/believer/environment/rails_env_spec.rb +0 -0
data/lib/believer/query.rb
CHANGED
@@ -53,9 +53,9 @@ module Believer
|
|
53
53
|
end
|
54
54
|
|
55
55
|
cql << " FROM #{@record_class.table_name}"
|
56
|
-
cql << " WHERE #{
|
57
|
-
cql << " #{
|
58
|
-
cql << " #{
|
56
|
+
cql << " WHERE #{wheres.map { |wc| "#{wc.to_cql}" }.join(' AND ')}" if wheres.any?
|
57
|
+
cql << " #{order_by.to_cql}" unless order_by.nil?
|
58
|
+
cql << " #{limit_to.to_cql}" unless limit_to.nil?
|
59
59
|
cql
|
60
60
|
end
|
61
61
|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Believer
|
2
|
+
module Relation
|
3
|
+
extend ::ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def cql_record_relations
|
7
|
+
@cql_record_relations ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_single(name, opts = {})
|
11
|
+
if opts[:foreign_key].nil?
|
12
|
+
opts[:foreign_key] = opts[:key].nil? ? :id : opts[:key]
|
13
|
+
end
|
14
|
+
if opts[:key].nil?
|
15
|
+
opts[:key] = opts[:foreign_key].nil? ? "#{self.name.underscore}_id".to_sym : opts[:foreign_key]
|
16
|
+
end
|
17
|
+
has_a_relation(name, opts.merge(:type => :one_2_one))
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_some(name, opts = {})
|
21
|
+
if opts[:foreign_key].nil?
|
22
|
+
opts[:foreign_key] = opts[:key].nil? ? "#{self.name.underscore}_id".to_sym : opts[:key]
|
23
|
+
end
|
24
|
+
if opts[:key].nil?
|
25
|
+
opts[:key] = opts[:foreign_key].nil? ? :id : opts[:foreign_key]
|
26
|
+
end
|
27
|
+
|
28
|
+
has_a_relation(name, opts.merge(:type => :one_2_many))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Defines a (one to many) relation.
|
32
|
+
# @param name [Symbol] the name of the relation, which will also be used as an accessor method
|
33
|
+
# @param opts [Hash] the options
|
34
|
+
# @option opts :class the name of the referenced class. If nil, it will be created from the relation name. Can be a constant or a String
|
35
|
+
# @option opts :foreign_key the name of the attribute of the referenced class which acts as the key to this object
|
36
|
+
# @option opts :key the name of the attribute of the referencing class which acts as the key the referenced records
|
37
|
+
# @option opts :filter a Proc or lambda which is called with a Believer::Query instance as a parameter to tweak the relation query
|
38
|
+
def has_a_relation(name, opts = {})
|
39
|
+
defaults = {
|
40
|
+
}
|
41
|
+
options = defaults.merge(opts)
|
42
|
+
|
43
|
+
# Use a proc to avoid classes not yet loaded being referenced
|
44
|
+
get_relation_class = lambda do
|
45
|
+
if options[:relation_class].nil?
|
46
|
+
relation_class = options[:class]
|
47
|
+
if relation_class.nil?
|
48
|
+
cn = name.to_s.camelize
|
49
|
+
if options[:type] == :one_2_many
|
50
|
+
cn = cn[0, (name.to_s.size - 1)]
|
51
|
+
end
|
52
|
+
relation_class = Kernel.const_get(cn)
|
53
|
+
elsif relation_class.is_a?(String)
|
54
|
+
relation_class = relation_class.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name) }
|
55
|
+
end
|
56
|
+
options[:relation_class] = relation_class
|
57
|
+
end
|
58
|
+
options[:relation_class]
|
59
|
+
end
|
60
|
+
|
61
|
+
cql_record_relations[name] = options
|
62
|
+
|
63
|
+
opts[:foreign_key] = opts[:foreign_key].is_a?(Array) ? opts[:foreign_key] : [opts[:foreign_key]]
|
64
|
+
opts[:key] = opts[:key].is_a?(Array) ? opts[:key] : [opts[:key]]
|
65
|
+
|
66
|
+
raise "key and foreign_key must have same number of items" if opts[:key].size != opts[:foreign_key].size
|
67
|
+
|
68
|
+
self.redefine_method(name) do
|
69
|
+
relation_class = get_relation_class.call
|
70
|
+
|
71
|
+
q = relation_class.scoped
|
72
|
+
opts[:foreign_key].each_with_index do |fk, index|
|
73
|
+
key = opts[:key][index]
|
74
|
+
q = q.where(fk => self.send(key))
|
75
|
+
end
|
76
|
+
if options[:filter]
|
77
|
+
q = self.instance_exec(q, &(options[:filter]))
|
78
|
+
return EmptyResult.new unless q
|
79
|
+
end
|
80
|
+
return q.first if options[:type] == :one_2_one
|
81
|
+
q
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
module Believer
|
2
|
+
# A command implementation which includes a CQL WHERE clause
|
2
3
|
class ScopedCommand < Command
|
3
4
|
|
4
|
-
attr_accessor :wheres
|
5
|
-
|
6
5
|
def query_attributes
|
7
6
|
attrs = super
|
8
|
-
attrs.merge(:wheres =>
|
7
|
+
attrs.merge(:wheres => (wheres.dup))
|
8
|
+
end
|
9
|
+
|
10
|
+
def wheres
|
11
|
+
#puts "Wheres: #{@wheres}"
|
12
|
+
@wheres ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def wheres=(w)
|
16
|
+
@wheres = w.is_a?(Array) ? w : [w]
|
9
17
|
end
|
10
18
|
|
11
19
|
def where(*args)
|
12
20
|
q = clone
|
13
|
-
q.wheres ||= []
|
14
21
|
q.wheres << WhereClause.new(*args)
|
15
22
|
q
|
16
23
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Believer
|
2
|
+
module Test
|
3
|
+
# Controls the life cycle for all objects created in a test
|
4
|
+
module TestRunLifeCycle
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
Believer::Base.observers = Destructor
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
Destructor.instance.cleanup
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def saved_models
|
16
|
+
@saved_models ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_save(model)
|
20
|
+
saved_models << model
|
21
|
+
end
|
22
|
+
|
23
|
+
# Detroys all CqlRecord::Base instances created
|
24
|
+
class Destructor < Believer::Observer
|
25
|
+
observe Believer::Base
|
26
|
+
|
27
|
+
def cleanup
|
28
|
+
saved_models.each do |model|
|
29
|
+
begin
|
30
|
+
model.destroy
|
31
|
+
rescue Exception => e
|
32
|
+
puts "Could not destroy model #{model}: #{e}\n#{e.backtrace.join("\n")}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def saved_models
|
38
|
+
@saved_models ||= []
|
39
|
+
end
|
40
|
+
|
41
|
+
def after_save(model)
|
42
|
+
saved_models << model
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/believer/values.rb
CHANGED
@@ -2,17 +2,6 @@ module Believer
|
|
2
2
|
|
3
3
|
module Values
|
4
4
|
|
5
|
-
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S%z'
|
6
|
-
|
7
|
-
def to_cql_literal(value)
|
8
|
-
return 'NULL' if value.nil?
|
9
|
-
return "'#{value}'" if value.is_a?(String)
|
10
|
-
return "#{value}" if value.is_a?(Numeric)
|
11
|
-
return "'#{value.strftime(TIMESTAMP_FORMAT)}'" if value.is_a?(Time) || value.is_a?(DateTime)
|
12
|
-
#return "#{value.to_i * 1000}" if value.is_a?(Time) || value.is_a?(DateTime)
|
13
|
-
return nil
|
14
|
-
end
|
15
|
-
|
16
5
|
def convert_to_integer(v)
|
17
6
|
return v.to_i unless v.nil?
|
18
7
|
nil
|
@@ -24,6 +13,8 @@ module Believer
|
|
24
13
|
end
|
25
14
|
|
26
15
|
def convert_to_boolean(v)
|
16
|
+
return true if v.is_a?(TrueClass)
|
17
|
+
return false if v.is_a?(FalseClass)
|
27
18
|
return v.to_bool if v.respond_to?(:to_bool)
|
28
19
|
nil
|
29
20
|
end
|
data/lib/believer/version.rb
CHANGED
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
describe Believer::Delete do
|
4
4
|
|
5
5
|
it "create a valid delete statement" do
|
6
|
-
del = Believer::Delete.new(:record_class => Test::
|
6
|
+
del = Believer::Delete.new(:record_class => Test::Artist)
|
7
7
|
del = del.where(:id => 1)
|
8
|
-
del.to_cql.should == 'DELETE FROM
|
8
|
+
del.to_cql.should == 'DELETE FROM artists WHERE id = 1'
|
9
9
|
end
|
10
10
|
|
11
11
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Believer::Environment do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@original_env = Believer::Base.environment
|
7
|
+
Believer::Base.environment = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
after :each do
|
11
|
+
Believer::Base.environment = @original_env
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'load the Merb configuration' do
|
15
|
+
Env = Struct.new(:root, :environment, :logger)
|
16
|
+
|
17
|
+
Merb = Env.new(File.join(RSpec.configuration.test_files_dir, 'merb'), :development, nil)
|
18
|
+
env = Believer::Base.environment
|
19
|
+
env.class.should == Believer::Environment::MerbEnv
|
20
|
+
env.configuration[:host].should == 'merb.local'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'load the rails configuration' do
|
24
|
+
Env = Struct.new(:root, :env, :logger)
|
25
|
+
|
26
|
+
Rails = Env.new(File.join(RSpec.configuration.test_files_dir, 'rails'), :development, nil)
|
27
|
+
env = Believer::Base.environment
|
28
|
+
env.class.should == Believer::Environment::RailsEnv
|
29
|
+
env.configuration[:host].should == '123.456.789.0'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Believer::Insert do
|
4
4
|
|
5
5
|
it "create statement based on hash" do
|
6
|
-
insert = Believer::Insert.new(:record_class => Test::
|
7
|
-
insert.to_cql.should == "INSERT INTO
|
6
|
+
insert = Believer::Insert.new(:record_class => Test::Artist, :values => {:id => 1, :name => 'Beatles'})
|
7
|
+
insert.to_cql.should == "INSERT INTO artists (id, name) VALUES (1, 'Beatles')"
|
8
8
|
end
|
9
9
|
end
|
data/spec/believer/query_spec.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Believer::Query do
|
4
|
-
include Believer::Test::
|
4
|
+
include Believer::Test::TestRunLifeCycle
|
5
5
|
|
6
6
|
it 'create simple statement' do
|
7
|
-
q = Believer::Query.new(:record_class => Test::
|
8
|
-
q = q.select(:
|
9
|
-
select(:
|
10
|
-
where(:
|
11
|
-
where(:
|
12
|
-
order(:
|
7
|
+
q = Believer::Query.new(:record_class => Test::Album)
|
8
|
+
q = q.select(:name).
|
9
|
+
select(:artist).
|
10
|
+
where(:name => 'Revolver').
|
11
|
+
where(:release_date => Time.utc(2013)).
|
12
|
+
order(:name, :desc).
|
13
13
|
limit(10)
|
14
|
-
q.to_cql.should == "SELECT
|
14
|
+
q.to_cql.should == "SELECT name, artist FROM albums WHERE name = 'Revolver' AND release_date = '2013-01-01 00:00:00+0000' ORDER BY name DESC LIMIT 10"
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should behave like an Enumerable' do
|
18
|
-
|
19
|
-
@objects = Test::
|
20
|
-
{:
|
21
|
-
{:
|
22
|
-
{:
|
18
|
+
|
19
|
+
@objects = Test::Artist.create([
|
20
|
+
{:name => 'Beatles', :label => 'Apple'},
|
21
|
+
{:name => 'Jethro Tull', :label => 'Crysalis'},
|
22
|
+
{:name => 'Pink Floyd', :label => 'Epic'}
|
23
23
|
])
|
24
|
-
q = Believer::Query.new(:record_class => Test::
|
24
|
+
q = Believer::Query.new(:record_class => Test::Artist).where(:name => @objects.map {|o|o.name})
|
25
25
|
Enumerable.instance_methods(false).each do |enum_method|
|
26
26
|
q.respond_to?(enum_method.to_sym).should == true
|
27
27
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Believer::Relation do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@artists = []
|
7
|
+
@artists << Test::Artist.create(:name => 'Beatles', :label => 'Apple')
|
8
|
+
@artists << Test::Artist.create(:name => 'Jethro Tull', :label => 'Crysalis')
|
9
|
+
@artists << Test::Artist.create(:name => 'Pink Floyd', :label => 'Epic')
|
10
|
+
|
11
|
+
@albums = []
|
12
|
+
@albums << Test::Album.create(:artist_name => 'Beatles', :name => 'Help', :release_date => Time.utc(1965))
|
13
|
+
@albums << Test::Album.create(:artist_name => 'Beatles', :name => 'Revolver', :release_date => Time.utc(1966))
|
14
|
+
@albums << Test::Album.create(:artist_name => 'Beatles', :name => 'Abbey Road', :release_date => Time.utc(1969))
|
15
|
+
|
16
|
+
@albums << Test::Album.create(:artist_name => 'Pink Floyd', :name => 'Dark side of the moon', :release_date => Time.utc(1973))
|
17
|
+
@albums << Test::Album.create(:artist_name => 'Pink Floyd', :name => 'Wish you were here', :release_date => Time.utc(1975))
|
18
|
+
|
19
|
+
@songs = []
|
20
|
+
@songs << Test::Song.create(:artist_name => 'Pink Floyd', :album_name => 'Wish you were here', :name => 'Have a cigar')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
it "report correct size of one to many relation" do
|
25
|
+
a = Test::Artist.where(:name => 'Beatles').first
|
26
|
+
a.albums.size.should == 3
|
27
|
+
end
|
28
|
+
|
29
|
+
it "one to one relation" do
|
30
|
+
@songs[0].album.should == Test::Album.where(:artist_name => 'Pink Floyd', :name => 'Wish you were here').first
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,28 +10,14 @@ unless ENV['COVERAGE'] == 'no'
|
|
10
10
|
end
|
11
11
|
|
12
12
|
require 'believer'
|
13
|
-
|
14
|
-
require 'believer/test/rspec/test_run_life_cycle'
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
require 'believer/test/test_run_life_cycle'
|
18
14
|
|
19
15
|
Dir[File.expand_path('../support/*.rb', __FILE__)].each {|f| require f}
|
20
16
|
|
21
|
-
|
22
17
|
setup_database
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#end
|
30
|
-
#
|
31
|
-
#class Person < Cql::Model
|
32
|
-
# primary_key :id
|
33
|
-
#
|
34
|
-
# column :first_name
|
35
|
-
# column :last_name
|
36
|
-
# column :birth_date, column_name: :dob
|
37
|
-
#end
|
19
|
+
RSpec.configure do |c|
|
20
|
+
c.add_setting :test_files_dir, :default => File.expand_path('../test_files/', __FILE__)
|
21
|
+
end
|
22
|
+
|
23
|
+
File.expand_path('../test_files/', __FILE__)
|
@@ -8,7 +8,7 @@ def setup_database
|
|
8
8
|
connection.use(env.connection_configuration[:keyspace])
|
9
9
|
rescue Cql::QueryError
|
10
10
|
|
11
|
-
env.create_keyspace(connection)
|
11
|
+
env.create_keyspace({}, connection)
|
12
12
|
connection.use(env.connection_configuration[:keyspace])
|
13
13
|
|
14
14
|
Test.classes.each do |cl|
|
@@ -1,34 +1,37 @@
|
|
1
1
|
|
2
2
|
module Test
|
3
3
|
|
4
|
-
class
|
5
|
-
|
6
|
-
column :speed, :type => :integer
|
7
|
-
primary_key :computer_id
|
8
|
-
end
|
9
|
-
|
10
|
-
class Computer < Believer::Base
|
11
|
-
include Believer::Owner
|
4
|
+
class Artist < Believer::Base
|
5
|
+
include Believer::Relation
|
12
6
|
|
7
|
+
column :name
|
8
|
+
column :label
|
13
9
|
|
14
|
-
|
15
|
-
column :brand, :type => :string
|
16
|
-
column :production_date, :type => :timestamp
|
10
|
+
primary_key :name
|
17
11
|
|
18
|
-
|
12
|
+
has_some :albums, :class => 'Test::Album', :key => :name, :foreign_key => :artist_name
|
13
|
+
end
|
19
14
|
|
20
|
-
|
15
|
+
class Album < Believer::Base
|
16
|
+
column :artist_name
|
17
|
+
column :name
|
18
|
+
column :release_date, :type => :timestamp
|
21
19
|
|
20
|
+
primary_key :artist_name, :name
|
22
21
|
end
|
23
22
|
|
24
|
-
class
|
25
|
-
|
26
|
-
column :event_type, :type => :integer
|
27
|
-
column :time, :type => :integer, :key => true
|
28
|
-
column :description, :type => :string
|
23
|
+
class Song < Believer::Base
|
24
|
+
include Believer::Relation
|
29
25
|
|
30
|
-
|
26
|
+
column :artist_name
|
27
|
+
column :album_name
|
28
|
+
column :name
|
29
|
+
column :track_number, :type => :integer
|
30
|
+
column :data, :cql_type => :blob
|
31
31
|
|
32
|
+
primary_key :artist_name, :album_name, :name
|
33
|
+
|
34
|
+
has_single :album, :class => 'Test::Album', :key => [:artist_name, :album_name], :foreign_key => [:artist_name, :name]
|
32
35
|
end
|
33
36
|
|
34
37
|
class Person < Believer::Base
|
@@ -41,8 +44,18 @@ module Test
|
|
41
44
|
|
42
45
|
end
|
43
46
|
|
47
|
+
class Event < Believer::Base
|
48
|
+
column :computer_id, :type => :integer
|
49
|
+
column :event_type, :type => :integer
|
50
|
+
column :time, :type => :integer, :key => true
|
51
|
+
column :description, :type => :string
|
52
|
+
|
53
|
+
primary_key [:computer_id, :event_type], :time
|
54
|
+
|
55
|
+
end
|
56
|
+
|
44
57
|
class Environment < Believer::Environment::BaseEnv
|
45
|
-
def
|
58
|
+
def configuration
|
46
59
|
{:host => '127.0.0.1', :keyspace => 'believer_test_space'}
|
47
60
|
end
|
48
61
|
end
|
@@ -52,7 +65,7 @@ module Test
|
|
52
65
|
end
|
53
66
|
|
54
67
|
Believer::Base.environment = test_environment
|
55
|
-
CLASSES = [
|
68
|
+
CLASSES = [Artist, Album, Song, Event, Person]
|
56
69
|
#CLASSES.each {|cl| cl.environment = test_environment}
|
57
70
|
|
58
71
|
def self.classes
|