cequel 0.0.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/lib/cequel.rb +16 -0
  2. data/lib/cequel/batch.rb +58 -0
  3. data/lib/cequel/cql_row_specification.rb +22 -0
  4. data/lib/cequel/data_set.rb +346 -0
  5. data/lib/cequel/errors.rb +4 -0
  6. data/lib/cequel/keyspace.rb +106 -0
  7. data/lib/cequel/model.rb +95 -0
  8. data/lib/cequel/model/associations.rb +120 -0
  9. data/lib/cequel/model/callbacks.rb +32 -0
  10. data/lib/cequel/model/class_internals.rb +48 -0
  11. data/lib/cequel/model/column.rb +20 -0
  12. data/lib/cequel/model/dictionary.rb +202 -0
  13. data/lib/cequel/model/dirty.rb +53 -0
  14. data/lib/cequel/model/dynamic.rb +31 -0
  15. data/lib/cequel/model/errors.rb +13 -0
  16. data/lib/cequel/model/inheritable.rb +48 -0
  17. data/lib/cequel/model/instance_internals.rb +23 -0
  18. data/lib/cequel/model/local_association.rb +42 -0
  19. data/lib/cequel/model/magic.rb +79 -0
  20. data/lib/cequel/model/mass_assignment_security.rb +21 -0
  21. data/lib/cequel/model/naming.rb +17 -0
  22. data/lib/cequel/model/observer.rb +42 -0
  23. data/lib/cequel/model/persistence.rb +173 -0
  24. data/lib/cequel/model/properties.rb +143 -0
  25. data/lib/cequel/model/railtie.rb +33 -0
  26. data/lib/cequel/model/remote_association.rb +40 -0
  27. data/lib/cequel/model/scope.rb +362 -0
  28. data/lib/cequel/model/scoped.rb +50 -0
  29. data/lib/cequel/model/subclass_internals.rb +45 -0
  30. data/lib/cequel/model/timestamps.rb +52 -0
  31. data/lib/cequel/model/translation.rb +17 -0
  32. data/lib/cequel/model/validations.rb +50 -0
  33. data/lib/cequel/new_relic_instrumentation.rb +22 -0
  34. data/lib/cequel/row_specification.rb +63 -0
  35. data/lib/cequel/statement.rb +23 -0
  36. data/lib/cequel/version.rb +3 -0
  37. data/spec/environment.rb +3 -0
  38. data/spec/examples/data_set_spec.rb +382 -0
  39. data/spec/examples/keyspace_spec.rb +63 -0
  40. data/spec/examples/model/associations_spec.rb +109 -0
  41. data/spec/examples/model/callbacks_spec.rb +79 -0
  42. data/spec/examples/model/dictionary_spec.rb +413 -0
  43. data/spec/examples/model/dirty_spec.rb +39 -0
  44. data/spec/examples/model/dynamic_spec.rb +41 -0
  45. data/spec/examples/model/inheritable_spec.rb +45 -0
  46. data/spec/examples/model/magic_spec.rb +199 -0
  47. data/spec/examples/model/mass_assignment_security_spec.rb +13 -0
  48. data/spec/examples/model/naming_spec.rb +9 -0
  49. data/spec/examples/model/observer_spec.rb +86 -0
  50. data/spec/examples/model/persistence_spec.rb +201 -0
  51. data/spec/examples/model/properties_spec.rb +81 -0
  52. data/spec/examples/model/scope_spec.rb +677 -0
  53. data/spec/examples/model/serialization_spec.rb +20 -0
  54. data/spec/examples/model/spec_helper.rb +12 -0
  55. data/spec/examples/model/timestamps_spec.rb +52 -0
  56. data/spec/examples/model/translation_spec.rb +23 -0
  57. data/spec/examples/model/validations_spec.rb +86 -0
  58. data/spec/examples/spec_helper.rb +9 -0
  59. data/spec/models/asset.rb +21 -0
  60. data/spec/models/asset_observer.rb +5 -0
  61. data/spec/models/blog.rb +14 -0
  62. data/spec/models/blog_posts.rb +6 -0
  63. data/spec/models/category.rb +9 -0
  64. data/spec/models/comment.rb +12 -0
  65. data/spec/models/photo.rb +5 -0
  66. data/spec/models/post.rb +88 -0
  67. data/spec/models/post_comments.rb +14 -0
  68. data/spec/models/post_observer.rb +43 -0
  69. data/spec/support/helpers.rb +26 -0
  70. data/spec/support/result_stub.rb +27 -0
  71. 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,9 @@
1
+ require File.expand_path('../../environment', __FILE__)
2
+
3
+ Dir.glob(File.expand_path('../../support/**/*.rb', __FILE__)).each do |file|
4
+ require file
5
+ end
6
+
7
+ RSpec.configure do |config|
8
+ config.include(Cequel::SpecSupport::Helpers)
9
+ 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
@@ -0,0 +1,5 @@
1
+ class AssetObserver < Cequel::Model::Observer
2
+ def before_save(asset)
3
+ asset.observed!(:before_save)
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ class Blog
2
+
3
+ include Cequel::Model
4
+ include Timestamps
5
+
6
+ key :id, :integer
7
+ column :name, :varchar
8
+ column :published, :boolean, :default => true
9
+
10
+ has_many :posts, :dependent => :destroy
11
+
12
+ default_scope limit(100)
13
+
14
+ end
@@ -0,0 +1,6 @@
1
+ class BlogPosts < Cequel::Model::Dictionary
2
+ key :blog_id, :int
3
+ maps :uuid => :int
4
+
5
+ self.default_batch_size = 2
6
+ end
@@ -0,0 +1,9 @@
1
+ class Category
2
+
3
+ include Cequel::Model
4
+ include Cequel::Model::Dynamic
5
+
6
+ key :id, :int
7
+ column :name, :text
8
+
9
+ end
@@ -0,0 +1,12 @@
1
+ class Comment
2
+ include Cequel::Model
3
+
4
+ key :id, :uuid
5
+ column :body, :text
6
+
7
+ private
8
+
9
+ def generate_key
10
+ SimpleUUID::UUID.new
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path('../asset', __FILE__)
2
+
3
+ class Photo < Asset
4
+ column :url, :varchar
5
+ end
@@ -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,14 @@
1
+ class PostComments < Cequel::Model::Dictionary
2
+ key :post_id, :int
3
+ maps :int => :text
4
+
5
+ private
6
+
7
+ def serialize_value(data)
8
+ data.to_json
9
+ end
10
+
11
+ def deserialize_value(post_id, json)
12
+ JSON.parse(json)
13
+ end
14
+ 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