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.
Files changed (77) hide show
  1. data/LICENSE +17 -0
  2. data/README.md +182 -0
  3. data/RELEASE_NOTES.md +284 -0
  4. data/better-ripple.gemspec +55 -0
  5. data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
  6. data/lib/rails/generators/ripple/configuration/templates/ripple.yml +25 -0
  7. data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
  8. data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
  9. data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
  10. data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
  11. data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
  12. data/lib/rails/generators/ripple/model/templates/model.rb.erb +10 -0
  13. data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
  14. data/lib/rails/generators/ripple/observer/templates/observer.rb.erb +2 -0
  15. data/lib/rails/generators/ripple/test/templates/cucumber.rb.erb +7 -0
  16. data/lib/rails/generators/ripple/test/test_generator.rb +44 -0
  17. data/lib/rails/generators/ripple_generator.rb +79 -0
  18. data/lib/ripple.rb +86 -0
  19. data/lib/ripple/associations.rb +380 -0
  20. data/lib/ripple/associations/embedded.rb +35 -0
  21. data/lib/ripple/associations/instantiators.rb +26 -0
  22. data/lib/ripple/associations/linked.rb +65 -0
  23. data/lib/ripple/associations/many.rb +38 -0
  24. data/lib/ripple/associations/many_embedded_proxy.rb +39 -0
  25. data/lib/ripple/associations/many_linked_proxy.rb +66 -0
  26. data/lib/ripple/associations/many_reference_proxy.rb +95 -0
  27. data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
  28. data/lib/ripple/associations/one.rb +20 -0
  29. data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
  30. data/lib/ripple/associations/one_key_proxy.rb +58 -0
  31. data/lib/ripple/associations/one_linked_proxy.rb +26 -0
  32. data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
  33. data/lib/ripple/associations/proxy.rb +118 -0
  34. data/lib/ripple/attribute_methods.rb +132 -0
  35. data/lib/ripple/attribute_methods/dirty.rb +59 -0
  36. data/lib/ripple/attribute_methods/query.rb +34 -0
  37. data/lib/ripple/attribute_methods/read.rb +28 -0
  38. data/lib/ripple/attribute_methods/write.rb +25 -0
  39. data/lib/ripple/callbacks.rb +71 -0
  40. data/lib/ripple/conflict/basic_resolver.rb +86 -0
  41. data/lib/ripple/conflict/document_hooks.rb +46 -0
  42. data/lib/ripple/conflict/resolver.rb +79 -0
  43. data/lib/ripple/conflict/test_helper.rb +34 -0
  44. data/lib/ripple/conversion.rb +29 -0
  45. data/lib/ripple/core_ext.rb +3 -0
  46. data/lib/ripple/core_ext/casting.rb +151 -0
  47. data/lib/ripple/core_ext/indexes.rb +89 -0
  48. data/lib/ripple/core_ext/object.rb +8 -0
  49. data/lib/ripple/document.rb +105 -0
  50. data/lib/ripple/document/bucket_access.rb +25 -0
  51. data/lib/ripple/document/finders.rb +131 -0
  52. data/lib/ripple/document/key.rb +35 -0
  53. data/lib/ripple/document/link.rb +30 -0
  54. data/lib/ripple/document/persistence.rb +130 -0
  55. data/lib/ripple/embedded_document.rb +63 -0
  56. data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
  57. data/lib/ripple/embedded_document/finders.rb +26 -0
  58. data/lib/ripple/embedded_document/persistence.rb +75 -0
  59. data/lib/ripple/i18n.rb +5 -0
  60. data/lib/ripple/indexes.rb +151 -0
  61. data/lib/ripple/inspection.rb +32 -0
  62. data/lib/ripple/locale/en.yml +26 -0
  63. data/lib/ripple/locale/fr.yml +24 -0
  64. data/lib/ripple/nested_attributes.rb +275 -0
  65. data/lib/ripple/observable.rb +28 -0
  66. data/lib/ripple/properties.rb +74 -0
  67. data/lib/ripple/property_type_mismatch.rb +12 -0
  68. data/lib/ripple/railtie.rb +26 -0
  69. data/lib/ripple/railties/ripple.rake +103 -0
  70. data/lib/ripple/serialization.rb +82 -0
  71. data/lib/ripple/test_server.rb +35 -0
  72. data/lib/ripple/timestamps.rb +25 -0
  73. data/lib/ripple/translation.rb +18 -0
  74. data/lib/ripple/validations.rb +65 -0
  75. data/lib/ripple/validations/associated_validator.rb +43 -0
  76. data/lib/ripple/version.rb +3 -0
  77. 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