ork 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,155 @@
1
+ require_relative '../model'
2
+ require_relative 'finders'
3
+
4
+ module Ork
5
+ module Document
6
+ attr_accessor :id
7
+
8
+ def self.included(klass)
9
+ klass.send(:include, Ork::Model)
10
+ klass.extend(Ork::Model::Finders)
11
+ end
12
+
13
+ # Check for equality by doing the following assertions:
14
+ #
15
+ # 1. That the passed model is of the same type.
16
+ # 2. That they represent the same RObject id.
17
+ #
18
+ def ==(other)
19
+ other.kind_of?(model) && other.id == id
20
+ end
21
+ alias :eql? :==
22
+
23
+ def new?
24
+ !id
25
+ end
26
+
27
+ def embeddable?
28
+ false
29
+ end
30
+
31
+ # Pretty print for the model
32
+ #
33
+ # Example:
34
+ #
35
+ # User.new(name: 'John').inspect
36
+ # # => #<User:6kS5VHNbaed9h7gFLnVg5lmO4U7 {:name=>"John"}>
37
+ def inspect
38
+ "#<#{model}:#{id || 'nil'} #{attributes.inspect}>"
39
+ end
40
+
41
+ # Update the model attributes and call save.
42
+ #
43
+ # Example:
44
+ #
45
+ # User[1].update(:name => "John")
46
+ #
47
+ # # It's the same as:
48
+ #
49
+ # u = User[1]
50
+ # u.update_attributes(:name => "John")
51
+ # u.save
52
+ #
53
+ def update(attributes)
54
+ update_attributes(attributes)
55
+ save
56
+ end
57
+
58
+ # Persist the model attributes and update indices and unique
59
+ # indices.
60
+ #
61
+ # Example:
62
+ #
63
+ # class User
64
+ # include Ork::Document
65
+ #
66
+ # attribute :name
67
+ # end
68
+ #
69
+ # u = User.new(:name => "John").save
70
+ # # => #<User:6kS5VHNbaed9h7gFLnVg5lmO4U7 {:name=>"John"}>
71
+ #
72
+ def save
73
+ __robject.content_type = 'application/json'
74
+ __robject.data = __persist_attributes
75
+
76
+ __check_unique_indices
77
+ __update_indices
78
+ __robject.store
79
+
80
+ @id = __robject.key
81
+
82
+ self
83
+ end
84
+
85
+ # Preload all the attributes of this model from Riak.
86
+ def reload
87
+ new? ? self : self.load!(@id)
88
+ end
89
+
90
+ # Delete the model
91
+ def delete
92
+ __robject.delete unless new?
93
+ freeze
94
+ rescue Riak::FailedRequest
95
+ false
96
+ end
97
+
98
+ protected
99
+
100
+ # Overwrite attributes with the persisted attributes in Riak.
101
+ #
102
+ def load!(id)
103
+ self.__robject.key = id
104
+ __load_robject! id, @__robject.reload(force: true)
105
+ end
106
+
107
+ # Transform a RObject returned by Riak into a Ork::Document.
108
+ #
109
+ def __load_robject!(id, robject)
110
+ @id = id
111
+ @__robject = robject
112
+ @attributes = {}
113
+ @embedding = {}
114
+ @_memo = {}
115
+
116
+ data = @__robject.data
117
+ embedded_data = {}
118
+
119
+ model.embedding.each do |embedded|
120
+ if d = data.delete(embedded.to_s)
121
+ embedded_data[embedded] = d
122
+ end
123
+ end
124
+
125
+ update_attributes data
126
+ update_embedded_attributes embedded_data
127
+
128
+ self
129
+ end
130
+
131
+ # Build the secondary indices of this object
132
+ def __update_indices
133
+ model.indices.values.each do |index|
134
+ __robject.indexes[index.riak_name] = index.value_from(attributes)
135
+ end
136
+ end
137
+
138
+ # Look up into Riak for repeated values on unique attributes
139
+ def __check_unique_indices
140
+ model.uniques.each do |uniq|
141
+ if value = attributes[uniq]
142
+ index = model.indices[uniq]
143
+ records = model.bucket.get_index(index.riak_name, value)
144
+ unless records.empty? || records == [self.id]
145
+ raise Ork::UniqueIndexViolation, "#{uniq} is not unique"
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def __robject
152
+ @__robject ||= model.bucket.new
153
+ end
154
+ end
155
+ end
@@ -7,13 +7,10 @@ module Ork::Model
7
7
  #
