rrod 1.0.0.alpha.1 → 1.0.0.alpha.2
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 +4 -4
- data/Gemfile +5 -0
- data/README.md +11 -6
- data/lib/rrod/all.rb +4 -0
- data/lib/rrod/caster/nested_model.rb +1 -1
- data/lib/rrod/configuration.rb +2 -0
- data/lib/rrod/model.rb +5 -3
- data/lib/rrod/model/attribute.rb +8 -3
- data/lib/rrod/model/attribute_methods.rb +8 -3
- data/lib/rrod/model/callbacks.rb +26 -0
- data/lib/rrod/model/collection.rb +22 -4
- data/lib/rrod/model/finders.rb +37 -26
- data/lib/rrod/model/persistence.rb +5 -4
- data/lib/rrod/model/schema.rb +9 -3
- data/lib/rrod/model/serialization.rb +9 -0
- data/lib/rrod/model/timestamps.rb +19 -0
- data/lib/rrod/model/validations.rb +15 -0
- data/lib/rrod/model/validations/associated_validator.rb +29 -0
- data/lib/rrod/query.rb +1 -1
- data/lib/rrod/test_server/rspec.rb +3 -2
- data/lib/rrod/version.rb +1 -1
- data/spec/rrod/model/attribute_methods_spec.rb +15 -4
- data/spec/rrod/model/attribute_spec.rb +15 -2
- data/spec/rrod/model/callbacks_spec.rb +60 -0
- data/spec/rrod/model/collection_spec.rb +14 -5
- data/spec/rrod/model/finders_spec.rb +11 -10
- data/spec/rrod/model/schema_spec.rb +19 -1
- data/spec/rrod/model/timestamps_spec.rb +30 -0
- data/spec/rrod/model/validations/associated_validator_spec.rb +46 -0
- data/spec/rrod/model/validations_spec.rb +43 -45
- data/spec/rrod/query_spec.rb +1 -1
- data/spec/support/models/car.rb +3 -0
- data/spec/support/models/person.rb +25 -1
- data/spec/support/models/player.rb +6 -0
- data/spec/support/models/team.rb +5 -0
- metadata +39 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95ae98b20d5fcbe473c9a350fad0b168d599c4e0
|
4
|
+
data.tar.gz: 454c37cfe3682918c89e72c69e586c30e261dd9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01f3c763fb67f2b88bb8ca2f1d1d1aa2c6ba240fb1b64f0e419bdc2fac9bf2e60778dff971152e6730dba2f68e40ebe0e851704249bc734944978e4d4fe5c781
|
7
|
+
data.tar.gz: 919a75a3101a49e1bcebc69a822925ce59b39fe5da96c8785665d9a1b9072bbc1d21e58d8da2a122fb210b752da37059a43c19f8c6371bb6f0fee4f7984249c6
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ First, you must make sure you
|
|
9
9
|
[have Riak installed](http://docs.basho.com/riak/latest/quickstart/#Install-Riak).
|
10
10
|
These guys have extremely good docs and will usually answer your questions
|
11
11
|
in freenode IRC #riak. You must also make sure you have
|
12
|
-
[enabled
|
12
|
+
[enabled Secondary Indexing](http://docs.basho.com/riak/latest/ops/advanced/configs/secondary-index/)
|
13
13
|
for your Riak installation.
|
14
14
|
|
15
15
|
## Rrod Installation
|
@@ -36,11 +36,16 @@ This will set you up with an pry environment with `Rrod` loaded.
|
|
36
36
|
Next you will wave your magic wand and enter the following incantations:
|
37
37
|
|
38
38
|
```ruby
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
class Person
|
40
|
+
attribute :name, String, index: true
|
41
|
+
attribute :age, Integer, index: true
|
42
|
+
end
|
43
|
+
|
44
|
+
hank = Person.new(name: "Hank", age: 40)
|
45
|
+
hank.save
|
46
|
+
|
47
|
+
@hank = Person.find_by name: "Hank"
|
48
|
+
@hank.age # => 40
|
44
49
|
```
|
45
50
|
|
46
51
|
`Rrod` lets you have arbitrary attributes on your ruby objects and persist and
|
data/lib/rrod/all.rb
CHANGED
@@ -15,10 +15,14 @@ require 'rrod/caster'
|
|
15
15
|
require 'rrod/caster/nested_model'
|
16
16
|
require 'rrod/model/attribute'
|
17
17
|
require 'rrod/model/attribute_methods'
|
18
|
+
require 'rrod/model/callbacks'
|
18
19
|
require 'rrod/model/collection'
|
19
20
|
require 'rrod/model/finders'
|
20
21
|
require 'rrod/model/persistence'
|
21
22
|
require 'rrod/model/schema'
|
22
23
|
require 'rrod/model/serialization'
|
24
|
+
require 'rrod/model/timestamps'
|
25
|
+
require 'rrod/model/validations'
|
26
|
+
require 'rrod/model/validations/associated_validator'
|
23
27
|
require 'rrod/model'
|
24
28
|
require 'rrod/query'
|
data/lib/rrod/configuration.rb
CHANGED
data/lib/rrod/model.rb
CHANGED
@@ -5,9 +5,11 @@ module Rrod
|
|
5
5
|
include AttributeMethods
|
6
6
|
include Persistence
|
7
7
|
include Serialization
|
8
|
-
|
9
|
-
include
|
10
|
-
|
8
|
+
|
9
|
+
include Validations
|
10
|
+
|
11
|
+
include Callbacks
|
12
|
+
include Timestamps
|
11
13
|
|
12
14
|
module ClassMethods
|
13
15
|
include Finders
|
data/lib/rrod/model/attribute.rb
CHANGED
@@ -10,7 +10,7 @@ module Rrod
|
|
10
10
|
->(value) { write_attribute name, value }
|
11
11
|
end
|
12
12
|
|
13
|
-
attr_accessor :model, :name, :type, :options, :default
|
13
|
+
attr_accessor :model, :name, :type, :options, :default, :index
|
14
14
|
|
15
15
|
# @param [Class] The class that is declaring the attribute
|
16
16
|
# @param [Symbol] The name of the attribute
|
@@ -22,11 +22,12 @@ module Rrod
|
|
22
22
|
self.type = type
|
23
23
|
self.default = options.delete(:default)
|
24
24
|
self.options = options
|
25
|
+
set_index if options.delete(:index)
|
25
26
|
end
|
26
27
|
|
27
28
|
# @return [Object] default value or result of call on default value
|
28
|
-
def default
|
29
|
-
@default.respond_to?(:call) ?
|
29
|
+
def default(instance)
|
30
|
+
@default.respond_to?(:call) ? instance.instance_exec(&@default) : @default
|
30
31
|
end
|
31
32
|
|
32
33
|
def define
|
@@ -63,6 +64,10 @@ module Rrod
|
|
63
64
|
def define_writer
|
64
65
|
model.send :define_method, "#{name}=", self.class.writer_definition(name)
|
65
66
|
end
|
67
|
+
|
68
|
+
def set_index
|
69
|
+
@index = Index.new(self)
|
70
|
+
end
|
66
71
|
|
67
72
|
def apply_validators
|
68
73
|
model.validates name, options if options.present?
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Rrod
|
2
2
|
module Model
|
3
3
|
module AttributeMethods
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
attr_accessor :_parent
|
4
7
|
|
5
8
|
def initialize(attributes = {})
|
6
9
|
@attributes = {}
|
@@ -36,7 +39,8 @@ module Rrod
|
|
36
39
|
# @param [Symbol, String] the key of the attribute to read
|
37
40
|
# @return the attribute at the given key
|
38
41
|
def read_attribute(key)
|
39
|
-
@attributes[key.to_s]
|
42
|
+
@attributes[key.to_s] ||
|
43
|
+
write_attribute(key, self.class.attributes[key.to_sym].try(:default, self))
|
40
44
|
end
|
41
45
|
alias :[] :read_attribute
|
42
46
|
|
@@ -44,7 +48,9 @@ module Rrod
|
|
44
48
|
# @param [Symbol, String] the key of the attribute to write
|
45
49
|
# @param the value to write to attributes
|
46
50
|
def write_attribute(key, value)
|
47
|
-
@attributes[key.to_s] = self.class.cast_attribute(key, value)
|
51
|
+
@attributes[key.to_s] = self.class.cast_attribute(key, value).tap { |attr|
|
52
|
+
attr._parent = self if attr.respond_to?(:_parent=)
|
53
|
+
}
|
48
54
|
end
|
49
55
|
alias :[]= :write_attribute
|
50
56
|
|
@@ -70,7 +76,6 @@ module Rrod
|
|
70
76
|
def method_missing(method_id, *args, &block)
|
71
77
|
method = method_id.to_s
|
72
78
|
return super unless magic_methods.include?(method)
|
73
|
-
|
74
79
|
accessor = method.ends_with?('=') ? :writer : :reader
|
75
80
|
send "define_singleton_#{accessor}", method.chomp('=')
|
76
81
|
send method_id, *args
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rrod
|
2
|
+
module Model
|
3
|
+
module Callbacks
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
extend ActiveModel::Callbacks
|
8
|
+
define_model_callbacks :assignment, :validation, :save, :create, :update
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes=(*)
|
12
|
+
run_callbacks(:assignment) { super }
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
run_callbacks(:validation) { super }
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(*)
|
20
|
+
run_callbacks(:create) if new?
|
21
|
+
run_callbacks(:update) if persisted?
|
22
|
+
run_callbacks(:save) { super }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -2,13 +2,22 @@ module Rrod
|
|
2
2
|
module Model
|
3
3
|
class Collection
|
4
4
|
include Enumerable
|
5
|
+
attr_accessor :model
|
5
6
|
|
6
|
-
|
7
|
+
# explicitly declare the public interface to the underlying collection
|
8
|
+
COLLECTION_INTERFACE = %w[clear count each length size [] first last]
|
7
9
|
|
8
|
-
|
10
|
+
delegate(*COLLECTION_INTERFACE, to: :collection)
|
11
|
+
|
12
|
+
def initialize(model, collection=[])
|
13
|
+
self.model = model
|
9
14
|
self.collection = collection
|
10
15
|
end
|
11
16
|
|
17
|
+
def _parent=(value)
|
18
|
+
each { |member| member._parent = value }
|
19
|
+
end
|
20
|
+
|
12
21
|
def collection=(collection)
|
13
22
|
raise InvalidCollectionTypeError.new unless collection.respond_to?(:each)
|
14
23
|
collection.map { |member| push member }
|
@@ -20,15 +29,24 @@ module Rrod
|
|
20
29
|
@collection ||= []
|
21
30
|
end
|
22
31
|
|
32
|
+
def build(attributes={})
|
33
|
+
push attributes
|
34
|
+
end
|
35
|
+
|
23
36
|
def push(value)
|
24
|
-
|
25
|
-
|
37
|
+
instance = model.rrod_cast(value)
|
38
|
+
raise InvalidMemberTypeError.new unless model === instance
|
39
|
+
collection.push(instance)
|
26
40
|
end
|
27
41
|
alias :<< :push
|
28
42
|
|
29
43
|
def serializable_hash(*)
|
30
44
|
collection.map(&:serializable_hash)
|
31
45
|
end
|
46
|
+
|
47
|
+
def valid?
|
48
|
+
collection.all?(&:valid?)
|
49
|
+
end
|
32
50
|
|
33
51
|
InvalidCollectionTypeError = Class.new(StandardError)
|
34
52
|
InvalidMemberTypeError = Class.new(StandardError)
|
data/lib/rrod/model/finders.rb
CHANGED
@@ -2,8 +2,12 @@ module Rrod
|
|
2
2
|
module Model
|
3
3
|
module Finders
|
4
4
|
|
5
|
-
def find(
|
6
|
-
|
5
|
+
def find(*ids)
|
6
|
+
if ids.length == 1
|
7
|
+
find_one(ids.first).tap { |found| raise NotFound.new if found.nil? }
|
8
|
+
else
|
9
|
+
find_many(ids)
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
def find_one(id)
|
@@ -13,45 +17,52 @@ module Rrod
|
|
13
17
|
raise e unless e.not_found?
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
query
|
18
|
-
|
19
|
-
|
20
|
-
find_one(query.id)
|
21
|
-
else
|
22
|
-
docs = search(query)
|
23
|
-
docs.any? ? found(nil, docs.first) : nil
|
24
|
-
end
|
20
|
+
def find_by(query)
|
21
|
+
find_by!(query)
|
22
|
+
rescue Rrod::Model::NotFound
|
23
|
+
nil
|
25
24
|
end
|
26
|
-
|
27
|
-
def
|
28
|
-
|
25
|
+
|
26
|
+
def find_by!(query)
|
27
|
+
return find(query[:id]) if query[:id]
|
28
|
+
find_all_by!(query).first
|
29
29
|
end
|
30
|
-
|
31
|
-
def find_all_by(
|
32
|
-
query
|
33
|
-
|
34
|
-
|
30
|
+
|
31
|
+
def find_all_by(query)
|
32
|
+
find_all_by!(query)
|
33
|
+
rescue Rrod::Model::NotFound
|
34
|
+
[]
|
35
35
|
end
|
36
36
|
|
37
37
|
def find_all_by!(attributes)
|
38
|
-
|
38
|
+
query = Query.new(attributes)
|
39
|
+
raise ArgumentError.new("Cannot search by id") if query.using_id?
|
40
|
+
search(query).map{ |doc| found(nil, doc) }
|
39
41
|
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
42
|
+
|
43
|
+
private
|
44
|
+
|
43
45
|
def search(query)
|
44
|
-
client.search(bucket_name, query.to_s)['docs']
|
46
|
+
records = client.search(bucket_name, query.to_s)['docs']
|
47
|
+
raise ArgumentError.new("NO RES ULTS") unless records.any?
|
48
|
+
records
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_many(ids)
|
52
|
+
bucket.get_many(ids).map{|doc| found(doc.first, doc.last.data) }
|
45
53
|
end
|
46
54
|
|
47
55
|
def found(key, data, robject=nil)
|
48
|
-
|
49
|
-
instance.id = key
|
56
|
+
instantiate(key, data).tap { |instance|
|
50
57
|
instance.robject = robject
|
51
58
|
instance.instance_variable_set(:@persisted, true)
|
52
59
|
}
|
53
60
|
end
|
54
61
|
|
62
|
+
def instantiate(key, data)
|
63
|
+
new(data).tap { |instance| instance.id = key if key }
|
64
|
+
end
|
65
|
+
|
55
66
|
end
|
56
67
|
|
57
68
|
NotFound = Class.new(StandardError)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Rrod
|
2
2
|
module Model
|
3
3
|
module Persistence
|
4
|
+
extend ActiveSupport::Concern # so we can override save
|
5
|
+
|
4
6
|
attr_accessor :robject
|
5
7
|
|
6
8
|
def persisted?
|
@@ -12,11 +14,10 @@ module Rrod
|
|
12
14
|
end
|
13
15
|
alias :new_record? :new?
|
14
16
|
|
15
|
-
def save
|
16
|
-
|
17
|
-
(valid? and persist) : persist
|
17
|
+
def save
|
18
|
+
persist
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
def persist
|
21
22
|
bucket.enable_index!
|
22
23
|
robject.raw_data = to_json
|
data/lib/rrod/model/schema.rb
CHANGED
@@ -5,7 +5,7 @@ module Rrod
|
|
5
5
|
def attributes
|
6
6
|
@attributes ||= {}
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def attribute(name, type, options={})
|
10
10
|
attributes[name.to_sym] = Attribute.new(self, name, type, options).define
|
11
11
|
end
|
@@ -15,6 +15,10 @@ module Rrod
|
|
15
15
|
attribute ? attribute.cast(value) : value
|
16
16
|
end
|
17
17
|
|
18
|
+
def nested_in(parent)
|
19
|
+
define_method(parent) { @_parent }
|
20
|
+
end
|
21
|
+
|
18
22
|
def schema?
|
19
23
|
attributes.any?
|
20
24
|
end
|
@@ -22,9 +26,11 @@ module Rrod
|
|
22
26
|
def rrod_cast(value)
|
23
27
|
return if value.nil?
|
24
28
|
return value if value.is_a?(Rrod::Model)
|
25
|
-
|
29
|
+
raise UncastableObjectError.new("#{value.inspect} cannot be rrod_cast") unless Hash === value
|
30
|
+
instantiate(nil, value)
|
26
31
|
end
|
27
32
|
|
28
|
-
end
|
33
|
+
end
|
34
|
+
UncastableObjectError = Class.new(StandardError)
|
29
35
|
end
|
30
36
|
end
|
@@ -2,10 +2,19 @@ module Rrod
|
|
2
2
|
module Model
|
3
3
|
module Serialization
|
4
4
|
|
5
|
+
def self.included(base)
|
6
|
+
base.send :include, ActiveModel::Serializers::JSON
|
7
|
+
base.send :include, ActiveModel::Serializers::Xml
|
8
|
+
end
|
9
|
+
|
5
10
|
def read_attribute_for_serialization(key)
|
6
11
|
value = read_attribute(key)
|
7
12
|
value.respond_to?(:serializable_hash) ? value.serializable_hash : value
|
8
13
|
end
|
14
|
+
|
15
|
+
def to_json(options={})
|
16
|
+
MultiJson.dump serializable_hash(options)
|
17
|
+
end
|
9
18
|
|
10
19
|
end
|
11
20
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rrod
|
2
|
+
module Model
|
3
|
+
module Timestamps
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def timestamps!
|
8
|
+
attribute :created_at, Time, default: -> { Time.now }
|
9
|
+
attribute :updated_at, Time, default: -> { Time.now }
|
10
|
+
before_save :touch
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def touch
|
15
|
+
tap { self.updated_at = Time.now }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|