better-ripple 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +17 -0
- data/README.md +182 -0
- data/RELEASE_NOTES.md +284 -0
- data/better-ripple.gemspec +55 -0
- data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
- data/lib/rails/generators/ripple/configuration/templates/ripple.yml +25 -0
- data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
- data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
- data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
- data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
- data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
- data/lib/rails/generators/ripple/model/templates/model.rb.erb +10 -0
- data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
- data/lib/rails/generators/ripple/observer/templates/observer.rb.erb +2 -0
- data/lib/rails/generators/ripple/test/templates/cucumber.rb.erb +7 -0
- data/lib/rails/generators/ripple/test/test_generator.rb +44 -0
- data/lib/rails/generators/ripple_generator.rb +79 -0
- data/lib/ripple.rb +86 -0
- data/lib/ripple/associations.rb +380 -0
- data/lib/ripple/associations/embedded.rb +35 -0
- data/lib/ripple/associations/instantiators.rb +26 -0
- data/lib/ripple/associations/linked.rb +65 -0
- data/lib/ripple/associations/many.rb +38 -0
- data/lib/ripple/associations/many_embedded_proxy.rb +39 -0
- data/lib/ripple/associations/many_linked_proxy.rb +66 -0
- data/lib/ripple/associations/many_reference_proxy.rb +95 -0
- data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
- data/lib/ripple/associations/one.rb +20 -0
- data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
- data/lib/ripple/associations/one_key_proxy.rb +58 -0
- data/lib/ripple/associations/one_linked_proxy.rb +26 -0
- data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
- data/lib/ripple/associations/proxy.rb +118 -0
- data/lib/ripple/attribute_methods.rb +132 -0
- data/lib/ripple/attribute_methods/dirty.rb +59 -0
- data/lib/ripple/attribute_methods/query.rb +34 -0
- data/lib/ripple/attribute_methods/read.rb +28 -0
- data/lib/ripple/attribute_methods/write.rb +25 -0
- data/lib/ripple/callbacks.rb +71 -0
- data/lib/ripple/conflict/basic_resolver.rb +86 -0
- data/lib/ripple/conflict/document_hooks.rb +46 -0
- data/lib/ripple/conflict/resolver.rb +79 -0
- data/lib/ripple/conflict/test_helper.rb +34 -0
- data/lib/ripple/conversion.rb +29 -0
- data/lib/ripple/core_ext.rb +3 -0
- data/lib/ripple/core_ext/casting.rb +151 -0
- data/lib/ripple/core_ext/indexes.rb +89 -0
- data/lib/ripple/core_ext/object.rb +8 -0
- data/lib/ripple/document.rb +105 -0
- data/lib/ripple/document/bucket_access.rb +25 -0
- data/lib/ripple/document/finders.rb +131 -0
- data/lib/ripple/document/key.rb +35 -0
- data/lib/ripple/document/link.rb +30 -0
- data/lib/ripple/document/persistence.rb +130 -0
- data/lib/ripple/embedded_document.rb +63 -0
- data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
- data/lib/ripple/embedded_document/finders.rb +26 -0
- data/lib/ripple/embedded_document/persistence.rb +75 -0
- data/lib/ripple/i18n.rb +5 -0
- data/lib/ripple/indexes.rb +151 -0
- data/lib/ripple/inspection.rb +32 -0
- data/lib/ripple/locale/en.yml +26 -0
- data/lib/ripple/locale/fr.yml +24 -0
- data/lib/ripple/nested_attributes.rb +275 -0
- data/lib/ripple/observable.rb +28 -0
- data/lib/ripple/properties.rb +74 -0
- data/lib/ripple/property_type_mismatch.rb +12 -0
- data/lib/ripple/railtie.rb +26 -0
- data/lib/ripple/railties/ripple.rake +103 -0
- data/lib/ripple/serialization.rb +82 -0
- data/lib/ripple/test_server.rb +35 -0
- data/lib/ripple/timestamps.rb +25 -0
- data/lib/ripple/translation.rb +18 -0
- data/lib/ripple/validations.rb +65 -0
- data/lib/ripple/validations/associated_validator.rb +43 -0
- data/lib/ripple/version.rb +3 -0
- metadata +310 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'ripple/associations/proxy'
|
2
|
+
require 'ripple/validations/associated_validator'
|
3
|
+
|
4
|
+
module Ripple
|
5
|
+
module Associations
|
6
|
+
module Embedded
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
lazy_load_validates_associated
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def lazy_load_validates_associated
|
16
|
+
return if @owner.class.validators_on(@reflection.name).any? {|v| Ripple::Validations::AssociatedValidator === v}
|
17
|
+
@owner.class.validates @reflection.name, :associated => true
|
18
|
+
end
|
19
|
+
|
20
|
+
def assign_references(docs)
|
21
|
+
Array.wrap(docs).each do |doc|
|
22
|
+
next unless doc.respond_to?(:_parent_document=)
|
23
|
+
doc._parent_document = owner
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def instantiate_target(*args)
|
28
|
+
doc = super
|
29
|
+
assign_references(doc)
|
30
|
+
doc
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ripple/associations'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Associations
|
5
|
+
module Instantiators
|
6
|
+
|
7
|
+
def build(attrs={})
|
8
|
+
instantiate_target(:new, attrs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(attrs={})
|
12
|
+
instantiate_target(:create, attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create!(attrs={})
|
16
|
+
instantiate_target(:create!, attrs)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def instantiate_target
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ripple/associations'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Ripple
|
5
|
+
module Associations
|
6
|
+
module Linked
|
7
|
+
def replace(value)
|
8
|
+
@reflection.verify_type!(value, @owner)
|
9
|
+
@owner.robject.links -= links
|
10
|
+
Array.wrap(value).compact.each do |doc|
|
11
|
+
@owner.robject.links << doc.to_link(@reflection.link_tag)
|
12
|
+
end
|
13
|
+
loaded
|
14
|
+
@keys = nil
|
15
|
+
@target = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def replace_links(value)
|
19
|
+
@owner.robject.links -= links
|
20
|
+
Array(value).each do |link|
|
21
|
+
@owner.robject.links << link
|
22
|
+
end
|
23
|
+
reset
|
24
|
+
end
|
25
|
+
|
26
|
+
def keys
|
27
|
+
@keys ||= Set.new(links.map { |l| l.key })
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset
|
31
|
+
super
|
32
|
+
@keys = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def include?(document)
|
36
|
+
return false unless document.respond_to?(:robject)
|
37
|
+
|
38
|
+
# TODO: when we allow polymorphic assocations, this will have to change
|
39
|
+
# since @reflection.bucket_name will be '_' in that case.
|
40
|
+
return false unless document.robject.bucket.name == @reflection.bucket_name
|
41
|
+
keys.include?(document.key)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def links
|
46
|
+
@owner.robject.links.select(&@reflection.link_filter)
|
47
|
+
end
|
48
|
+
|
49
|
+
def robjects
|
50
|
+
walk_result = begin
|
51
|
+
@owner.robject.walk(*Array(@reflection.link_spec)).first || []
|
52
|
+
rescue
|
53
|
+
[]
|
54
|
+
end
|
55
|
+
|
56
|
+
# We can get more robject results that we have links when there is conflict,
|
57
|
+
# since link-walking returns the robjects for the union of all sibling links.
|
58
|
+
# Here, we filter out robjects that should not be included.
|
59
|
+
walk_result.select do |robject|
|
60
|
+
links.include?(robject.to_link(@reflection.link_tag))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'ripple/associations'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Associations
|
5
|
+
module Many
|
6
|
+
include Instantiators
|
7
|
+
|
8
|
+
def to_ary
|
9
|
+
load_target
|
10
|
+
Array === target ? target.to_ary : Array.wrap(target)
|
11
|
+
end
|
12
|
+
|
13
|
+
def count
|
14
|
+
load_target
|
15
|
+
target.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
super
|
20
|
+
@target = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(value)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :push, :<<
|
28
|
+
alias_method :concat, :<<
|
29
|
+
|
30
|
+
protected
|
31
|
+
def instantiate_target(instantiator, attrs={})
|
32
|
+
doc = klass.send(instantiator, attrs)
|
33
|
+
self << doc
|
34
|
+
doc
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'ripple/associations/proxy'
|
2
|
+
require 'ripple/associations/many'
|
3
|
+
require 'ripple/associations/embedded'
|
4
|
+
|
5
|
+
module Ripple
|
6
|
+
module Associations
|
7
|
+
class ManyEmbeddedProxy < Proxy
|
8
|
+
include Many
|
9
|
+
include Embedded
|
10
|
+
|
11
|
+
def <<(docs)
|
12
|
+
load_target
|
13
|
+
docs = Array.wrap(docs)
|
14
|
+
@reflection.verify_type!(docs, @owner)
|
15
|
+
assign_references(docs)
|
16
|
+
@target += docs
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace(docs)
|
21
|
+
@reflection.verify_type!(docs, @owner)
|
22
|
+
@_docs = docs.map { |doc| attrs = doc.respond_to?(:attributes_for_persistence) ? doc.attributes_for_persistence : doc }
|
23
|
+
assign_references(docs)
|
24
|
+
reset
|
25
|
+
@_docs
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def find_target
|
30
|
+
(@_docs || []).map do |attrs|
|
31
|
+
klass.instantiate(attrs).tap do |doc|
|
32
|
+
assign_references(doc)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ripple/associations/proxy'
|
2
|
+
require 'ripple/associations/many'
|
3
|
+
require 'ripple/associations/linked'
|
4
|
+
|
5
|
+
module Ripple
|
6
|
+
module Associations
|
7
|
+
class ManyLinkedProxy < Proxy
|
8
|
+
include Many
|
9
|
+
include Linked
|
10
|
+
|
11
|
+
def count
|
12
|
+
# avoid having to load all documents by using our keys set instead
|
13
|
+
keys.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(value)
|
17
|
+
if loaded?
|
18
|
+
new_target = @target.concat(Array.wrap(value))
|
19
|
+
replace new_target
|
20
|
+
else
|
21
|
+
@reflection.verify_type!([value], @owner)
|
22
|
+
@owner.robject.links << value.to_link(@reflection.link_tag)
|
23
|
+
appended_documents << value
|
24
|
+
@keys = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(value)
|
31
|
+
load_target
|
32
|
+
@target.delete(value)
|
33
|
+
replace @target
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset
|
38
|
+
@appended_documents = nil
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
def loaded_documents
|
43
|
+
(super + appended_documents).uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def find_target
|
49
|
+
robjs = robjects
|
50
|
+
|
51
|
+
robjs.delete_if do |robj|
|
52
|
+
appended_documents.any? do |doc|
|
53
|
+
doc.key == robj.key &&
|
54
|
+
doc.class.bucket_name == robj.bucket.name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
appended_documents + robjs.map {|robj| klass.send(:instantiate, robj) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def appended_documents
|
62
|
+
@appended_documents ||= []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'ripple/associations/proxy'
|
2
|
+
require 'ripple/associations/many'
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Ripple
|
7
|
+
module Associations
|
8
|
+
class ManyReferenceProxy < Proxy
|
9
|
+
include Many
|
10
|
+
|
11
|
+
def <<(value)
|
12
|
+
values = Array.wrap(value)
|
13
|
+
@reflection.verify_type!(values, @owner)
|
14
|
+
|
15
|
+
values.each {|v| assign_key(v) }
|
16
|
+
load_target
|
17
|
+
@target.merge values
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def replace(value)
|
23
|
+
@reflection.verify_type!(value, @owner)
|
24
|
+
delete_all
|
25
|
+
Array.wrap(value).compact.each do |doc|
|
26
|
+
assign_key(doc)
|
27
|
+
end
|
28
|
+
loaded
|
29
|
+
@keys = nil
|
30
|
+
@target = Set.new(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_all
|
34
|
+
load_target
|
35
|
+
@target.each do |e|
|
36
|
+
delete(e)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(value)
|
41
|
+
load_target
|
42
|
+
assign_key(value, nil)
|
43
|
+
@target.delete(value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def target
|
47
|
+
load_target
|
48
|
+
@target.to_a
|
49
|
+
end
|
50
|
+
|
51
|
+
def keys
|
52
|
+
@keys ||= Ripple.client.search(klass.bucket_name, "#{key_name}: #{@owner.key}")["response"]["docs"].inject(Set.new) do |set, search_document|
|
53
|
+
set << search_document["id"]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def reset
|
58
|
+
@keys = nil
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
def include?(document)
|
63
|
+
return false unless document.class.respond_to?(:bucket_name)
|
64
|
+
|
65
|
+
return false unless document.class.bucket_name == @reflection.bucket_name
|
66
|
+
keys.include?(document.key)
|
67
|
+
end
|
68
|
+
|
69
|
+
def count
|
70
|
+
if loaded?
|
71
|
+
@target.count
|
72
|
+
else
|
73
|
+
keys.count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
def find_target
|
79
|
+
Set.new(klass.find(keys.to_a))
|
80
|
+
end
|
81
|
+
|
82
|
+
def key_name
|
83
|
+
"#{@owner.class.name.underscore}_key"
|
84
|
+
end
|
85
|
+
|
86
|
+
def assign_key(target, key=@owner.key)
|
87
|
+
if target.new_record?
|
88
|
+
target.send("#{key_name}=", key)
|
89
|
+
else
|
90
|
+
target.update_attribute(key_name, key)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'ripple/associations/proxy'
|
2
|
+
require 'ripple/associations/many'
|
3
|
+
|
4
|
+
module Ripple
|
5
|
+
module Associations
|
6
|
+
class ManyStoredKeyProxy < Proxy
|
7
|
+
include Many
|
8
|
+
|
9
|
+
def count
|
10
|
+
keys.size
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(value)
|
14
|
+
@reflection.verify_type!([value], @owner)
|
15
|
+
|
16
|
+
raise "Unable to append if the document isn't first saved." if value.new_record?
|
17
|
+
load_target
|
18
|
+
@target << value
|
19
|
+
keys << value.key
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace(value)
|
25
|
+
@reflection.verify_type!(value, @owner)
|
26
|
+
|
27
|
+
reset_owner_keys
|
28
|
+
value.each { |doc| self << doc }
|
29
|
+
@target = value
|
30
|
+
loaded
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(value)
|
34
|
+
keys.delete(value.key)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def keys
|
39
|
+
if @owner.send(keys_name).nil?
|
40
|
+
reset_owner_keys
|
41
|
+
end
|
42
|
+
|
43
|
+
@owner.send(keys_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset
|
47
|
+
super
|
48
|
+
self.owner_keys = @owner.robject.data ? @owner.robject.data[keys_name] : []
|
49
|
+
end
|
50
|
+
|
51
|
+
def include?(document)
|
52
|
+
return false unless document.respond_to?(:robject)
|
53
|
+
return false unless document.robject.bucket.name == @reflection.bucket_name
|
54
|
+
keys.include?(document.key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def reset_owner_keys
|
58
|
+
self.owner_keys = []
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
protected
|
63
|
+
def find_target
|
64
|
+
klass.find(keys.to_a)
|
65
|
+
end
|
66
|
+
|
67
|
+
def keys_name
|
68
|
+
"#{@reflection.name.to_s.singularize}_keys"
|
69
|
+
end
|
70
|
+
|
71
|
+
def owner_keys=(new_keys)
|
72
|
+
@owner.send("#{keys_name}=", @owner.class.properties[keys_name].type.new(new_keys))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|