8
8
  # u = User.create
9
9
  # u == User[u.id]
10
- # # => true
10
+ # # => true
11
11
  #
12
12
  def [](id)
13
- new.send(:load!, id) if exist?(id)
14
- rescue Riak::FailedRequest => e
15
- raise e unless e.not_found?
16
- nil
13
+ load_key(id) if exist?(id)
17
14
  end
18
15
 
19
16
  # Check if the ID exists.
@@ -23,22 +20,57 @@ module Ork::Model
23
20
  alias :exists? :exist?
24
21
 
25
22
  # Find all documents in the Document's bucket and return them.
26
- # @overload list()
27
- # Get all documents and return them in an array.
28
- # @param [Hash] options options to be passed to the
29
- # underlying {Bucket#keys} method.
30
23
  # @return [Array<Document>] all found documents in the bucket
31
24
  #
32
25
  # @Note: This operation is incredibly expensive and should not
33
26
  # be used in production applications.
34
27
  #
35
28
  def all
36
- bucket.keys.inject([]) do |acc, k|
37
- obj = self[k]
38
- obj ? acc << obj : acc
39
- end
29
+ load_robjects bucket.get_many(bucket.keys)
40
30
  end
41
31
  alias :list :all
42
32
 
33
+ # Find values in indexed fields.
34
+ #
35
+ # Example:
36
+ #
37
+ # class User
38
+ # include Ork::Document
39
+ #
40
+ # attribute :name
41
+ # index :name
42
+ # end
43
+ #
44
+ # u = User.create(name: 'John')
45
+ # User.find(name: 'John').include?(u)
46
+ # # => true
47
+ #
48
+ # User.find(name: 'Mike').include?(u)
49
+ # # => false
50
+ #
51
+ # Note: If the key was not defined, an
52
+ # `Ork::IndexNotFound` exception is raised.
53
+ #
54
+ def find(by_index, value)
55
+ raise Ork::IndexNotFound unless indices.has_key? by_index
56
+
57
+ index = indices[by_index]
58
+ load_robjects bucket.get_many(bucket.get_index(index.riak_name, value))
59
+ end
60
+
61
+ private
62
+
63
+ def load_key(id)
64
+ new.send(:load!, id)
65
+ rescue Riak::FailedRequest => e
66
+ raise e unless e.not_found?
67
+ end
68
+
69
+ def load_robjects(robjects)
70
+ robjects.map do |id, robject|
71
+ new.send(:__load_robject!, id, robject)
72
+ end
73
+ end
74
+
43
75
  end
44
76
  end
@@ -0,0 +1,20 @@
1
+ module Ork::Model
2
+ class Index
3
+
4
+ def initialize(name, type = 'bin')
5
+ @name, @type = name, type
6
+ end
7
+
8
+ # Index name in riak format
9
+ def riak_name
10
+ "#@name\_#@type"
11
+ end
12
+
13
+ # Take the attributes needed by the index.
14
+ # It's best to normalize or encode any
15
+ # user-supplied data before using it as an index
16
+ def value_from(attributes)
17
+ Set[attributes[@name]]
18
+ end
19
+ end
20
+ end
data/lib/ork/model.rb CHANGED
@@ -1,16 +1,13 @@
1
1
  require_relative 'model/class_methods'
2
2
  require_relative 'model/associations'
3
- require_relative 'model/finders'
4
3
 
5
4
  module Ork
6
5
  module Model
7
- attr_reader :attributes, :id
8
- attr_writer :id
6
+ attr_reader :attributes, :embedding
9
7
 
10
8
  def self.included(klass)
11
9
  klass.extend(Ork::Model::ClassMethods)
12
10
  klass.extend(Ork::Model::Associations)
13
- klass.extend(Ork::Model::Finders)
14
11
  end
15
12
 
16
13
  # Initialize a model using a dictionary of attributes.
@@ -21,50 +18,9 @@ module Ork
21
18
  #
