believer 0.1.4 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|