activecouch 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +42 -0
  3. data/Rakefile +31 -0
  4. data/VERSION +1 -0
  5. data/lib/active_couch.rb +18 -0
  6. data/lib/active_couch/associations.rb +1 -0
  7. data/lib/active_couch/associations/has_many_association.rb +35 -0
  8. data/lib/active_couch/attribute.rb +46 -0
  9. data/lib/active_couch/base.rb +458 -0
  10. data/lib/active_couch/callbacks.rb +81 -0
  11. data/lib/active_couch/connection.rb +155 -0
  12. data/lib/active_couch/errors.rb +17 -0
  13. data/lib/active_couch/migrations.rb +3 -0
  14. data/lib/active_couch/migrations/errors.rb +4 -0
  15. data/lib/active_couch/migrations/migration.rb +77 -0
  16. data/lib/active_couch/migrations/migrator.rb +53 -0
  17. data/lib/active_couch/support.rb +2 -0
  18. data/lib/active_couch/support/extensions.rb +92 -0
  19. data/lib/active_couch/support/inflections.rb +52 -0
  20. data/lib/active_couch/support/inflector.rb +280 -0
  21. data/spec/attribute/initialize_spec.rb +138 -0
  22. data/spec/base/after_delete_spec.rb +107 -0
  23. data/spec/base/after_save_spec.rb +99 -0
  24. data/spec/base/before_delete_spec.rb +106 -0
  25. data/spec/base/before_save_spec.rb +98 -0
  26. data/spec/base/create_spec.rb +27 -0
  27. data/spec/base/database_spec.rb +65 -0
  28. data/spec/base/delete_spec.rb +78 -0
  29. data/spec/base/find_spec.rb +165 -0
  30. data/spec/base/from_json_spec.rb +48 -0
  31. data/spec/base/has_many_spec.rb +81 -0
  32. data/spec/base/has_spec.rb +76 -0
  33. data/spec/base/id_spec.rb +22 -0
  34. data/spec/base/initialize_spec.rb +43 -0
  35. data/spec/base/module_spec.rb +18 -0
  36. data/spec/base/nested_class_spec.rb +19 -0
  37. data/spec/base/rev_spec.rb +16 -0
  38. data/spec/base/save_spec.rb +65 -0
  39. data/spec/base/site_spec.rb +41 -0
  40. data/spec/base/to_json_spec.rb +64 -0
  41. data/spec/connection/initialize_spec.rb +28 -0
  42. data/spec/has_many_association/initialize_spec.rb +33 -0
  43. data/spec/migration/define_spec.rb +29 -0
  44. data/spec/migration/include_attributes_spec.rb +30 -0
  45. data/spec/migration/view_js_spec.rb +46 -0
  46. data/spec/migration/with_filter_spec.rb +13 -0
  47. data/spec/migration/with_key_spec.rb +13 -0
  48. data/spec/migrator/create_database_spec.rb +48 -0
  49. data/spec/migrator/delete_database_spec.rb +46 -0
  50. data/spec/migrator/migrate_spec.rb +99 -0
  51. data/spec/spec_helper.rb +9 -0
  52. metadata +104 -0