22
19
  def initialize(atts = {})
23
20
  @attributes = {}
21
+ @embedding = {}
24
22
  @_memo = {}
25
- update_attributes(atts)
26
- end
27
-
28
- # Check for equality by doing the following assertions:
29
- #
30
- # 1. That the passed model is of the same type.
31
- # 2. That they represent the same RObject id.
32
- #
33
- def ==(other)
34
- other.kind_of?(model) && other.id == id
35
- end
36
-
37
- alias :eql? :==
38
-
39
- def new?
40
- !id
41
- end
42
-
43
- # Pretty print for the model
44
- #
45
- # Example:
46
- #
47
- # User.new(name: 'John').inspect
48
- # # => #<User:6kS5VHNbaed9h7gFLnVg5lmO4U7 {:name=>"John"}>
49
- def inspect
50
- "#<#{model}:#{id || 'nil'} #{attributes.inspect}>"
51
- end
52
-
53
- # Update the model attributes and call save.
54
- #
55
- # Example:
56
- #
57
- # User[1].update(:name => "John")
58
- #
59
- # # It's the same as:
60
- #
61
- # u = User[1]
62
- # u.update_attributes(:name => "John")
63
- # u.save
64
- #
65
- def update(attributes)
66
- update_attributes(attributes)
67
- save
23
+ update_attributes(model.defaults.merge(atts))
68
24
  end
69
25
 
70
26
  # Write the dictionary of key-value pairs to the model.
@@ -74,77 +30,40 @@ module Ork
74
30
  atts.each { |att, val| send(:"#{att}=", val) }
75
31
  end
76
32
 
77
- # Delete the model
78
- def delete
79
- __robject.delete unless new?
80
- freeze
81
- rescue Riak::FailedRequest
82
- false
83
- end
84
-
85
- # Persist the model attributes and update indices and unique
86
- # indices.
87
- #
88
- # If the model is not valid, nil is returned. Otherwise, the
89
- # persisted model is returned.
90
- #
91
- # Example:
92
- #
93
- # class User
94
- # include Ork::Model
95
- #
96
- # attribute :name
97
- #
98
- # def validate
99
- # assert_present :name
100
- # end
101
- # end
102
- #
103
- # User.new(:name => nil).save
104
- # # => nil
105
- #
106
- # u = User.new(:name => "John").save
107
- # # => #<User:6kS5VHNbaed9h7gFLnVg5lmO4U7 {:name=>"John"}>
108
- #
109
- def save
110
- # FIXME: Work with validations, scrivener or hatch?
111
- # save! if valid?
112
- save!
113
- end
114
-
115
- # Saves the model without checking for validity. Refer to
116
- # `Model#save` for more details.
117
- def save!
118
- __save__
119
- end
120
-
121
- # Preload all the attributes of this model from Riak.
122
- def reload
123
- new? ? self : self.load!(@id)
33
+ # Writhe the dictionary of key-value pairs of embedded objects.
34
+ #
35
+ def update_embedded_attributes(atts)
36
+ atts.each do |att, val|
37
+ if val.is_a? Array
38
+ val.each do |object_atts|
39
+ model = Ork::Utils.const(self.class, object_atts.delete('_type'))
40
+ send(:"add_#{att}", model.new(object_atts))
41
+ end
42
+ else
43
+ model = Ork::Utils.const(self.class, val.delete('_type'))
44
+ send(:"#{att}=", model.new(val))
45
+ end
46
+ end
124
47
  end
125
48
 
126
49
  protected
127
50
 
128
- def load!(id)
129
- @id = self.__robject.key = id
130
- @__robject = @__robject.reload(force: true)
131
- update_attributes(@__robject.data)
132
-
133
- self
134
- end
135
-
136
- # Persist the object in Riak database
137
- def __save__
138
- __robject.content_type = 'application/json'
139
- __robject.data = @attributes.merge('_type' => model.name)
140
- __robject.store
141
- @id = __robject.key
142
-
143
- self
144
- end
145
-
146
- def __robject
147
- @__robject ||= model.bucket.new
51
+ def __persist_attributes
52
+ attributes = @attributes.merge('_type' => model.name)
53
+ attributes.delete(model.__parent_key) if model.respond_to? :__parent_key
54
+
55
+ model.embedding.each do |embedded|
56
+ object = self.send(embedded)
57
+ unless object.nil?
58
+ attributes[embedded] = if object.is_a? Array
59
+ object.map{|o| o.send :__persist_attributes}
60
+ else
61
+ object.__persist_attributes
62
+ end
63
+ end
64
+ end
65
+
66
+ attributes
148
67
  end
