cequel 0.0.0 → 0.4.0
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/lib/cequel.rb +16 -0
- data/lib/cequel/batch.rb +58 -0
- data/lib/cequel/cql_row_specification.rb +22 -0
- data/lib/cequel/data_set.rb +346 -0
- data/lib/cequel/errors.rb +4 -0
- data/lib/cequel/keyspace.rb +106 -0
- data/lib/cequel/model.rb +95 -0
- data/lib/cequel/model/associations.rb +120 -0
- data/lib/cequel/model/callbacks.rb +32 -0
- data/lib/cequel/model/class_internals.rb +48 -0
- data/lib/cequel/model/column.rb +20 -0
- data/lib/cequel/model/dictionary.rb +202 -0
- data/lib/cequel/model/dirty.rb +53 -0
- data/lib/cequel/model/dynamic.rb +31 -0
- data/lib/cequel/model/errors.rb +13 -0
- data/lib/cequel/model/inheritable.rb +48 -0
- data/lib/cequel/model/instance_internals.rb +23 -0
- data/lib/cequel/model/local_association.rb +42 -0
- data/lib/cequel/model/magic.rb +79 -0
- data/lib/cequel/model/mass_assignment_security.rb +21 -0
- data/lib/cequel/model/naming.rb +17 -0
- data/lib/cequel/model/observer.rb +42 -0
- data/lib/cequel/model/persistence.rb +173 -0
- data/lib/cequel/model/properties.rb +143 -0
- data/lib/cequel/model/railtie.rb +33 -0
- data/lib/cequel/model/remote_association.rb +40 -0
- data/lib/cequel/model/scope.rb +362 -0
- data/lib/cequel/model/scoped.rb +50 -0
- data/lib/cequel/model/subclass_internals.rb +45 -0
- data/lib/cequel/model/timestamps.rb +52 -0
- data/lib/cequel/model/translation.rb +17 -0
- data/lib/cequel/model/validations.rb +50 -0
- data/lib/cequel/new_relic_instrumentation.rb +22 -0
- data/lib/cequel/row_specification.rb +63 -0
- data/lib/cequel/statement.rb +23 -0
- data/lib/cequel/version.rb +3 -0
- data/spec/environment.rb +3 -0
- data/spec/examples/data_set_spec.rb +382 -0
- data/spec/examples/keyspace_spec.rb +63 -0
- data/spec/examples/model/associations_spec.rb +109 -0
- data/spec/examples/model/callbacks_spec.rb +79 -0
- data/spec/examples/model/dictionary_spec.rb +413 -0
- data/spec/examples/model/dirty_spec.rb +39 -0
- data/spec/examples/model/dynamic_spec.rb +41 -0
- data/spec/examples/model/inheritable_spec.rb +45 -0
- data/spec/examples/model/magic_spec.rb +199 -0
- data/spec/examples/model/mass_assignment_security_spec.rb +13 -0
- data/spec/examples/model/naming_spec.rb +9 -0
- data/spec/examples/model/observer_spec.rb +86 -0
- data/spec/examples/model/persistence_spec.rb +201 -0
- data/spec/examples/model/properties_spec.rb +81 -0
- data/spec/examples/model/scope_spec.rb +677 -0
- data/spec/examples/model/serialization_spec.rb +20 -0
- data/spec/examples/model/spec_helper.rb +12 -0
- data/spec/examples/model/timestamps_spec.rb +52 -0
- data/spec/examples/model/translation_spec.rb +23 -0
- data/spec/examples/model/validations_spec.rb +86 -0
- data/spec/examples/spec_helper.rb +9 -0
- data/spec/models/asset.rb +21 -0
- data/spec/models/asset_observer.rb +5 -0
- data/spec/models/blog.rb +14 -0
- data/spec/models/blog_posts.rb +6 -0
- data/spec/models/category.rb +9 -0
- data/spec/models/comment.rb +12 -0
- data/spec/models/photo.rb +5 -0
- data/spec/models/post.rb +88 -0
- data/spec/models/post_comments.rb +14 -0
- data/spec/models/post_observer.rb +43 -0
- data/spec/support/helpers.rb +26 -0
- data/spec/support/result_stub.rb +27 -0
- metadata +125 -23
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe 'Serialization' do
|
4
|
+
let(:post) { Post.new(:id => 1, :title => 'Cequel') }
|
5
|
+
|
6
|
+
it 'should serialize to JSON' do
|
7
|
+
post.to_json.should ==
|
8
|
+
{'post' => {'id' => 1, 'title' => 'Cequel'}}.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should serialize to XML' do
|
12
|
+
post.to_xml.should == <<XML
|
13
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
14
|
+
<post>
|
15
|
+
<id type="integer">1</id>
|
16
|
+
<title>Cequel</title>
|
17
|
+
</post>
|
18
|
+
XML
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
require 'cequel/model'
|
3
|
+
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../models/**/*.rb')).each do |file|
|
5
|
+
require file
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.before :each do
|
10
|
+
Cequel::Model.keyspace.connection = connection
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Cequel::Model::Timestamps do
|
4
|
+
let!(:now) { Time.now }
|
5
|
+
before { Time.stub(:now).and_return now }
|
6
|
+
|
7
|
+
it 'should add created_at column' do
|
8
|
+
Blog.column_names.should include(:created_at)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should add updated_at column' do
|
12
|
+
Blog.column_names.should include(:updated_at)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should populate created_at and updated_at column on create' do
|
16
|
+
connection.should_receive(:execute).
|
17
|
+
with "INSERT INTO blogs (?) VALUES (?)",
|
18
|
+
['id', 'published', 'updated_at', 'created_at'],
|
19
|
+
[1, true, now, now]
|
20
|
+
Blog.create!(:id => 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should populate updated_at column on update' do
|
24
|
+
connection.stub(:execute).
|
25
|
+
with("SELECT * FROM blogs WHERE ? = ? LIMIT 1", :id, 1).
|
26
|
+
and_return result_stub(
|
27
|
+
:id => 1,
|
28
|
+
:name => 'Blogtime',
|
29
|
+
:created_at => now - 1.day,
|
30
|
+
:updated_at => now - 1.day
|
31
|
+
)
|
32
|
+
blog = Blog.find(1)
|
33
|
+
blog.name = 'Bloggy'
|
34
|
+
connection.should_receive(:execute).
|
35
|
+
with("UPDATE blogs SET ? = ?, ? = ? WHERE ? = ?", 'name', 'Bloggy', 'updated_at', now, :id, 1)
|
36
|
+
blog.save
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should not update updated_at when calling save with no dirty attributes' do
|
40
|
+
connection.stub(:execute).
|
41
|
+
with("SELECT * FROM blogs WHERE ? = ? LIMIT 1", :id, 1).
|
42
|
+
and_return result_stub(
|
43
|
+
:id => 1,
|
44
|
+
:name => 'Blogtime',
|
45
|
+
:created_at => now - 1.day,
|
46
|
+
:updated_at => now - 1.day
|
47
|
+
)
|
48
|
+
blog = Blog.find(1)
|
49
|
+
blog.save
|
50
|
+
blog.updated_at.should == now - 1.day
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Cequel::Model::Translation do
|
4
|
+
before do
|
5
|
+
I18n.backend.load_translations(
|
6
|
+
File.expand_path('../../../support/en.yml', __FILE__))
|
7
|
+
I18n.locale = 'en'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should translate model names' do
|
11
|
+
Post.model_name.human.should == 'Blog post'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should translate attribute names' do
|
15
|
+
Post.human_attribute_name(:title).should == 'Post title'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should translate error messages' do
|
19
|
+
post = Post.new(:id => 1, :require_title => true)
|
20
|
+
post.valid?
|
21
|
+
post.errors.full_messages.should include("Post title is a required field")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Cequel::Model::Validations do
|
4
|
+
describe '#valid?' do
|
5
|
+
it 'should be false if model is not valid' do
|
6
|
+
Post.new(:id => 1, :require_title => true).should_not be_valid
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should be true if model is valid' do
|
10
|
+
Post.new(:id => 1, :require_title => true, :title => 'Cequel').should be_valid
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#save' do
|
15
|
+
it 'should return false and not persist model if invalid' do
|
16
|
+
Post.new(:id => 1, :body => 'Cequel', :require_title => true).save.should be_false
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return true and persist model if valid' do
|
20
|
+
connection.should_receive(:execute).
|
21
|
+
with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
|
22
|
+
|
23
|
+
Post.new(:id => 1, :title => 'Cequel', :require_title => true).save.should be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#save!' do
|
28
|
+
it 'should raise error and not persist model if invalid' do
|
29
|
+
expect do
|
30
|
+
Post.new(:id => 1, :body => 'Cequel', :require_title => true).save!
|
31
|
+
end.to raise_error(Cequel::Model::RecordInvalid)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should persist model and return self if valid' do
|
35
|
+
connection.should_receive(:execute).
|
36
|
+
with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
|
37
|
+
|
38
|
+
post = Post.new(:id => 1, :title => 'Cequel', :require_title => true)
|
39
|
+
post.save!.should == post
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#update_attributes!' do
|
44
|
+
let(:post) do
|
45
|
+
connection.stub(:execute).with("SELECT * FROM posts WHERE ? = ? LIMIT 1", :id, 1).
|
46
|
+
and_return result_stub(:id => 1, :blog_id => 1, :title => 'Cequel')
|
47
|
+
Post.find(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should change attributes and save them if valid' do
|
51
|
+
connection.should_receive(:execute).
|
52
|
+
with "UPDATE posts SET ? = ? WHERE ? = ?", 'body', 'Cequel cequel', :id, 1
|
53
|
+
post.update_attributes!(:body => 'Cequel cequel')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should raise error if not valid' do
|
57
|
+
post.require_title = true
|
58
|
+
expect { post.update_attributes!(:title => nil) }.
|
59
|
+
to raise_error(Cequel::Model::RecordInvalid)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '::create!' do
|
64
|
+
it 'should raise RecordInvalid and not persist model if invalid' do
|
65
|
+
expect do
|
66
|
+
Post.create!(:id => 1, :body => 'Cequel', :require_title => true)
|
67
|
+
end.to raise_error(Cequel::Model::RecordInvalid)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should and return model if valid' do
|
71
|
+
connection.should_receive(:execute).
|
72
|
+
with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
|
73
|
+
|
74
|
+
Post.create!(:id => 1, :title => 'Cequel', :require_title => true).
|
75
|
+
title.should == 'Cequel'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'callbacks' do
|
80
|
+
it 'should call validation callbacks' do
|
81
|
+
post = Post.new(:id => 1)
|
82
|
+
post.valid?
|
83
|
+
post.should have_callback(:validation)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Asset
|
2
|
+
|
3
|
+
include Cequel::Model
|
4
|
+
|
5
|
+
key :id, :integer
|
6
|
+
column :class_name, :ascii
|
7
|
+
column :label, :varchar
|
8
|
+
column :checksum, :varchar
|
9
|
+
|
10
|
+
index_preference :checksum, :class_name
|
11
|
+
|
12
|
+
def observed!(callback)
|
13
|
+
@observed ||= []
|
14
|
+
@observed << callback
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_observed?(callback)
|
18
|
+
@observed.include?(callback)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/models/blog.rb
ADDED
data/spec/models/post.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
class Post
|
2
|
+
|
3
|
+
include Cequel::Model
|
4
|
+
|
5
|
+
key :id, :int
|
6
|
+
column :title, :varchar
|
7
|
+
column :body, :varchar
|
8
|
+
column :published, :boolean
|
9
|
+
column :author_name, :text
|
10
|
+
|
11
|
+
before_save :record_save_callback
|
12
|
+
before_create :record_create_callback
|
13
|
+
before_update :record_update_callback
|
14
|
+
before_destroy :record_destroy_callback
|
15
|
+
before_validation :record_validation_callback
|
16
|
+
|
17
|
+
belongs_to :blog
|
18
|
+
has_one :thumbnail, :class_name => 'Photo'
|
19
|
+
|
20
|
+
validates :title, :presence => true, :if => :require_title?
|
21
|
+
|
22
|
+
attr_protected :blog_id
|
23
|
+
|
24
|
+
attr_writer :require_title
|
25
|
+
|
26
|
+
def self.for_blog(blog_id)
|
27
|
+
where(:blog_id => blog_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_callback?(callback)
|
31
|
+
callbacks.include?(callback)
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_been_observed?(callback)
|
35
|
+
observed.include?(callback)
|
36
|
+
end
|
37
|
+
|
38
|
+
def observed!(callback)
|
39
|
+
observed << callback
|
40
|
+
end
|
41
|
+
|
42
|
+
def require_title?
|
43
|
+
!!@require_title
|
44
|
+
end
|
45
|
+
|
46
|
+
def author_name=(author_name)
|
47
|
+
write_attribute(:author_name, author_name.try(:reverse))
|
48
|
+
end
|
49
|
+
|
50
|
+
def author_name
|
51
|
+
read_attribute(:author_name).try(:reverse)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def record_save_callback
|
57
|
+
record_callback(:save)
|
58
|
+
end
|
59
|
+
|
60
|
+
def record_create_callback
|
61
|
+
record_callback(:create)
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_update_callback
|
65
|
+
record_callback(:update)
|
66
|
+
end
|
67
|
+
|
68
|
+
def record_destroy_callback
|
69
|
+
record_callback(:destroy)
|
70
|
+
end
|
71
|
+
|
72
|
+
def record_validation_callback
|
73
|
+
record_callback(:validation)
|
74
|
+
end
|
75
|
+
|
76
|
+
def record_callback(callback)
|
77
|
+
callbacks << callback
|
78
|
+
end
|
79
|
+
|
80
|
+
def callbacks
|
81
|
+
@callbacks ||= Set[]
|
82
|
+
end
|
83
|
+
|
84
|
+
def observed
|
85
|
+
@observed ||= Set[]
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class PostObserver < Cequel::Model::Observer
|
2
|
+
|
3
|
+
def before_create(post)
|
4
|
+
post.observed!(:before_create)
|
5
|
+
end
|
6
|
+
|
7
|
+
def after_create(post)
|
8
|
+
post.observed!(:after_create)
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_save(post)
|
12
|
+
post.observed!(:before_save)
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_save(post)
|
16
|
+
post.observed!(:after_save)
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_update(post)
|
20
|
+
post.observed!(:before_update)
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_update(post)
|
24
|
+
post.observed!(:after_update)
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_destroy(post)
|
28
|
+
post.observed!(:before_destroy)
|
29
|
+
end
|
30
|
+
|
31
|
+
def after_destroy(post)
|
32
|
+
post.observed!(:after_destroy)
|
33
|
+
end
|
34
|
+
|
35
|
+
def before_validation(post)
|
36
|
+
post.observed!(:before_validation)
|
37
|
+
end
|
38
|
+
|
39
|
+
def after_validation(post)
|
40
|
+
post.observed!(:after_validation)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cequel
|
2
|
+
|
3
|
+
module SpecSupport
|
4
|
+
|
5
|
+
Connection = Object.new
|
6
|
+
|
7
|
+
module Helpers
|
8
|
+
|
9
|
+
def result_stub(*results)
|
10
|
+
ResultStub.new(results)
|
11
|
+
end
|
12
|
+
|
13
|
+
def connection
|
14
|
+
Connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def cequel
|
18
|
+
@cequel ||= Cequel::Keyspace.new({}).tap do |keyspace|
|
19
|
+
keyspace.connection = connection
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|