ork 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/ork.png)](http://badge.fury.io/rb/ork)
|
3
|
+
[![Build Status](https://secure.travis-ci.org/eMancu/ork.png)](http://travis-ci.org/eMancu/ork)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/eMancu/ork.png)](https://codeclimate.com/github/eMancu/ork)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/eMancu/ork/badge.png)](https://coveralls.io/r/eMancu/ork)
|
6
|
+
[![Dependency Status](https://gemnasium.com/eMancu/ork.png)](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
|