149
68
 
150
69
  def model
data/lib/ork/utils.rb CHANGED
@@ -11,7 +11,7 @@ module Ork
11
11
  # Example:
12
12
  #
13
13
  # class Comment
14
- # include Ork::Model
14
+ # include Ork::Document
15
15
  #
16
16
  # reference :user, User # NameError undefined constant User.
17
17
  # end
@@ -20,7 +20,7 @@ module Ork
20
20
  # simply use a Symbol.
21
21
  #
22
22
  # class Comment
23
- # include Ork::Model
23
+ # include Ork::Document
24
24
  #
25
25
  # reference :user, :User
26
26
  # end
@@ -28,6 +28,7 @@ module Ork
28
28
  def self.const(context, name)
29
29
  case name
30
30
  when Symbol then context.const_get(name)
31
+ when String then context.const_get(name.to_sym)
31
32
  else name
32
33
  end
33
34
  end
data/lib/ork.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  require 'ork/connection'
2
2
  require 'ork/model'
3
+ require 'ork/model/document'
4
+ require 'ork/embedded'
3
5
  require 'ork/utils'
6
+ require 'ork/errors'
4
7
  require "riak"
5
8
 
6
9
  module Ork
7
- class Error < StandardError; end
8
10
 
9
11
  def self.conn
10
12
  @conn ||= Ork::Connection.new
data/ork.gemspec CHANGED
@@ -1,12 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'ork'
3
- s.version = '0.0.1'
3
+ s.version = '0.1.0'
4
4
  s.date = Time.now.strftime('%Y-%m-%d')
5
5
  s.summary = 'Ruby modeling layer for Riak.'
6
6
  s.description = 'Ork is a small Ruby modeling layer for Riak, inspired by Ohm.'
7
7
  s.authors = ['Emiliano Mancuso']
8
8
  s.email = ['emiliano.mancuso@gmail.com']
9
- s.homepage = 'http://github.com/eMancu/ork'
9
+ s.homepage = 'http://github.com/emancu/ork'
10
10
  s.license = 'MIT'
11
11
 
