ork 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d614293fd63e1d8d2a8de96cfb92ad40c09ebdc5
4
- data.tar.gz: f487220a7969011adfe79399feb8ddcecc0d7187
3
+ metadata.gz: 609ee14359a522c9a80be687981cf6280fb2f2ea
4
+ data.tar.gz: d2b93d067769324259a9d48daebaff978a6e32c6
5
5
  SHA512:
6
- metadata.gz: 39953ee12661946b11f55e278253d8726871ff9b8d3eb3c389e9d2ba447460f8c959048c99b9d6ade5a7992974297bc7bc78d8b55ba0e7c9366c82b6492fb9ba
7
- data.tar.gz: 7b565b3a8ff7901cc7457464a335a54b0c5c1a1f53cf159f87815e4402a250d168bbd275da6f7a4a12a8621c59be86e53e08cf5066e10c9eb5428ea632a6cc03
6
+ metadata.gz: beb105369966731ad130fb658f7586defa8d8d2c64f12e1438a3c6fad6946e75cf181a836d4c197f308020d1178733559ff76494f85e812691bfd35c096c71fe
7
+ data.tar.gz: caa7d2aff4293bc60f84cc9b85b228389447aa76ecac6ac291b85ff0ac23d059d2ec552eb4f7b00025fb4402d2b3db82a2eee27a67b26402c6f6c37fe9b219e6
data/README.md CHANGED
@@ -1,45 +1,173 @@
1
- # `ork`
1
+ # Ork
2
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)
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
7
 
