cassandra_mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/README.rdoc +98 -0
  2. data/Rakefile.rb +11 -0
  3. data/lib/cassandra_mapper.rb +5 -0
  4. data/lib/cassandra_mapper/base.rb +19 -0
  5. data/lib/cassandra_mapper/connection.rb +9 -0
  6. data/lib/cassandra_mapper/core_ext/array/extract_options.rb +29 -0
  7. data/lib/cassandra_mapper/core_ext/array/wrap.rb +22 -0
  8. data/lib/cassandra_mapper/core_ext/class/inheritable_attributes.rb +232 -0
  9. data/lib/cassandra_mapper/core_ext/kernel/reporting.rb +62 -0
  10. data/lib/cassandra_mapper/core_ext/kernel/singleton_class.rb +13 -0
  11. data/lib/cassandra_mapper/core_ext/module/aliasing.rb +70 -0
  12. data/lib/cassandra_mapper/core_ext/module/attribute_accessors.rb +66 -0
  13. data/lib/cassandra_mapper/core_ext/object/duplicable.rb +65 -0
  14. data/lib/cassandra_mapper/core_ext/string/inflections.rb +160 -0
  15. data/lib/cassandra_mapper/core_ext/string/multibyte.rb +72 -0
  16. data/lib/cassandra_mapper/exceptions.rb +10 -0
  17. data/lib/cassandra_mapper/identity.rb +29 -0
  18. data/lib/cassandra_mapper/indexing.rb +465 -0
  19. data/lib/cassandra_mapper/observable.rb +36 -0
  20. data/lib/cassandra_mapper/persistence.rb +309 -0
  21. data/lib/cassandra_mapper/support/callbacks.rb +136 -0
  22. data/lib/cassandra_mapper/support/concern.rb +31 -0
  23. data/lib/cassandra_mapper/support/dependencies.rb +60 -0
  24. data/lib/cassandra_mapper/support/descendants_tracker.rb +41 -0
  25. data/lib/cassandra_mapper/support/inflections.rb +58 -0
  26. data/lib/cassandra_mapper/support/inflector.rb +7 -0
  27. data/lib/cassandra_mapper/support/inflector/inflections.rb +213 -0
  28. data/lib/cassandra_mapper/support/inflector/methods.rb +143 -0
  29. data/lib/cassandra_mapper/support/inflector/transliterate.rb +99 -0
  30. data/lib/cassandra_mapper/support/multibyte.rb +46 -0
  31. data/lib/cassandra_mapper/support/multibyte/utils.rb +62 -0
  32. data/lib/cassandra_mapper/support/observing.rb +218 -0
  33. data/lib/cassandra_mapper/support/support_callbacks.rb +593 -0
  34. data/test/test_helper.rb +11 -0
  35. data/test/unit/callbacks_test.rb +100 -0
  36. data/test/unit/identity_test.rb +51 -0
  37. data/test/unit/indexing_test.rb +406 -0
  38. data/test/unit/observer_test.rb +56 -0
  39. data/test/unit/persistence_test.rb +561 -0
  40. metadata +192 -0
