ork 0.0.1 → 0.1.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.
@@ -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