cassandra_mapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +98 -0
- data/Rakefile.rb +11 -0
- data/lib/cassandra_mapper.rb +5 -0
- data/lib/cassandra_mapper/base.rb +19 -0
- data/lib/cassandra_mapper/connection.rb +9 -0
- data/lib/cassandra_mapper/core_ext/array/extract_options.rb +29 -0
- data/lib/cassandra_mapper/core_ext/array/wrap.rb +22 -0
- data/lib/cassandra_mapper/core_ext/class/inheritable_attributes.rb +232 -0
- data/lib/cassandra_mapper/core_ext/kernel/reporting.rb +62 -0
- data/lib/cassandra_mapper/core_ext/kernel/singleton_class.rb +13 -0
- data/lib/cassandra_mapper/core_ext/module/aliasing.rb +70 -0
- data/lib/cassandra_mapper/core_ext/module/attribute_accessors.rb +66 -0
- data/lib/cassandra_mapper/core_ext/object/duplicable.rb +65 -0
- data/lib/cassandra_mapper/core_ext/string/inflections.rb +160 -0
- data/lib/cassandra_mapper/core_ext/string/multibyte.rb +72 -0
- data/lib/cassandra_mapper/exceptions.rb +10 -0
- data/lib/cassandra_mapper/identity.rb +29 -0
- data/lib/cassandra_mapper/indexing.rb +465 -0
- data/lib/cassandra_mapper/observable.rb +36 -0
- data/lib/cassandra_mapper/persistence.rb +309 -0
- data/lib/cassandra_mapper/support/callbacks.rb +136 -0
- data/lib/cassandra_mapper/support/concern.rb +31 -0
- data/lib/cassandra_mapper/support/dependencies.rb +60 -0
- data/lib/cassandra_mapper/support/descendants_tracker.rb +41 -0
- data/lib/cassandra_mapper/support/inflections.rb +58 -0
- data/lib/cassandra_mapper/support/inflector.rb +7 -0
- data/lib/cassandra_mapper/support/inflector/inflections.rb +213 -0
- data/lib/cassandra_mapper/support/inflector/methods.rb +143 -0
- data/lib/cassandra_mapper/support/inflector/transliterate.rb +99 -0
- data/lib/cassandra_mapper/support/multibyte.rb +46 -0
- data/lib/cassandra_mapper/support/multibyte/utils.rb +62 -0
- data/lib/cassandra_mapper/support/observing.rb +218 -0
- data/lib/cassandra_mapper/support/support_callbacks.rb +593 -0
- data/test/test_helper.rb +11 -0
- data/test/unit/callbacks_test.rb +100 -0
- data/test/unit/identity_test.rb +51 -0
- data/test/unit/indexing_test.rb +406 -0
- data/test/unit/observer_test.rb +56 -0
- data/test/unit/persistence_test.rb +561 -0
- 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
|