@@ -0,0 +1,52 @@
1
+ Inflector.inflections do |inflect|
2
+ inflect.plural(/$/, 's')
3
+ inflect.plural(/s$/i, 's')
4
+ inflect.plural(/(ax|test)is$/i, '\1es')
5
+ inflect.plural(/(octop|vir)us$/i, '\1i')
6
+ inflect.plural(/(alias|status)$/i, '\1es')
7
+ inflect.plural(/(bu)s$/i, '\1ses')
8
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
9
+ inflect.plural(/([ti])um$/i, '\1a')
10
+ inflect.plural(/sis$/i, 'ses')
11
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
12
+ inflect.plural(/(hive)$/i, '\1s')
13
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
14
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
15
+ inflect.plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
16
+ inflect.plural(/([m|l])ouse$/i, '\1ice')
17
+ inflect.plural(/^(ox)$/i, '\1en')
18
+ inflect.plural(/(quiz)$/i, '\1zes')
19
+
20
+ inflect.singular(/s$/i, '')
21
+ inflect.singular(/(n)ews$/i, '\1ews')
22
+ inflect.singular(/([ti])a$/i, '\1um')
23
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
24
+ inflect.singular(/(^analy)ses$/i, '\1sis')
25
+ inflect.singular(/([^f])ves$/i, '\1fe')
26
+ inflect.singular(/(hive)s$/i, '\1')
27
+ inflect.singular(/(tive)s$/i, '\1')
28
+ inflect.singular(/([lr])ves$/i, '\1f')
29
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
30
+ inflect.singular(/(s)eries$/i, '\1eries')
31
+ inflect.singular(/(m)ovies$/i, '\1ovie')
32
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
33
+ inflect.singular(/([m|l])ice$/i, '\1ouse')
34
+ inflect.singular(/(bus)es$/i, '\1')
35
+ inflect.singular(/(o)es$/i, '\1')
36
+ inflect.singular(/(shoe)s$/i, '\1')
37
+ inflect.singular(/(cris|ax|test)es$/i, '\1is')
38
+ inflect.singular(/(octop|vir)i$/i, '\1us')
39
+ inflect.singular(/(alias|status)es$/i, '\1')
40
+ inflect.singular(/^(ox)en/i, '\1')
41
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
42
+ inflect.singular(/(matr)ices$/i, '\1ix')
43
+ inflect.singular(/(quiz)zes$/i, '\1')
44
+
45
+ inflect.irregular('person', 'people')
46
+ inflect.irregular('man', 'men')
47
+ inflect.irregular('child', 'children')
48
+ inflect.irregular('sex', 'sexes')
49
+ inflect.irregular('move', 'moves')
50
+
51
+ inflect.uncountable(%w(equipment information rice money species series fish sheep))
52
+ end
@@ -0,0 +1,280 @@
1
+ # This file is copied from the ActiveSupport project, which
2
+ # is a part of the Ruby On Rails web-framework (http://rubyonrails.org).
3
+
4
+ # TODO Consider a String extension that delegates to Inflector.
5
+
6
+ require 'singleton'
7
+
8
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
9
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
10
+ # in inflections.rb.
11
+ module Inflector
12
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
13
+ # inflection rules. Examples:
14
+ #
15
+ # Inflector.inflections do |inflect|
16
+ # inflect.plural /^(ox)$/i, '\1\2en'
17
+ # inflect.singular /^(ox)en/i, '\1'
18
+ #
19
+ # inflect.irregular 'octopus', 'octopi'
20
+ #
21
+ # inflect.uncountable "equipment"
22
+ # end
23
+ #
24
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
25
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
26
+ # already have been loaded.
27
+ class Inflections
28
+ include Singleton
29
+
30
+ attr_reader :plurals, :singulars, :uncountables
31
+
32
+ def initialize
33
+ @plurals, @singulars, @uncountables = [], [], []
34
+ end
35
+
36
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
37
+ # The replacement should always be a string that may include references to the matched data from the rule.
38
+ def plural(rule, replacement)
39
+ @plurals.insert(0, [rule, replacement])
40
+ end
41
+
42
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
43
+ # The replacement should always be a string that may include references to the matched data from the rule.
44
+ def singular(rule, replacement)
45
+ @singulars.insert(0, [rule, replacement])
46
+ end
47
+
48
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
49
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
50
+ #
51
+ # Examples:
52
+ # irregular 'octopus', 'octopi'
53
+ # irregular 'person', 'people'
54
+ def irregular(singular, plural)
55
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
56
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
57
+ end
58
+
59
+ # Add uncountable words that shouldn't be attempted inflected.
60
+ #
61
+ # Examples:
62
+ # uncountable "money"
63
+ # uncountable "money", "information"
64
+ # uncountable %w( money information rice )
65
+ def uncountable(*words)
66
+ (@uncountables << words).flatten!
67
+ end
68
+
69
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
70
+ # the options are: :plurals, :singulars, :uncountables
71
+ #
72
+ # Examples:
73
+ # clear :all
74
+ # clear :plurals
75
+ def clear(scope = :all)
76
+ case scope
77
+ when :all
78
+ @plurals, @singulars, @uncountables = [], [], []
79
+ else
80
+ instance_variable_set "@#{scope}", []
81
+ end
82
+ end
83
+ end
84
+
85
+ extend self
86
+
87
+ def inflections
88
+ if block_given?
89
+ yield Inflections.instance
90
+ else
91
+ Inflections.instance
92
+ end
93
+ end
94
+
95
+ # Returns the plural form of the word in the string.
96
+ #
97
+ # Examples
98
+ # "post".pluralize #=> "posts"
99
+ # "octopus".pluralize #=> "octopi"
100
+ # "sheep".pluralize #=> "sheep"
101
+ # "words".pluralize #=> "words"
102
+ # "the blue mailman".pluralize #=> "the blue mailmen"
103
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
104
+ def pluralize(word)
105
+ result = word.to_s.dup
106
+
107
+ if inflections.uncountables.include?(result.downcase)
108
+ result
109
+ else
110
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
111
+ result
112
+ end
113
+ end
114
+
115
+ # The reverse of pluralize, returns the singular form of a word in a string.
116
+ #
117
+ # Examples
118
+ # "posts".singularize #=> "post"
119
+ # "octopi".singularize #=> "octopus"
120
+ # "sheep".singluarize #=> "sheep"
121
+ # "word".singluarize #=> "word"
122
+ # "the blue mailmen".singularize #=> "the blue mailman"
123
+ # "CamelOctopi".singularize #=> "CamelOctopus"
124
+ def singularize(word)
125
+ result = word.to_s.dup
126
+
127
+ if inflections.uncountables.include?(result.downcase)
128
+ result
129
+ else
130
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
131
+ result
132
+ end
133
+ end
134
+
135
+ # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
136
+ # is set to ":lower" then camelize produces lowerCamelCase.
137
+ #
138
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
139
+ #
140
+ # Examples
141
+ # "active_record".camelize #=> "ActiveRecord"
142
+ # "active_record".camelize(:lower) #=> "activeRecord"
143
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
144
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
145
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
146
+ if first_letter_in_uppercase
147
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
148
+ else
149
+ lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
150
+ end
151
+ end
152
+
153
+ # Capitalizes all the words and replaces some characters in the string to create
154
+ # a nicer looking title. Titleize is meant for creating pretty output. It is not
155
+ # used in the Rails internals.
156
+ #
157
+ # titleize is also aliased as as titlecase
158
+ #
159
+ # Examples
160
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
161
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
162
+ def titleize(word)
163
+ humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
164
+ end
165
+
166
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
167
+ #
168
+ # Changes '::' to '/' to convert namespaces to paths.
169
+ #
170
+ # Examples
171
+ # "ActiveRecord".underscore #=> "active_record"
172
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
173
+ def underscore(camel_cased_word)
174
+ camel_cased_word.to_s.gsub(/::/, '/').
175
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
176
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
177
+ tr("-", "_").
178
+ downcase
179
+ end
180
+
181
+ # Replaces underscores with dashes in the string.
182
+ #
183
+ # Example
184
+ # "puni_puni" #=> "puni-puni"
185
+ def dasherize(underscored_word)
186
+ underscored_word.gsub(/_/, '-')
187
+ end
188
+
189
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
190
+ # Like titleize, this is meant for creating pretty output.
191
+ #
192
+ # Examples
193
+ # "employee_salary" #=> "Employee salary"
194
+ # "author_id" #=> "Author"
195
+ def humanize(lower_case_and_underscored_word)
196
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
197
+ end
198
+
199
+ # Removes the module part from the expression in the string
200
+ #
201
+ # Examples
202
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
203
+ # "Inflections".demodulize #=> "Inflections"
204
+ def demodulize(class_name_in_module)
205
+ class_name_in_module.to_s.gsub(/^.*::/, '')
206
+ end
207
+
208
+ # Create the name of a table like Rails does for models to table names. This method
209
+ # uses the pluralize method on the last word in the string.
210
+ #
211
+ # Examples
212
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
213
+ # "egg_and_ham".tableize #=> "egg_and_hams"
214
+ # "fancyCategory".tableize #=> "fancy_categories"
215
+ def tableize(class_name)
216
+ pluralize(underscore(class_name))
217
+ end
218
+
219
+ # Create a class name from a table name like Rails does for table names to models.
220
+ # Note that this returns a string and not a Class. (To convert to an actual class
221
+ # follow classify with constantize.)
222
+ #
223
+ # Examples
224
+ # "egg_and_hams".classify #=> "EggAndHam"
225
+ # "post".classify #=> "Post"
226
+ def classify(table_name)
227
+ # strip out any leading schema name
228
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
229
+ end
230
+
231
+ # Creates a foreign key name from a class name.
232
+ # +separate_class_name_and_id_with_underscore+ sets whether
233
+ # the method should put '_' between the name and 'id'.
234
+ #
235
+ # Examples
236
+ # "Message".foreign_key #=> "message_id"
237
+ # "Message".foreign_key(false) #=> "messageid"
238
+ # "Admin::Post".foreign_key #=> "post_id"
239
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
240
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
241
+ end
242
+
243
+ # Constantize tries to find a declared constant with the name specified
244
+ # in the string. It raises a NameError when the name is not in CamelCase
245
+ # or is not initialized.
246
+ #
247
+ # Examples
248
+ # "Module".constantize #=> Module
249
+ # "Class".constantize #=> Class
250
+ def constantize(camel_cased_word)
251
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
252
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
253
+ end
254
+
255
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
256
+ end
257
+
258
+ # Ordinalize turns a number into an ordinal string used to denote the
259
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
260
+ #
261
+ # Examples
262
+ # ordinalize(1) # => "1st"
263
+ # ordinalize(2) # => "2nd"
264
+ # ordinalize(1002) # => "1002nd"
265
+ # ordinalize(1003) # => "1003rd"
266
+ def ordinalize(number)
267
+ if (11..13).include?(number.to_i % 100)
268
+ "#{number}th"
269
+ else
270
+ case number.to_i % 10
271
+ when 1: "#{number}st"
272
+ when 2: "#{number}nd"
273
+ when 3: "#{number}rd"
274
+ else "#{number}th"
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ require File.dirname(__FILE__) + '/inflections'
@@ -0,0 +1,138 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "An ActiveCouch::Attribute object initialized with no options" do
4
+ before(:each) do
5
+ @att = ActiveCouch::Attribute.new(:name)
6
+ end
7
+
8
+ it "should set the type to String and value to an empty String" do
9
+ @att.klass.should == String
10
+ @att.value.should == ""
11
+ @att.name.should == "name"
12
+ end
13
+ end
14
+
15
+ describe "An ActiveCouch::Attribute object initialized with a type :text but no default value" do
16
+ before(:each) do
17
+ @att = ActiveCouch::Attribute.new(:name, :which_is => :text)
18
+ end
19
+
20
+ it "should set the type to String and value to an empty String" do
21
+ @att.klass.should == String
22
+ @att.value.should == ""
23
+ @att.name.should == "name"
24
+ end
25
+ end
26
+
27
+ describe "An ActiveCouch::Attribute object initialized with a type :decimal but no default value" do
28
+ before(:each) do
29
+ @att = ActiveCouch::Attribute.new(:amount, :which_is => :decimal)
30
+ end
31
+
32
+ it "should set the type to Float and value to 0.0" do
33
+ @att.klass.should == Float
34
+ @att.value.should == 0.0
35
+ @att.name.should == "amount"
36
+ end
37
+ end
38
+
39
+ describe "An ActiveCouch::Attribute object initialized with a type :number but no default value" do
40
+ before(:each) do
41
+ @att = ActiveCouch::Attribute.new(:age, :which_is => :number)
42
+ end
43
+
44
+ it "should set the type to Integer and value to 0" do
45
+ @att.klass.should == Integer
46
+ @att.value.should == 0
47
+ @att.name.should == "age"
48
+ end
49
+ end
50
+
51
+ describe "An ActiveCouch::Attribute object initialized with type :text and with a default value" do
52
+ before(:each) do
53
+ @att = ActiveCouch::Attribute.new(:name, :which_is => :text, :with_default_value => "Hotel")
54
+ end
55
+
56
+ it "should set the type to String and value to Hotel" do
57
+ @att.klass.should == String
58
+ @att.value.should == "Hotel"
59
+ @att.name.should == "name"
60
+ end
61
+ end
62
+
63
+ describe "An ActiveCouch::Attribute object initialized with type :decimal and with a default value" do
64
+ before(:each) do
65
+ @att = ActiveCouch::Attribute.new(:star_rating, :which_is => :decimal, :with_default_value => 4.5)
66
+ end
67
+
68
+ it "should set the type to Float and value to 4.5" do
69
+ @att.klass.should == Float
70
+ @att.value.should == 4.5
71
+ @att.name.should == "star_rating"
72
+ end
73
+ end
74
+
75
+ describe "An ActiveCouch::Attribute object initialized with type :number and with a default value" do
76
+ before(:each) do
77
+ @att = ActiveCouch::Attribute.new(:marks, :which_is => :number, :with_default_value => 100)
78
+ end
79
+
80
+ it "should set the klass to Integer and value to 100" do
81
+ @att.klass.should == Integer
82
+ @att.value.should == 100
83
+ @att.name.should == "marks"
84
+ end
85
+ end
86
+
87
+ describe "An ActiveCouch::Attribute object initialized with a certain type and with a value which is not of that type" do
88
+
89
+ it "should raise an InvalidCouchTypeError with type String and value which is not string" do
90
+ lambda { ActiveCouch::Attribute.new(:x, :which_is => :text, :with_default_value => 100) }.should raise_error(ActiveCouch::InvalidCouchTypeError)
91
+ end
92
+
93
+ it "should raise an InvalidCouchTypeError with type Number and value which is not Number" do
94
+ lambda { ActiveCouch::Attribute.new(:x, :which_is => :number, :with_default_value => "abc") }.should raise_error(ActiveCouch::InvalidCouchTypeError)
95
+ end
96
+
97
+ it "should raise an InvalidCouchTypeError with type Number and value which is not Number" do
98
+ lambda { ActiveCouch::Attribute.new(:x, :which_is => :number, :with_default_value => 3.0) }.should raise_error(ActiveCouch::InvalidCouchTypeError)
99
+ end
100
+
101
+ it "should raise an InvalidCouchTypeError with type Decimal and value which is not Decimal" do
102
+ lambda { ActiveCouch::Attribute.new(:x, :which_is => :number, :with_default_value => []) }.should raise_error(ActiveCouch::InvalidCouchTypeError)
103
+ end
104
+
105
+ it "should raise an InvalidCouchTypeError for an unsupported type" do
106
+ lambda { ActiveCouch::Attribute.new(:x, :which_is => :array) }.should raise_error(ActiveCouch::InvalidCouchTypeError)
107
+ end
108
+
109
+ end
110
+
111
+ describe "An ActiveCouch::Attribute object initialized with a certain type must be able to assign values" do
112
+ before(:each) do
113
+ @a = ActiveCouch::Attribute.new(:a, :which_is => :text)
114
+ @b = ActiveCouch::Attribute.new(:b, :which_is => :number)
115
+ @c = ActiveCouch::Attribute.new(:c, :which_is => :decimal)
116
+ end
117
+
118
+ it "should set the value correctly if it is of the right type" do
119
+ @a.value = "McLovin"
120
+ @a.value.should == "McLovin"
121
+ @a.name.should == "a"
122
+
123
+ @b.value = 100
124
+ @b.value.should == 100
125
+ @b.name.should == "b"
126
+
127
+ @c.value = 5.4
128
+ @c.value.should == 5.4
129
+ @c.name.should == "c"
130
+
131
+ end
132
+
133
+ it "should raise an exception if the type is not String" do
134
+ lambda { @a.value = 100 }.should raise_error(ActiveCouch::InvalidCouchTypeError)
135
+ lambda { @b.value = "a" }.should raise_error(ActiveCouch::InvalidCouchTypeError)
136
+ lambda { @c.value = [] }.should raise_error(ActiveCouch::InvalidCouchTypeError)
137
+ end
138
+ end