@@ -0,0 +1,13 @@
1
+ module Kernel
2
+ # Returns the object's singleton class.
3
+ def singleton_class
4
+ class << self
5
+ self
6
+ end
7
+ end unless respond_to?(:singleton_class) # exists in 1.9.2
8
+
9
+ # class_eval on an object acts like singleton_class.class_eval.
10
+ def class_eval(*args, &block)
11
+ singleton_class.class_eval(*args, &block)
12
+ end
13
+ end
@@ -0,0 +1,70 @@
1
+ class Module
2
+ # Encapsulates the common pattern of:
3
+ #
4
+ # alias_method :foo_without_feature, :foo
5
+ # alias_method :foo, :foo_with_feature
6
+ #
7
+ # With this, you simply do:
8
+ #
9
+ # alias_method_chain :foo, :feature
10
+ #
11
+ # And both aliases are set up for you.
12
+ #
13
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
14
+ #
15
+ # alias_method_chain :foo?, :feature
16
+ #
17
+ # is equivalent to
18
+ #
19
+ # alias_method :foo_without_feature?, :foo?
20
+ # alias_method :foo?, :foo_with_feature?
21
+ #
22
+ # so you can safely chain foo, foo?, and foo! with the same feature.
23
+ def alias_method_chain(target, feature)
24
+ # Strip out punctuation on predicates or bang methods since
25
+ # e.g. target?_without_feature is not a valid method name.
26
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
27
+ yield(aliased_target, punctuation) if block_given?
28
+
29
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
30
+
31
+ alias_method without_method, target
32
+ alias_method target, with_method
33
+
34
+ case
35
+ when public_method_defined?(without_method)
36
+ public target
37
+ when protected_method_defined?(without_method)
38
+ protected target
39
+ when private_method_defined?(without_method)
40
+ private target
41
+ end
42
+ end
43
+
44
+ # Allows you to make aliases for attributes, which includes
45
+ # getter, setter, and query methods.
46
+ #
47
+ # Example:
48
+ #
49
+ # class Content < ActiveRecord::Base
50
+ # # has a title attribute
51
+ # end
52
+ #
53
+ # class Email < Content
54
+ # alias_attribute :subject, :title
55
+ # end
56
+ #
57
+ # e = Email.find(1)
58
+ # e.title # => "Superstars"
59
+ # e.subject # => "Superstars"
60
+ # e.subject? # => true
61
+ # e.subject = "Megastars"
62
+ # e.title # => "Megastars"
63
+ def alias_attribute(new_name, old_name)
64
+ module_eval <<-STR, __FILE__, __LINE__ + 1
65
+ def #{new_name}; self.#{old_name}; end # def subject; self.title; end
66
+ def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
67
+ def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
68
+ STR
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ require 'cassandra_mapper/core_ext/array/extract_options'
2
+
3
+ class Module
4
+ def mattr_reader(*syms)
5
+ options = syms.extract_options!
6
+ syms.each do |sym|
7
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
8
+ unless defined? @@#{sym}
9
+ @@#{sym} = nil
10
+ end
11
+
12
+ def self.#{sym}
13
+ @@#{sym}
14
+ end
15
+ EOS
16
+
17
+ unless options[:instance_reader] == false
18
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
19
+ def #{sym}
20
+ @@#{sym}
21
+ end
22
+ EOS
23
+ end
24
+ end
25
+ end
26
+
27
+ def mattr_writer(*syms)
28
+ options = syms.extract_options!
29
+ syms.each do |sym|
30
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
31
+ unless defined? @@#{sym}
32
+ @@#{sym} = nil
33
+ end
34
+
35
+ def self.#{sym}=(obj)
36
+ @@#{sym} = obj
37
+ end
38
+ EOS
39
+
40
+ unless options[:instance_writer] == false
41
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
42
+ def #{sym}=(obj)
43
+ @@#{sym} = obj
44
+ end
45
+ EOS
46
+ end
47
+ end
48
+ end
49
+
50
+ # Extends the module object with module and instance accessors for class attributes,
51
+ # just like the native attr* accessors for instance attributes.
52
+ #
53
+ # module AppConfiguration
54
+ # mattr_accessor :google_api_key
55
+ # self.google_api_key = "123456789"
56
+ #
57
+ # mattr_accessor :paypal_url
58
+ # self.paypal_url = "www.sandbox.paypal.com"
59
+ # end
60
+ #
61
+ # AppConfiguration.google_api_key = "overriding the api key!"
62
+ def mattr_accessor(*syms)
63
+ mattr_reader(*syms)
64
+ mattr_writer(*syms)
65
+ end
66
+ end
@@ -0,0 +1,65 @@
1
+ # Most objects are cloneable, but not all. For example you can't dup +nil+:
2
+ #
3
+ # nil.dup # => TypeError: can't dup NilClass
4
+ #
5
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
6
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
7
+ # use an optimistic approach and are ready to catch an exception, say:
8
+ #
9
+ # arbitrary_object.dup rescue object
10
+ #
11
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
12
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
13
+ # is often triggered.
14
+ #
15
+ # That's why we hardcode the following cases and check duplicable? instead of
16
+ # using that rescue idiom.
17
+ class Object
18
+ # Can you safely .dup this object?
19
+ # False for nil, false, true, symbols, numbers, class and module objects; true otherwise.
20
+ def duplicable?
21
+ true
22
+ end
23
+ end
24
+
25
+ class NilClass #:nodoc:
26
+ def duplicable?
27
+ false
28
+ end
29
+ end
30
+
31
+ class FalseClass #:nodoc:
32
+ def duplicable?
33
+ false
34
+ end
35
+ end
36
+
37
+ class TrueClass #:nodoc:
38
+ def duplicable?
39
+ false
40
+ end
41
+ end
42
+
43
+ class Symbol #:nodoc:
44
+ def duplicable?
45
+ false
46
+ end
47
+ end
48
+
49
+ class Numeric #:nodoc:
50
+ def duplicable?
51
+ false
52
+ end
53
+ end
54
+
55
+ class Class #:nodoc:
56
+ def duplicable?
57
+ false
58
+ end
59
+ end
60
+
61
+ class Module #:nodoc:
62
+ def duplicable?
63
+ false
64
+ end
65
+ end
@@ -0,0 +1,160 @@
1
+ # String inflections define new methods on the String class to transform names for different purposes.
2
+ # For instance, you can figure out the name of a database from the name of a class.
3
+ #
4
+ # "ScaleScore".tableize # => "scale_scores"
5
+ #
6
+ class String
7
+ # Returns the plural form of the word in the string.
8
+ #
9
+ # "post".pluralize # => "posts"
10
+ # "octopus".pluralize # => "octopi"
11
+ # "sheep".pluralize # => "sheep"
12
+ # "words".pluralize # => "words"
13
+ # "the blue mailman".pluralize # => "the blue mailmen"
14
+ # "CamelOctopus".pluralize # => "CamelOctopi"
15
+ def pluralize
16
+ CassandraMapper::Support::Inflector.pluralize(self)
17
+ end
18
+
19
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
20
+ #
21
+ # "posts".singularize # => "post"
22
+ # "octopi".singularize # => "octopus"
23
+ # "sheep".singularize # => "sheep"
24
+ # "word".singularize # => "word"
25
+ # "the blue mailmen".singularize # => "the blue mailman"
26
+ # "CamelOctopi".singularize # => "CamelOctopus"
27
+ def singularize
28
+ CassandraMapper::Support::Inflector.singularize(self)
29
+ end
30
+
31
+ # +constantize+ tries to find a declared constant with the name specified
32
+ # in the string. It raises a NameError when the name is not in CamelCase
33
+ # or is not initialized.
34
+ #
35
+ # Examples
36
+ # "Module".constantize # => Module
37
+ # "Class".constantize # => Class
38
+ def constantize
39
+ CassandraMapper::Support::Inflector.constantize(self)
40
+ end
41
+
42
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
43
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
44
+ #
45
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
46
+ #
47
+ # "active_record".camelize # => "ActiveRecord"
48
+ # "active_record".camelize(:lower) # => "activeRecord"
49
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
50
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
51
+ def camelize(first_letter = :upper)
52
+ case first_letter
53
+ when :upper then CassandraMapper::Support::Inflector.camelize(self, true)
54
+ when :lower then CassandraMapper::Support::Inflector.camelize(self, false)
55
+ end
56
+ end
57
+ alias_method :camelcase, :camelize
58
+
59
+ # Capitalizes all the words and replaces some characters in the string to create
60
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
61
+ # used in the Rails internals.
62
+ #
63
+ # +titleize+ is also aliased as +titlecase+.
64
+ #
65
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
66
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
67
+ def titleize
68
+ CassandraMapper::Support::Inflector.titleize(self)
69
+ end
70
+ alias_method :titlecase, :titleize
71
+
72
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
73
+ #
74
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
75
+ #
76
+ # "ActiveRecord".underscore # => "active_record"
77
+ # "ActiveRecord::Errors".underscore # => active_record/errors
78
+ def underscore
79
+ CassandraMapper::Support::Inflector.underscore(self)
80
+ end
81
+
82
+ # Replaces underscores with dashes in the string.
83
+ #
84
+ # "puni_puni" # => "puni-puni"
85
+ def dasherize
86
+ CassandraMapper::Support::Inflector.dasherize(self)
87
+ end
88
+
89
+ # Removes the module part from the constant expression in the string.
90
+ #
91
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
92
+ # "Inflections".demodulize # => "Inflections"
93
+ def demodulize
94
+ CassandraMapper::Support::Inflector.demodulize(self)
95
+ end
96
+
97
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
98
+ #
99
+ # ==== Examples
100
+ #
101
+ # class Person
102
+ # def to_param
103
+ # "#{id}-#{name.parameterize}"
104
+ # end
105
+ # end
106
+ #
107
+ # @person = Person.find(1)
108
+ # # => #<Person id: 1, name: "Donald E. Knuth">
109
+ #
110
+ # <%= link_to(@person.name, person_path %>
111
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
112
+ def parameterize(sep = '-')
113
+ CassandraMapper::Support::Inflector.parameterize(self, sep)
114
+ end
115
+
116
+ # Creates the name of a table like Rails does for models to table names. This method
117
+ # uses the +pluralize+ method on the last word in the string.
118
+ #
119
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
120
+ # "egg_and_ham".tableize # => "egg_and_hams"
121
+ # "fancyCategory".tableize # => "fancy_categories"
122
+ def tableize
123
+ CassandraMapper::Support::Inflector.tableize(self)
124
+ end
125
+
126
+ # Create a class name from a plural table name like Rails does for table names to models.
127
+ # Note that this returns a string and not a class. (To convert to an actual class
128
+ # follow +classify+ with +constantize+.)
129
+ #
130
+ # "egg_and_hams".classify # => "EggAndHam"
131
+ # "posts".classify # => "Post"
132
+ #
133
+ # Singular names are not handled correctly.
134
+ #
135
+ # "business".classify # => "Busines"
136
+ def classify
137
+ CassandraMapper::Support::Inflector.classify(self)
138
+ end
139
+
140
+ # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
141
+ # Like +titleize+, this is meant for creating pretty output.
142
+ #
143
+ # "employee_salary" # => "Employee salary"
144
+ # "author_id" # => "Author"
145
+ def humanize
146
+ CassandraMapper::Support::Inflector.humanize(self)
147
+ end
148
+
149
+ # Creates a foreign key name from a class name.
150
+ # +separate_class_name_and_id_with_underscore+ sets whether
151
+ # the method should put '_' between the name and 'id'.
152
+ #
153
+ # Examples
154
+ # "Message".foreign_key # => "message_id"
155
+ # "Message".foreign_key(false) # => "messageid"
156
+ # "Admin::Post".foreign_key # => "post_id"
157
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
158
+ CassandraMapper::Support::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
159
+ end
160
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+ require 'cassandra_mapper/support/multibyte'
3
+
4
+ class String
5
+ if '1.9'.respond_to?(:force_encoding)
6
+ # == Multibyte proxy
7
+ #
8
+ # +mb_chars+ is a multibyte safe proxy for string methods.
9
+ #
10
+ # In Ruby 1.8 and older it creates and returns an instance of the CassandraMapper::Support::Multibyte::Chars class which
11
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
12
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
13
+ #
14
+ # name = 'Claus Müller'
15
+ # name.reverse #=> "rell??M sualC"
16
+ # name.length #=> 13
17
+ #
18
+ # name.mb_chars.reverse.to_s #=> "rellüM sualC"
19
+ # name.mb_chars.length #=> 12
20
+ #
21
+ # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
22
+ # it becomes easy to run one version of your code on multiple Ruby versions.
23
+ #
24
+ # == Method chaining
25
+ #
26
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
27
+ # method chaining on the result of any of these methods.
28
+ #
29
+ # name.mb_chars.reverse.length #=> 12
30
+ #
31
+ # == Interoperability and configuration
32
+ #
33
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
34
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
35
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
36
+ #
37
+ # For more information about the methods defined on the Chars proxy see CassandraMapper::Support::Multibyte::Chars. For
38
+ # information about how to change the default Multibyte behaviour see CassandraMapper::Support::Multibyte.
39
+ def mb_chars
40
+ if CassandraMapper::Support::Multibyte.proxy_class.consumes?(self)
41
+ CassandraMapper::Support::Multibyte.proxy_class.new(self)
42
+ else
43
+ self
44
+ end
45
+ end
46
+
47
+ def is_utf8? #:nodoc
48
+ case encoding
49
+ when Encoding::UTF_8
50
+ valid_encoding?
51
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
52
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
53
+ else
54
+ false
55
+ end
56
+ end
57
+ else
58
+ def mb_chars
59
+ if CassandraMapper::Support::Multibyte.proxy_class.wants?(self)
60
+ CassandraMapper::Support::Multibyte.proxy_class.new(self)
61
+ else
62
+ self
63
+ end
64
+ end
65
+
66
+ # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
67
+ # them), returns false otherwise.
68
+ def is_utf8?
69
+ CassandraMapper::Support::Multibyte::Chars.consumes?(self)
70
+ end
71
+ end
72
+ end