ork 0.0.1
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.
- checksums.yaml +7 -0
- data/README.md +45 -0
- data/lib/ork/connection.rb +28 -0
- data/lib/ork/model/associations.rb +138 -0
- data/lib/ork/model/class_methods.rb +89 -0
- data/lib/ork/model/finders.rb +44 -0
- data/lib/ork/model.rb +154 -0
- data/lib/ork/utils.rb +35 -0
- data/lib/ork/version.rb +3 -0
- data/lib/ork.rb +36 -0
- data/ork.gemspec +24 -0
- data/rakefile +31 -0
- data/test/finders.rb +43 -0
- data/test/helper.rb +41 -0
- data/test/model.rb +139 -0
- data/test/reference.rb +73 -0
- data/test/test_riak_server.toml +15 -0
- data/test/test_riak_server.toml.example +15 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d614293fd63e1d8d2a8de96cfb92ad40c09ebdc5
|
4
|
+
data.tar.gz: f487220a7969011adfe79399feb8ddcecc0d7187
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 39953ee12661946b11f55e278253d8726871ff9b8d3eb3c389e9d2ba447460f8c959048c99b9d6ade5a7992974297bc7bc78d8b55ba0e7c9366c82b6492fb9ba
|
7
|
+
data.tar.gz: 7b565b3a8ff7901cc7457464a335a54b0c5c1a1f53cf159f87815e4402a250d168bbd275da6f7a4a12a8621c59be86e53e08cf5066e10c9eb5428ea632a6cc03
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# `ork`
|
2
|
+
[](http://badge.fury.io/rb/ork)
|
3
|
+
[](http://travis-ci.org/eMancu/ork)
|
4
|
+
[](https://codeclimate.com/github/eMancu/ork)
|
5
|
+
[](https://coveralls.io/r/eMancu/ork)
|
6
|
+
[](https://gemnasium.com/eMancu/ork)
|
7
|
+
|
8
|
+
`ork` is a small Ruby modeling layer for **Riak**, Basho's distributed database inspired by [Ohm](http://ohm.keyvalue.org).
|
9
|
+
|
10
|
+
## Dependencies
|
11
|
+
|
12
|
+
`ork` requires Ruby 1.9 or later and the `riak-client` gem to connect to **Riak**.
|
13
|
+
|
14
|
+
## Getting started
|
15
|
+
|
16
|
+
## Attributes
|
17
|
+
|
18
|
+
Ork::Model provides one attribute type:
|
19
|
+
|
20
|
+
- Ork::Model.attribute attribute
|
21
|
+
|
22
|
+
And three meta types:
|
23
|
+
|
24
|
+
- Ork::Model.reference reference
|
25
|
+
- Ork::Model.referenced referenced
|
26
|
+
- Ork::Model.collection collection
|
27
|
+
|
28
|
+
### attribute
|
29
|
+
|
30
|
+
An `attribute` is just any value that can be stored.
|
31
|
+
|
32
|
+
### reference
|
33
|
+
|
34
|
+
It's a special kind of attribute that references another model.
|
35
|
+
Internally, Ork will keep a pointer to the model (its ID), but you get
|
36
|
+
accessors that give you real instances. You can think of it as the model
|
37
|
+
containing the foreign key to another model.
|
38
|
+
|
39
|
+
### referenced
|
40
|
+
|
41
|
+
Provides an accessor to search for _one_ model that `reference` the current model.
|
42
|
+
|
43
|
+
### collection
|
44
|
+
|
45
|
+
Provides an accessor to search for _all_ models that `reference` the current model.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ork
|
2
|
+
class Connection
|
3
|
+
attr_accessor :context, :options
|
4
|
+
|
5
|
+
def initialize(context = :main, options = {})
|
6
|
+
@context = context
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def reset!
|
11
|
+
threaded[context] = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(context, options = {})
|
15
|
+
self.context = context
|
16
|
+
self.options = options
|
17
|
+
self.reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
def riak
|
21
|
+
threaded[context] ||= Riak::Client.new(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def threaded
|
25
|
+
Thread.current[:ork] ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Ork::Model
|
2
|
+
module Associations
|
3
|
+
# A macro for defining an attribute, an index, and an accessor
|
4
|
+
# for a given model.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class Post
|
9
|
+
# include Ork::Model
|
10
|
+
#
|
11
|
+
# reference :user, :User
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # It's the same as:
|
15
|
+
#
|
16
|
+
# class Post
|
17
|
+
# include Ork::Model
|
18
|
+
#
|
19
|
+
# attribute :user_id
|
20
|
+
# index :user_id
|
21
|
+
#
|
22
|
+
# def user
|
23
|
+
# @_memo[:user] ||= User[user_id]
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def user=(user)
|
27
|
+
# self.user_id = user.id
|
28
|
+
# @_memo[:user] = user
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def user_id=(user_id)
|
32
|
+
# @_memo.delete(:user_id)
|
33
|
+
# self.user_id = user_id
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
def reference(name, model)
|
38
|
+
reader = :"#{name}_id"
|
39
|
+
writer = :"#{name}_id="
|
40
|
+
|
41
|
+
index reader
|
42
|
+
|
43
|
+
define_method(reader) do
|
44
|
+
@attributes[reader]
|
45
|
+
end
|
46
|
+
|
47
|
+
define_method(writer) do |value|
|
48
|
+
@_memo.delete(name)
|
49
|
+
@attributes[reader] = value
|
50
|
+
end
|
51
|
+
|
52
|
+
define_method(:"#{name}=") do |value|
|
53
|
+
@_memo.delete(name)
|
54
|
+
send(writer, value ? value.id : nil)
|
55
|
+
end
|
56
|
+
|
57
|
+
define_method(name) do
|
58
|
+
@_memo[name] ||= begin
|
59
|
+
model = Ork::Utils.const(self.class, model)
|
60
|
+
model[send(reader)]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# A macro for defining a method which basically does a find.
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
# class Post
|
69
|
+
# include Ork::Model
|
70
|
+
#
|
71
|
+
# reference :user, :User
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# class User
|
75
|
+
# include Ork::Model
|
76
|
+
#
|
77
|
+
# referenced :post, :Post
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# # is the same as
|
81
|
+
#
|
82
|
+
# class User
|
83
|
+
# include Ork::Model
|
84
|
+
#
|
85
|
+
# def post
|
86
|
+
# Post.find(:user_id => self.id)
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
def referenced(name, model, reference = to_reference)
|
91
|
+
define_method name do
|
92
|
+
model = Ork::Utils.const(self.class, model)
|
93
|
+
model.find(:"#{reference}_id" => id).first
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# A macro for defining a method which basically does a find.
|
98
|
+
#
|
99
|
+
# Example:
|
100
|
+
# class Post
|
101
|
+
# include Ork::Model
|
102
|
+
#
|
103
|
+
# reference :user, :User
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# class User
|
107
|
+
# include Ork::Model
|
108
|
+
#
|
109
|
+
# collection :posts, :Post
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# # is the same as
|
113
|
+
#
|
114
|
+
# class User
|
115
|
+
# include Ork::Model
|
116
|
+
#
|
117
|
+
# def posts
|
118
|
+
# Post.find(:user_id => self.id)
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
def collection(name, model, reference = to_reference)
|
123
|
+
define_method name do
|
124
|
+
model = Ork::Utils.const(self.class, model)
|
125
|
+
model.find(:"#{reference}_id" => id)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def to_reference
|
132
|
+
name.to_s.
|
133
|
+
match(/^(?:.*::)*(.*)$/)[1].
|
134
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
135
|
+
downcase.to_sym
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Ork::Model
|
2
|
+
module ClassMethods
|
3
|
+
attr_writer :bucket_name
|
4
|
+
|
5
|
+
# Syntactic sugar for Model.new(atts).save
|
6
|
+
def create(atts = {})
|
7
|
+
new(atts).save
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
@attributes ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def bucket
|
15
|
+
Ork.riak.bucket(bucket_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bucket_name
|
19
|
+
@bucket_name ||= self.to_s.downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def indices
|
23
|
+
@indices ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def uniques
|
27
|
+
@uniques ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
# Declares persisted attributes.
|
33
|
+
# All attributes are stored on the Riak hash.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# class User
|
37
|
+
# include Ork::Model
|
38
|
+
#
|
39
|
+
# attribute :name
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# # It's the same as:
|
43
|
+
#
|
44
|
+
# class User
|
45
|
+
# include Ork::Model
|
46
|
+
#
|
47
|
+
# def name
|
48
|
+
# @attributes[:name]
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def name=(name)
|
52
|
+
# @attributes[:name] = name
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
def attribute(name, cast = nil)
|
57
|
+
attributes << name unless attributes.include?(name)
|
58
|
+
|
59
|
+
if cast
|
60
|
+
define_method(name) do
|
61
|
+
cast[@attributes[name]]
|
62
|
+
end
|
63
|
+
else
|
64
|
+
define_method(name) do
|
65
|
+
@attributes[name]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
define_method(:"#{name}=") do |value|
|
70
|
+
@attributes[name] = value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Index any method on your model. Once you index a method, you can
|
75
|
+
# use it in `find` statements.
|
76
|
+
def index(attribute)
|
77
|
+
indices << attribute unless indices.include?(attribute)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Create a unique index for any method on your model.
|
81
|
+
#
|
82
|
+
# Note: if there is a conflict while saving, an
|
83
|
+
# `Ork::UniqueIndexViolation` violation is raised.
|
84
|
+
#
|
85
|
+
def unique(attribute)
|
86
|
+
uniques << attribute unless uniques.include?(attribute)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Ork::Model
|
2
|
+
module Finders
|
3
|
+
|
4
|
+
# Retrieve a record by ID.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# u = User.create
|
9
|
+
# u == User[u.id]
|
10
|
+
# # => true
|
11
|
+
#
|
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
|
17
|
+
end
|
18
|
+
|
19
|
+
# Check if the ID exists.
|
20
|
+
def exist?(id)
|
21
|
+
!id.nil? && bucket.exists?(id)
|
22
|
+
end
|
23
|
+
alias :exists? :exist?
|
24
|
+
|
25
|
+
# 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
|
+
# @return [Array<Document>] all found documents in the bucket
|
31
|
+
#
|
32
|
+
# @Note: This operation is incredibly expensive and should not
|
33
|
+
# be used in production applications.
|
34
|
+
#
|
35
|
+
def all
|
36
|
+
bucket.keys.inject([]) do |acc, k|
|
37
|
+
obj = self[k]
|
38
|
+
obj ? acc << obj : acc
|
39
|
+
end
|
40
|
+
end
|
41
|
+
alias :list :all
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/ork/model.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require_relative 'model/class_methods'
|
2
|
+
require_relative 'model/associations'
|
3
|
+
require_relative 'model/finders'
|
4
|
+
|
5
|
+
module Ork
|
6
|
+
module Model
|
7
|
+
attr_reader :attributes, :id
|
8
|
+
attr_writer :id
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend(Ork::Model::ClassMethods)
|
12
|
+
klass.extend(Ork::Model::Associations)
|
13
|
+
klass.extend(Ork::Model::Finders)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Initialize a model using a dictionary of attributes.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# u = User.new(:name => "John")
|
21
|
+
#
|
22
|
+
def initialize(atts = {})
|
23
|
+
@attributes = {}
|
24
|
+
@_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
|
68
|
+
end
|
69
|
+
|
70
|
+
# Write the dictionary of key-value pairs to the model.
|
71
|
+
#
|
72
|
+
def update_attributes(atts)
|
73
|
+
atts.delete('_type')
|
74
|
+
atts.each { |att, val| send(:"#{att}=", val) }
|
75
|
+
end
|
76
|
+
|
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)
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
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
|
148
|
+
end
|
149
|
+
|
150
|
+
def model
|
151
|
+
self.class
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/ork/utils.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Ork
|
2
|
+
# Instead of monkey patching Kernel or trying to be clever, it's
|
3
|
+
# best to confine all the helper methods in a Utils module.
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
# Used by: `attribute`, `reference`, `collection`.
|
7
|
+
#
|
8
|
+
# Employed as a solution to avoid `NameError` problems when trying
|
9
|
+
# to load models referring to other models not yet loaded.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# class Comment
|
14
|
+
# include Ork::Model
|
15
|
+
#
|
16
|
+
# reference :user, User # NameError undefined constant User.
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Instead of relying on some clever `const_missing` hack, we can
|
20
|
+
# simply use a Symbol.
|
21
|
+
#
|
22
|
+
# class Comment
|
23
|
+
# include Ork::Model
|
24
|
+
#
|
25
|
+
# reference :user, :User
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
def self.const(context, name)
|
29
|
+
case name
|
30
|
+
when Symbol then context.const_get(name)
|
31
|
+
else name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/ork/version.rb
ADDED
data/lib/ork.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ork/connection'
|
2
|
+
require 'ork/model'
|
3
|
+
require 'ork/utils'
|
4
|
+
require "riak"
|
5
|
+
|
6
|
+
module Ork
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
def self.conn
|
10
|
+
@conn ||= Ork::Connection.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# Stores the connection options for the Riak instance.
|
14
|
+
#
|
15
|
+
# Examples:
|
16
|
+
#
|
17
|
+
# Ork.connect(:http_port => 6380, :pb_port => 15000)
|
18
|
+
#
|
19
|
+
# All of the options are simply passed on to `Riak::Client.new`.
|
20
|
+
#
|
21
|
+
def self.connect(context = :main, options = {})
|
22
|
+
conn.start(context, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Use this if you want to do quick ad hoc riak commands against the
|
26
|
+
# defined Ork connection.
|
27
|
+
#
|
28
|
+
# Examples:
|
29
|
+
#
|
30
|
+
# Ork.riak.buckets
|
31
|
+
# Ork.riak.bucket('foo').keys
|
32
|
+
#
|
33
|
+
def self.riak
|
34
|
+
conn.riak
|
35
|
+
end
|
36
|
+
end
|
data/ork.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'ork'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
5
|
+
s.summary = 'Ruby modeling layer for Riak.'
|
6
|
+
s.description = 'Ork is a small Ruby modeling layer for Riak, inspired by Ohm.'
|
7
|
+
s.authors = ['Emiliano Mancuso']
|
8
|
+
s.email = ['emiliano.mancuso@gmail.com']
|
9
|
+
s.homepage = 'http://github.com/eMancu/ork'
|
10
|
+
s.license = 'MIT'
|
11
|
+
|
12
|
+
s.files = Dir[
|
13
|
+
'README.md',
|
14
|
+
'rakefile',
|
15
|
+
'lib/**/*.rb',
|
16
|
+
'*.gemspec'
|
17
|
+
]
|
18
|
+
s.test_files = Dir['test/*.*']
|
19
|
+
|
20
|
+
s.add_dependency 'riak-client'
|
21
|
+
s.add_development_dependency 'protest'
|
22
|
+
s.add_development_dependency 'toml-rb'
|
23
|
+
end
|
24
|
+
|
data/rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
|
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
|
+
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
|
28
|
+
|
29
|
+
flush_db!
|
30
|
+
test_server.stop
|
31
|
+
end
|
data/test/finders.rb
ADDED
@@ -0,0 +1,43 @@
|
|
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
|
data/test/helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "protest"
|
5
|
+
require "ork"
|
6
|
+
|
7
|
+
# require 'coveralls'
|
8
|
+
# Coveralls.wear!
|
9
|
+
|
10
|
+
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
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush_db!
|
40
|
+
test_server.drop
|
41
|
+
end
|
data/test/model.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative 'helper'
|
3
|
+
|
4
|
+
class Event
|
5
|
+
include Ork::Model
|
6
|
+
|
7
|
+
attribute :name
|
8
|
+
attribute :location
|
9
|
+
|
10
|
+
unique :name
|
11
|
+
index :location
|
12
|
+
end
|
13
|
+
|
14
|
+
Protest.describe 'Ork::Model' do
|
15
|
+
context 'Definition' do
|
16
|
+
test 'have an attributes list' do
|
17
|
+
assert_equal [:name, :location], Event.attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'have a uniques list' do
|
21
|
+
assert_equal [:name], Event.uniques
|
22
|
+
end
|
23
|
+
|
24
|
+
test 'have an indices list' do
|
25
|
+
assert_equal [:location], Event.indices
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'model owns a bucket name by default' do
|
29
|
+
assert_equal 'event', Event.bucket_name
|
30
|
+
end
|
31
|
+
|
32
|
+
test 'generate accessors for attributes' do
|
33
|
+
event = Event.new
|
34
|
+
|
35
|
+
assert event.respond_to? :name
|
36
|
+
assert event.respond_to? :name=
|
37
|
+
assert event.respond_to? :location
|
38
|
+
assert event.respond_to? :location=
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'cast to different types' do
|
42
|
+
pending 'Define how to cast objects'
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'model can change bucket name' do
|
46
|
+
Event.bucket_name= 'other_bucket_for_event'
|
47
|
+
assert_equal 'other_bucket_for_event', Event.bucket_name
|
48
|
+
end
|
49
|
+
|
50
|
+
test 'there is a Riak::Bucket corresponding to the model' do
|
51
|
+
assert_equal Riak::Bucket, Event.bucket.class
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'Instance' do
|
56
|
+
setup do
|
57
|
+
@event = Event.new(name: 'Ruby')
|
58
|
+
end
|
59
|
+
|
60
|
+
test 'determine if it is a new instance or it was saved' do
|
61
|
+
assert @event.new?
|
62
|
+
@event.save
|
63
|
+
assert !@event.new?
|
64
|
+
end
|
65
|
+
|
66
|
+
test 'assign attributes from the hash' do
|
67
|
+
assert_equal 'Ruby', @event.name
|
68
|
+
end
|
69
|
+
|
70
|
+
test 'inspect a new object shows the class, attributes with id nil' do
|
71
|
+
assert_equal '#<Event:nil {:name=>"Ruby"}>', @event.inspect
|
72
|
+
end
|
73
|
+
|
74
|
+
test 'inspect a saved object shows the class, attributes with id nil' do
|
75
|
+
@event.save
|
76
|
+
assert_equal '#<Event:' + @event.id + ' {:name=>"Ruby"}>', @event.inspect
|
77
|
+
end
|
78
|
+
|
79
|
+
test 'assign an ID and save the object' do
|
80
|
+
event = Event.create(name: 'Ruby')
|
81
|
+
|
82
|
+
assert !event.new?
|
83
|
+
assert !event.id.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
test 'update and save the attributes in UTF8' do
|
87
|
+
@event.update(name: '32° Kisei-sen')
|
88
|
+
assert_equal '32° Kisei-sen', Event[@event.id].name
|
89
|
+
end
|
90
|
+
|
91
|
+
test 'update_attributes changes attributes but does not save the object' do
|
92
|
+
assert @event.new?
|
93
|
+
assert_equal 'Ruby', @event.name
|
94
|
+
|
95
|
+
@event.update_attributes(name: 'Emerald', location: 4)
|
96
|
+
|
97
|
+
assert @event.new?
|
98
|
+
assert_equal 'Emerald', @event.name
|
99
|
+
assert_equal 4, @event.location
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'Deletion' do
|
103
|
+
test 'freeze the object' do
|
104
|
+
assert !@event.frozen?
|
105
|
+
@event.delete
|
106
|
+
assert @event.frozen?
|
107
|
+
end
|
108
|
+
|
109
|
+
test 'delete the object from the bucket' do
|
110
|
+
@event.save
|
111
|
+
assert Event.bucket.exist?(@event.id)
|
112
|
+
@event.delete
|
113
|
+
assert !Event.bucket.exist?(@event.id)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "Equality" do
|
119
|
+
setup do
|
120
|
+
@event = Event.new(name: 'Ruby')
|
121
|
+
@other = Event.new(name: 'Emerald')
|
122
|
+
end
|
123
|
+
|
124
|
+
test 'different types' do
|
125
|
+
assert @event != 'Not an event'
|
126
|
+
end
|
127
|
+
|
128
|
+
test 'saved instances with different ids' do
|
129
|
+
@event.save
|
130
|
+
@other.save
|
131
|
+
|
132
|
+
assert @event != @other
|
133
|
+
end
|
134
|
+
|
135
|
+
test 'unsaved intances' do
|
136
|
+
pending 'Define how equality will be'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/test/reference.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class Post
|
4
|
+
include Ork::Model
|
5
|
+
attribute :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class Comment
|
9
|
+
include Ork::Model
|
10
|
+
attribute :text
|
11
|
+
|
12
|
+
reference :post, :Post
|
13
|
+
reference :weird_post, :Post
|
14
|
+
end
|
15
|
+
|
16
|
+
Protest.describe 'reference' do
|
17
|
+
teardown do
|
18
|
+
flush_db!
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'return nil when there is no reference object' do
|
22
|
+
comment = Comment.new
|
23
|
+
|
24
|
+
assert comment.post.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'raise an exception assigning an object of the wrong type' do
|
28
|
+
pending 'Not sure to support this'
|
29
|
+
assert_raise(Error) do
|
30
|
+
Comment.new post: 'Not a post'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'return the object referenced' do
|
35
|
+
post = Post.create name: 'New'
|
36
|
+
comment = Comment.new post: post
|
37
|
+
|
38
|
+
assert_equal post, comment.post
|
39
|
+
assert_equal post.id, comment.post_id
|
40
|
+
end
|
41
|
+
|
42
|
+
test 'object reference with not default key' do
|
43
|
+
post = Post.create name: 'New'
|
44
|
+
comment = Comment.new weird_post: post
|
45
|
+
|
46
|
+
assert_equal post, comment.weird_post
|
47
|
+
assert_equal post.id, comment.weird_post_id
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'update reference to an object given the id or object' do
|
51
|
+
post = Post.create name: 'New'
|
52
|
+
comment = Comment.new
|
53
|
+
|
54
|
+
assert comment.post.nil?
|
55
|
+
assert comment.post_id.nil?
|
56
|
+
|
57
|
+
comment.post = post
|
58
|
+
|
59
|
+
assert_equal post, comment.post
|
60
|
+
assert_equal post.id, comment.post_id
|
61
|
+
|
62
|
+
post = Post.create name: 'Other'
|
63
|
+
comment.post_id = post.id
|
64
|
+
|
65
|
+
assert_equal post, comment.post
|
66
|
+
assert_equal post.id, comment.post_id
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'Deletion' do
|
70
|
+
# Discuss if we want cascade all deletetion and that sort of things
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This is where the test server node will be generated. Something on
|
2
|
+
# /tmp is usually ok.
|
3
|
+
root = "/tmp/.ork_node"
|
4
|
+
min_port = 15000
|
5
|
+
|
6
|
+
# This is where Riak is installed on your system, that is, the path to
|
7
|
+
# the 'riak' and 'riak-admin' scripts. I use a self-built node, but
|
8
|
+
# here's where it will generally be on various platforms:
|
9
|
+
#
|
10
|
+
# Linux: /usr/sbin
|
11
|
+
# Solaris/OpenSolaris: /opt/riak/bin
|
12
|
+
# Mac OS/X (Homebrew): /usr/local/bin
|
13
|
+
# Source/Self built: /path/to/your/install/rel/riak/bin
|
14
|
+
#
|
15
|
+
source = "/usr/local/bin"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This is where the test server node will be generated. Something on
|
2
|
+
# /tmp is usually ok.
|
3
|
+
root = "/tmp/.ork_node"
|
4
|
+
min_port = 15000
|
5
|
+
|
6
|
+
# This is where Riak is installed on your system, that is, the path to
|
7
|
+
# the 'riak' and 'riak-admin' scripts. I use a self-built node, but
|
8
|
+
# here's where it will generally be on various platforms:
|
9
|
+
#
|
10
|
+
# Linux: /usr/sbin
|
11
|
+
# Solaris/OpenSolaris: /opt/riak/bin
|
12
|
+
# Mac OS/X (Homebrew): /usr/local/bin
|
13
|
+
# Source/Self built: /path/to/your/install/rel/riak/bin
|
14
|
+
#
|
15
|
+
source = "/usr/local/bin"
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ork
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emiliano Mancuso
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: riak-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: protest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: toml-rb
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Ork is a small Ruby modeling layer for Riak, inspired by Ohm.
|
56
|
+
email:
|
57
|
+
- emiliano.mancuso@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- README.md
|
63
|
+
- rakefile
|
64
|
+
- lib/ork/connection.rb
|
65
|
+
- lib/ork/model/associations.rb
|
66
|
+
- lib/ork/model/class_methods.rb
|
67
|
+
- lib/ork/model/finders.rb
|
68
|
+
- lib/ork/model.rb
|
69
|
+
- lib/ork/utils.rb
|
70
|
+
- lib/ork/version.rb
|
71
|
+
- lib/ork.rb
|
72
|
+
- ork.gemspec
|
73
|
+
- test/finders.rb
|
74
|
+
- 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
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.0.3
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Ruby modeling layer for Riak.
|
103
|
+
test_files:
|
104
|
+
- test/finders.rb
|
105
|
+
- test/helper.rb
|
106
|
+
- test/model.rb
|
107
|
+
- test/reference.rb
|
108
|
+
- test/test_riak_server.toml
|
109
|
+
- test/test_riak_server.toml.example
|