8
- `ork` is a small Ruby modeling layer for **Riak**, Basho's distributed database inspired by [Ohm](http://ohm.keyvalue.org).
8
+ Ork is a small Ruby modeling layer for **Riak** database, inspired by [Ohm](http://ohm.keyvalue.org).
9
9
 
10
10
  ## Dependencies
11
11
 
12
12
  `ork` requires Ruby 1.9 or later and the `riak-client` gem to connect to **Riak**.
13
13
 
14
+ Install dependencies using `dep` is easy as run:
15
+
16
+ dep install
17
+
18
+ ## Installation
19
+
20
+ Install [Riak](http://basho.com/riak/) with your package manager:
21
+
22
+ $ brew install riak
23
+
24
+ Or download it from [Riak's download page](http://docs.basho.com/riak/latest/downloads/)
25
+
26
+ Once you have it installed, you can execute `riak start` and it will run on `localhost:8098` by default.
27
+
28
+ If you don't have Ork, try this:
29
+
30
+ $ gem install ork
31
+
14
32
  ## Getting started
15
33
 
16
- ## Attributes
34
+ Ork helps you to focus your energy on modeling and designing the object collaborations without worry about how Riak works.
35
+ Take a look at the example below:
36
+
37
+ ### Example
38
+
39
+ ```ruby
40
+ class Post
41
+ include Ork::Document
42
+
43
+ attribute :name
44
+ attribute :age, default: 18
45
+
46
+ index :age
47
+ unique :name
48
+ end
49
+
50
+ class Comment
51
+ include Ork::Document
52
+
53
+ attribute :text
54
+ reference :post, :Post
55
+ end
56
+ ```
57
+
58
+ It also gives you some helpful **class methods**:
59
+
60
+
61
+ | Class Method | Description | Example (ruby) |
62
+ |:-------------|:-------------------------------------------------|:-------------------------|
63
+ | bucket | `Riak::Bucket` The bucket assigned to this class | `#<Riak::Bucket {post}>` |
64
+ | bucket_name | `String` The bucket name | `"post"` |
65
+ | attributes | `Array` Attributes declared | `[:name, :age]` |
66
+ | indices | `Array` Indices declared | `[:age]` |
67
+ | uniques | `Array` Unique indices declared | `[:name]` |
68
+ | embedding | `Array` Embedded attributes declared | `[:post]` |
69
+ | defaults | `Hash` Defaults for attributes | `{:age=>18}` |
70
+
71
+
72
+ And for **instance methods** it defines:
73
+
74
+ | Instance Method | Description |
75
+ |:-----------------------------------|:--------------------------------------------------|
76
+ | new? | `Bool` Answer if its a new instance or not. |
77
+ | embeddable? | `Bool` Answer if its an embeddable object or not. |
78
+ | update(_attr_) | `Bool` Update model attributes and save it. |
79
+ | update_attributes(_attr_) | `Array` Update model attributes. |
80
+ | update_embedded_attributes(_attr_) | `Array` Update embedded model attributes. |
81
+ | reload | `<class>` Preload all the attributes from Riak. |
82
+ | save | `Bool` Persist document. |
83
+ | delete | `Bool` Delete the document from Riak. |
17
84
 
18
- Ork::Model provides one attribute type:
19
85
 
20
- - Ork::Model.attribute attribute
21
86
 
22
- And three meta types:
87
+ # Modeling
88
+ > Embeddable objects are those with `include Ork::Embeddable` and they can not be saved
89
+ > without a parent.
23
90
 
24
- - Ork::Model.reference reference
25
- - Ork::Model.referenced referenced
26
- - Ork::Model.collection collection
27
91
 
28
- ### attribute
92
+ Core behaviour of `Ork::Model`.
29
93
 
30
- An `attribute` is just any value that can be stored.
94
+ ## attribute
31
95
 
32
- ### reference
96
+ An `attribute` is just any value that can be stored. It is composed of a `:name` and an optional `hash`.
97
+
98
+ ```ruby
99
+ attribute :age, default: 18
100
+ ```
101
+
102
+ #### Options
103
+
104
+ - `default: nil` set to the attribute a _value_ by default.
105
+
106
+ - `accessors: [:reader, :writer]` defines which accessors will be defined
107
+ * `:reader` a.k.a **attr_reader**, create a method to read the value.
108
+ * `:writer` a.k.a **attr_writer**, create a method to write the value.
109
+ * `:question` create a question method. Perfect for **bool** attributes.
110
+
111
+
112
+ ## reference
33
113
 
34
114
  It's a special kind of attribute that references another model.
35
115
  Internally, Ork will keep a pointer to the model (its ID), but you get
36
116
  accessors that give you real instances. You can think of it as the model
37
117
  containing the foreign key to another model.
38
118
 
39
- ### referenced
119
+ ```ruby
120
+ reference :user, :User
121
+ ```
122
+
123
+ ## referenced
40
124
 
41
125
  Provides an accessor to search for _one_ model that `reference` the current model.
42
126
 
43
- ### collection
127
+ ```ruby
128
+ referenced :comment, :Comment
129
+ ```
130
+
131
+ ## collection
44
132
 
45
133
  Provides an accessor to search for _all_ models that `reference` the current model.
134
+
135
+ ```ruby
136
+ collection :comments, :Comment
137
+ ```
138
+
139
+ ## embed
140
+ > Only accepts embeddable objects.
141
+
142
+ It's a special kind of attribute that embeds another model.
143
+ Internally, Ork will keep the object as an attribute, but you get
144
+ accessors that give you real instances.
145
+
146
+ ```ruby
147
+ embed :comment, :Comment
148
+ ```
149
+
150
+ ## embed_collection
151
+ > Only accepts embeddable objects.
152
+
153
+ Provides an accessor for _all_ models that are `embedded` into the current model.
154
+ It also provides a method for _adding_ objects to this collection.
155
+
156
+ ```ruby
157
+ embed_collection :comments, :Comment
158
+
159
+ # It provides
160
+ def add_comments(a_comment)
161
+ # code
162
+ end
163
+ ```
164
+
165
+
166
+ ## embedded
167
+ > Only for embeddable objects.
168
+
169
+ Provides an accessor to the object that `embeds` the current model.
170
+
171
+ ```ruby
172
+ embedded :post, :Post
173
+ ```
@@ -0,0 +1,48 @@
1
+ require_relative 'model'
2
+
3
+ module Ork
4
+ module Embeddable
5
+
6
+ def self.included(klass)
7
+ klass.send(:include, Ork::Model)
8
+ klass.extend(Ork::Model::Embedded::ClassMethods)
9
+ end
10
+
11
+ def embeddable?
12
+ true
13
+ end
14
+
15
+ def __parent
16
+ @attributes[model.__parent_key] or raise Ork::ParentMissing
17
+ end
18
+
19
+ def __parent=(object)
20
+ @attributes[model.__parent_key] = object
21
+ end
22
+
23
+ # Check for equality by doing the following assertions:
24
+ #
25
+ # 1. That the passed model is of the same type.
26
+ # 2. That they have the same attributes.
27
+ #
28
+ # How it was developed, 2 implies 1.
29
+ #
30
+ def ==(other)
31
+ other.kind_of?(model) &&
32
+ __persist_attributes == other.__persist_attributes &&
33
+ other.attributes[model.__parent_key] == @attributes[model.__parent_key]
34
+ end
35
+ alias :eql? :==
36
+
37
+ # Pretty print for the model
38
+ #
39
+ # Example:
40
+ #
41
+ # User.new(name: 'John').inspect
42
+ # # => #<User {:name=>"John"}>
43
+ #
44
+ def inspect
45
+ "#<#{model} #{attributes.inspect}>"
46
+ end
47
+ end
48
+ end
data/lib/ork/errors.rb ADDED
@@ -0,0 +1,8 @@
1
+ module Ork
2
+ class Error < StandardError; end
3
+ class IndexNotFound < RuntimeError; end
4
+ class UniqueIndexViolation < RuntimeError; end
5
+
6
+ class NotAnEmbeddableObject < RuntimeError; end
7
+ class ParentMissing < RuntimeError; end
8
+ end
@@ -6,7 +6,7 @@ module Ork::Model
6
6
  # Example:
7
7
  #
8
8
  # class Post
9
- # include Ork::Model
9
+ # include Ork::Document
10
10
  #
11
11
  # reference :user, :User
12
12
  # end
@@ -14,7 +14,7 @@ module Ork::Model
14
14
  # # It's the same as:
15
15
  #
16
16
  # class Post
17
- # include Ork::Model
17
+ # include Ork::Document
18
18
  #
19
19
  # attribute :user_id
20
20
  # index :user_id
@@ -50,8 +50,8 @@ module Ork::Model
50
50
  end
51
51
 
52
52
  define_method(:"#{name}=") do |value|
53
- @_memo.delete(name)
54
53
  send(writer, value ? value.id : nil)
54
+ @_memo[name] = value
55
55
  end
56
56
 
57
57
  define_method(name) do
@@ -66,13 +66,13 @@ module Ork::Model
66
66
  #
67
67
  # Example:
68
68
  # class Post
69
- # include Ork::Model
69
+ # include Ork::Document
70
70
  #
71
71
  # reference :user, :User
72
72
  # end
73
73
  #
74
74
  # class User
75
- # include Ork::Model
75
+ # include Ork::Document
76
76
  #
77
77
  # referenced :post, :Post
78
78
  # end
@@ -80,7 +80,7 @@ module Ork::Model
80
80
  # # is the same as
81
81
  #
82
82
  # class User
83
- # include Ork::Model
83
+ # include Ork::Document
84
84
  #
85
85
  # def post
86
86
  # Post.find(:user_id => self.id)
@@ -89,8 +89,11 @@ module Ork::Model
89
89
  #
90
90
  def referenced(name, model, reference = to_reference)
91
91
  define_method name do
92
- model = Ork::Utils.const(self.class, model)
93
- model.find(:"#{reference}_id" => id).first
92
+ return nil if self.id.nil?
93
+ @_memo[name] ||= begin
94
+ model = Ork::Utils.const(self.class, model)
95
+ model.find(:"#{reference}_id", self.id).first
96
+ end
94
97
  end
95
98
  end
96
99
 
@@ -98,13 +101,13 @@ module Ork::Model
98
101
  #
99
102
  # Example:
100
103
  # class Post
101
- # include Ork::Model
104
+ # include Ork::Document
102
105
  #
103
106
  # reference :user, :User
104
107
  # end
105
108
  #
106
109
  # class User
107
- # include Ork::Model
110
+ # include Ork::Document
108
111
  #
109
112
  # collection :posts, :Post
110
113
  # end
@@ -112,7 +115,7 @@ module Ork::Model
112
115
  # # is the same as
113
116
  #
114
117
  # class User
115
- # include Ork::Model
118
+ # include Ork::Document
116
119
  #
117
120
  # def posts
118
121
  # Post.find(:user_id => self.id)
@@ -121,8 +124,106 @@ module Ork::Model
121
124
  #
122
125
  def collection(name, model, reference = to_reference)
123
126
  define_method name do
124
- model = Ork::Utils.const(self.class, model)
125
- model.find(:"#{reference}_id" => id)
127
+ return [] if self.id.nil?
128
+ @_memo[name] ||= begin
129
+ model = Ork::Utils.const(self.class, model)
130
+ model.find(:"#{reference}_id", self.id)
131
+ end
132
+ end
133
+ end
134
+
135
+ # A macro for defining an attribute, and the accessors
136
+ # for a given model.
137
+ #
138
+ # Example:
139
+ #
140
+ # class Post
141
+ # include Ork::Document
142
+ #
143
+ # embed :author, :Author
144
+ # end
145
+ #
146
+ # # It's the same as:
147
+ #
148
+ # class Post
149
+ # include Ork::Document
150
+ #
151
+ # def author
152
+ # @embedding[:author]
153
+ # end
154
+ #
155
+ # def author=(author)
156
+ # @embedding[:author] = author
157
+ # author.__parent = self
158
+ # end
159
+ # end
160
+ #
161
+ def embed(name, model)
162
+ embedding << name unless embedding.include?(name)
163
+
164
+ define_method(name) do
165
+ return nil unless @embedding.has_key? name
166
+ @_memo[name] ||= begin
167
+ model = Ork::Utils.const(self.class, model)
168
+ model.new(@embedding[name])
169
+ end
170
+ end
171
+
172
+ define_method(:"#{name}=") do |object|
173
+ unless object.respond_to?(:embeddable?) && object.embeddable?
174
+ raise Ork::NotAnEmbeddableObject.new(object)
175
+ end
176
+
177
+ @embedding[name] = object.attributes
178
+ object.__parent = self
179
+
180
+ @_memo[name] = object
181
+ end
182
+ end
183
+
184
+ # A macro for find embedded objects of the same type, massive assign and
185
+ # syntactic sugar for add an object to the collection.
186
+ #
187
+ # Example:
188
+ #
189
+ # class Post
190
+ # include Ork::Document
191
+ #
192
+ # embed_collection :authors, :Author
193
+ # end
194
+ #
195
+ # # It's the same as:
196
+ #
197
+ # class Post
198
+ # include Ork::Document
199
+ #
200
+ # def authors
201
+ # # An array of authors
202
+ # end
203
+ #
204
+ # def add_author(author)
205
+ # # Add an author to the embed collection
206
+ # end
207
+ # end
208
+ #
209
+ def embed_collection(name, model)
210
+ embedding << name unless embedding.include?(name)
211
+
212
+ define_method(name) do
213
+ return [] unless @embedding.has_key? name
214
+
215
+ @_memo[name] ||= begin
216
+ model = Ork::Utils.const(self.class, model)
217
+ @embedding[name].map{|atts| model.new atts}
218
+ end
219
+ end
220
+
221
+ define_method(:"add_#{name}") do |object|
222
+ raise Ork::NotAnEmbeddableObject.new(object) unless object.embeddable?
223
+
224
+ object.__parent = self
225
+ @_memo[name] << object unless @_memo[name].nil?
226
+ @embedding[name] = Array(@embedding[name]) << object.attributes
126
227
  end
127
228
  end
128
229
 
@@ -1,8 +1,11 @@
1
+ require_relative 'index'
2
+
1
3
  module Ork::Model
2
4
  module ClassMethods
3
5
  attr_writer :bucket_name
4
6
 
5
7
  # Syntactic sugar for Model.new(atts).save
8
+ #
6
9
  def create(atts = {})
7
10
  new(atts).save
8
11
  end
@@ -19,14 +22,22 @@ module Ork::Model
19
22
  @bucket_name ||= self.to_s.downcase
20
23
  end
21
24
 
25
+ def embedding
26
+ @embedding ||= []
27
+ end
28
+
22
29
  def indices
23
- @indices ||= []
30
+ @indices ||= {}
24
31
  end
25
32
 
26
33
  def uniques
27
34
  @uniques ||= []
28
35
  end
29
36
 
37
+ def defaults
38
+ @defaults ||= {}
39
+ end
40
+
30
41
  protected
31
42
 
32
43
  # Declares persisted attributes.
@@ -34,7 +45,7 @@ module Ork::Model
34
45
  #
35
46
  # Example:
36
47
  # class User
37
- # include Ork::Model
48
+ # include Ork::Document
38
49
  #
39
50
  # attribute :name
40
51
  # end
@@ -42,7 +53,7 @@ module Ork::Model
42
53
  # # It's the same as:
43
54
  #
44
55
  # class User
45
- # include Ork::Model
56
+ # include Ork::Document
46
57
  #
47
58
  # def name
48
59
  # @attributes[:name]
@@ -53,37 +64,112 @@ module Ork::Model
53
64
  # end
54
65
  # end
55
66
  #
56
- def attribute(name, cast = nil)
67
+ def attribute(name, options = {})
57
68
  attributes << name unless attributes.include?(name)
69
+ defaults[name] = options[:default] if options.has_key?(:default)
58
70
 
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
71
+ if options.has_key?(:accessors)
72
+ to_define = Array(options[:accessors]) & accessor_options
73
+ else # Default methods
74
+ to_define = [:reader, :writer]
67
75
  end
68
76
 
69
- define_method(:"#{name}=") do |value|
70
- @attributes[name] = value
71
- end
77
+ to_define.each{|m| send("#{m}_for", name) }
72
78
  end
73
79
 
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)
80
+ # Index any attribute on your model. Once you index an attribute,
81
+ # you can use it in `find` statements.
82
+ #
83
+ def index(name)
84
+ indices[name] = Index.new(name) unless indices.include?(name)
78
85
  end
79
86
 
80
- # Create a unique index for any method on your model.
87
+ # Create a 'unique index' for any method on your model.
88
+ # Actually it creates a regular index, but it checks if
89
+ # it's repeated just before persist the new values.
81
90
  #
82
91
  # Note: if there is a conflict while saving, an
83
92
  # `Ork::UniqueIndexViolation` violation is raised.
84
93
  #
85
94
  def unique(attribute)
86
95
  uniques << attribute unless uniques.include?(attribute)
96
+ index(attribute)
97
+ end
98
+
99
+ private
100
+
101
+ # Valid options for attribute accessor value
102
+ #
103
+ def accessor_options
104
+ [:reader, :writer, :question]
105
+ end
106
+
107
+ # Create reader method
108
+ #
109
+ def reader_for(name)
110
+ define_method(name) do
111
+ @attributes[name]
112
+ end
113
+ end
114
+
115
+ # Create writer method
116
+ #
117
+ def writer_for(name)
118
+ define_method(:"#{name}=") do |value|
119
+ @attributes[name] = value
120
+ end
121
+ end
122
+
123
+ # Create question method
124
+ #
125
+ def question_for(name)
126
+ define_method(:"#{name}?") do
127
+ !!@attributes[name]
128
+ end
87
129
  end
88
130
  end
131
+
132
+ module Embedded
133
+ module ClassMethods
134
+ attr_accessor :__parent_key
135
+
136
+ # Declares parent accessors for embedded objects and set the parent key
137
+ #
138
+ # Example:
139
+ # class Comment
140
+ # include Ork::Embeddable
141
+ #
142
+ # embedded :post, :Post
143
+ # end
144
+ #
145
+ # # It's the same as:
146
+ #
147
+ # class Comment
148
+ # include Ork::Embeddable
149
+ #
150
+ # def post
151
+ # @attributes[:post]
152
+ # end
153
+ #
154
+ # def post=(post)
155
+ # @attributes[:post] = post
156
+ # end
157
+ # end
158
+ #
159
+ def embedded(name, model)
160
+ @__parent_key = name
161
+
162
+ define_method(name) do
163
+ @attributes[name]
164
+ end
165
+
166
+ define_method(:"#{name}=") do |object|
167
+ raise Ork::ParentMissing if object.nil?
168
+
169
+ @attributes[name] = object
170
+ end
171
+ end
172
+ end
173
+ end
174
+
89
175
  end