activecouch 0.1.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 (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