12
12
  s.files = Dir[
data/rakefile CHANGED
@@ -1,31 +1,8 @@
1
- require "rake/testtask"
2
-
3
1
  task :default => :test
4
2
 
5
- desc 'Start riak test server'
6
- task :start do
7
- require File.expand_path("./test/helper", File.dirname(__FILE__))
8
- puts '..:: Starting riak test server ::..'
9
- test_server.start
10
- end
11
-
12
3
  desc 'Run tests'
13
- task :test => [:start] do
14
- Dir["test/*.rb"].each { |file| load file unless file =~ /helper.rb/ }
15
- end
16
-
17
- desc 'Stop riak test server'
18
- task :stop => [:start] do
19
- puts '..:: Stop riak test server ::..'
20
-
21
- flush_db!
22
- test_server.stop
23
- end
24
-
25
- at_exit do
26
- puts '..:: Stop riak test server ::..'
27
- sleep 1
4
+ task :test do
5
+ require File.expand_path("./test/helper", File.dirname(__FILE__))
28
6
 
29
- flush_db!
30
- test_server.stop
7
+ Dir["test/**/*_test.rb"].each { |file| load file }
31
8
  end
data/test/helper.rb CHANGED
@@ -1,41 +1,31 @@
1
1
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
2
 
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+
6
+ SimpleCov.start do
7
+ project_name "Ork"
8
+ command_name "Protest"
9
+
10
+ add_filter "/test/"
11
+ end
12
+
3
13
  require "rubygems"
4
14
  require "protest"
5
15
  require "ork"
6
16
 
7
- # require 'coveralls'
8
- # Coveralls.wear!
9
-
10
17
  Riak.disable_list_keys_warnings = true
11
-
12
- require 'riak/test_server'
13
-
14
- def test_server
15
- $test_server ||= begin
16
- require 'toml'
17
- path = File.expand_path("../test_riak_server.toml", __FILE__)
18
- config = TOML.load_file(path, symbolize_keys: true)
19
-
20
-
21
- server = Riak::TestServer.create(root: config[:root],
22
- source: config[:source],
23
- min_port: config[:min_port])
24
-
25
-
26
- Ork.connect(:test, {
27
- http_port: server.http_port,
28
- pb_port: server.pb_port
29
- })
30
-
31
- server
32
- rescue => e
33
- puts ("Can't run Ork tests without the test server.\n" +
34
- "Specify the location of your Riak installation in test/test_riak_server.toml\n" +
35
- e.inspect)
36
- end
18
+ Protest.report_with(:progress)
19
+
20
+ def randomize_bucket_name(klass)
21
+ klass.bucket_name= [
22
+ 'test',
23
+ klass.to_s,
24
+ Time.now.to_i,
25
+ rand(36**10).to_s(36)
26
+ ].join('-')
37
27
  end
38
28
 
39
- def flush_db!
40
- test_server.drop
29
+ def deny(condition, message="Expected condition to be unsatisfied")
30
+ assert !condition, message
41
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emiliano Mancuso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-08 00:00:00.000000000 Z
11
+ date: 2013-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: riak-client
@@ -62,21 +62,20 @@ files:
62
62
  - README.md
63
63
  - rakefile
64
64
  - lib/ork/connection.rb
65
+ - lib/ork/embedded.rb
66
+ - lib/ork/errors.rb
65
67
  - lib/ork/model/associations.rb
66
68
  - lib/ork/model/class_methods.rb
69
+ - lib/ork/model/document.rb
67
70
  - lib/ork/model/finders.rb
71
+ - lib/ork/model/index.rb
68
72
  - lib/ork/model.rb
69
73
  - lib/ork/utils.rb
70
74
  - lib/ork/version.rb
71
75
  - lib/ork.rb
72
76
  - ork.gemspec
73
- - test/finders.rb
74
77
  - test/helper.rb
75
- - test/model.rb
76
- - test/reference.rb
77
- - test/test_riak_server.toml
78
- - test/test_riak_server.toml.example
79
- homepage: http://github.com/eMancu/ork
78
+ homepage: http://github.com/emancu/ork
80
79
  licenses:
81
80
  - MIT
82
81
  metadata: {}
@@ -101,9 +100,4 @@ signing_key:
101
100
  specification_version: 4
102
101
  summary: Ruby modeling layer for Riak.
103
102
  test_files:
104
- - test/finders.rb
105
103
  - test/helper.rb
106
- - test/model.rb
107
- - test/reference.rb
108
- - test/test_riak_server.toml
109
- - test/test_riak_server.toml.example
data/test/finders.rb DELETED
@@ -1,43 +0,0 @@
1
- require_relative 'helper'
2
-
3
- class Human
4
- include Ork::Model
5
-
6
- attribute :name
7
- attribute :last_name
8
-
9
- unique :name
10
- index :last_name
11
- end
12
-
13
- Protest.describe 'Finders' do
14
- setup do
15
- @human1 = Human.create(name: 'Tony', last_name: 'Montana')
16
- @human2 = Human.create(name: 'Cacho', last_name: 'Castaña')
17
- end
18
-
19
- teardown do
20
- flush_db!
21
- end
22
-
23
- test 'retrieve an object by id' do
24
- assert_equal @human1, Human[@human1.id]
25
- end
26
-
27
- test 'return nil when the id does not belong to an object of this bucket' do
28
- assert_equal nil, Human['not_an_id']
29
- end
30
-
31
- test 'if exist an object with the id' do
32
- assert !Human.exist?(nil)
33
- assert !Human.exist?('not_an_id')
34
- assert Human.exist?(@human1.id)
35
- end
36
-
37
- test 'list all the objects' do
38
- assert_equal 2, Human.list.size
39
- assert Human.list.include?(@human1)
40
- assert Human.list.include?(@human2)
41
- end
42
-
43
- end