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.
@@ -53,9 +53,9 @@ module Believer
53
53
  end
54
54
 
55
55
  cql << " FROM #{@record_class.table_name}"
56
- cql << " WHERE #{@wheres.map { |wc| "#{wc.to_cql}" }.join(' AND ')}" if @wheres && @wheres.any?
57
- cql << " #{@order_by.to_cql}" unless @order_by.nil?
58
- cql << " #{@limit_to.to_cql}" unless @limit_to.nil?
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 => @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
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Believer
2
2
  module Version
3
- VERSION = '0.1.4'
3
+ VERSION = '0.2'
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  module Believer
2
2
 
3
3
  class WhereClause
4
- include Values
4
+ include CqlHelper
5
5
 
6
6
  def initialize(*args)
7
7
  if args.any?
@@ -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::Computer)
6
+ del = Believer::Delete.new(:record_class => Test::Artist)
7
7
  del = del.where(:id => 1)
8
- del.to_cql.should == 'DELETE FROM computers WHERE id = 1'
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::Computer, :values => {:id => 1, :brand => 'Apple'})
7
- insert.to_cql.should == "INSERT INTO computers (id, brand) VALUES (1, 'Apple')"
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
@@ -1,27 +1,27 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Believer::Query do
4
- include Believer::Test::RSpec::TestRunLifeCycle
4
+ include Believer::Test::TestRunLifeCycle
5
5
 
6
6
  it 'create simple statement' do
7
- q = Believer::Query.new(:record_class => Test::Computer)
8
- q = q.select(:id).
9
- select(:brand).
10
- where(:brand => 'Atari').
11
- where(:production_date => Time.utc(2013)).
12
- order(:id, :desc).
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 id, brand FROM computers WHERE brand = 'Atari' AND production_date = '2013-01-01 00:00:00+0000' ORDER BY id DESC LIMIT 10"
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
- puts Test::Computer.environment
19
- @objects = Test::Computer.create([
20
- {:id => 1, :brand => 'Dell'},
21
- {:id => 2, :brand => 'Apple'},
22
- {:id => 3, :brand => 'HP'}
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::Computer).where(:id => @objects.map {|o|o.id})
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
@@ -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
- #class Event < Cql::Model
25
- # primary_key :id
26
- #
27
- # column :location
28
- # column :date
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 Processor < Believer::Base
5
- column :computer_id, :type => :integer
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
- column :id, :type => :integer
15
- column :brand, :type => :string
16
- column :production_date, :type => :timestamp
10
+ primary_key :name
17
11
 
18
- primary_key :id
12
+ has_some :albums, :class => 'Test::Album', :key => :name, :foreign_key => :artist_name
13
+ end
19
14
 
20
- has_some :processors, :class => Processor
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 Event < Believer::Base
25
- column :computer_id, :type => :integer
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
- primary_key [:computer_id, :event_type], :time
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 connection_configuration
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 = [Processor, Computer, Event, Person]
68
+ CLASSES = [Artist, Album, Song, Event, Person]
56
69
  #CLASSES.each {|cl| cl.environment = test_environment}
57
70
 
58
71
  def self.classes