arunthampi-supermodel 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.
@@ -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,277 @@
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
+ require 'singleton'
4
+
5
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
6
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
7
+ # in inflections.rb.
8
+ module Inflector
9
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
10
+ # inflection rules. Examples:
11
+ #
12
+ # Inflector.inflections do |inflect|
13
+ # inflect.plural /^(ox)$/i, '\1\2en'
14
+ # inflect.singular /^(ox)en/i, '\1'
15
+ #
16
+ # inflect.irregular 'octopus', 'octopi'
17
+ #
18
+ # inflect.uncountable "equipment"
19
+ # end
20
+ #
21
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
22
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
23
+ # already have been loaded.
24
+ class Inflections
25
+ include Singleton
26
+
27
+ attr_reader :plurals, :singulars, :uncountables
28
+
29
+ def initialize
30
+ @plurals, @singulars, @uncountables = [], [], []
31
+ end
32
+
33
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
34
+ # The replacement should always be a string that may include references to the matched data from the rule.
35
+ def plural(rule, replacement)
36
+ @plurals.insert(0, [rule, replacement])
37
+ end
38
+
39
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
40
+ # The replacement should always be a string that may include references to the matched data from the rule.
41
+ def singular(rule, replacement)
42
+ @singulars.insert(0, [rule, replacement])
43
+ end
44
+
45
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
46
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
47
+ #
48
+ # Examples:
49
+ # irregular 'octopus', 'octopi'
50
+ # irregular 'person', 'people'
51
+ def irregular(singular, plural)
52
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
53
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
54
+ end
55
+
56
+ # Add uncountable words that shouldn't be attempted inflected.
57
+ #
58
+ # Examples:
59
+ # uncountable "money"
60
+ # uncountable "money", "information"
61
+ # uncountable %w( money information rice )
62
+ def uncountable(*words)
63
+ (@uncountables << words).flatten!
64
+ end
65
+
66
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
67
+ # the options are: :plurals, :singulars, :uncountables
68
+ #
69
+ # Examples:
70
+ # clear :all
71
+ # clear :plurals
72
+ def clear(scope = :all)
73
+ case scope
74
+ when :all
75
+ @plurals, @singulars, @uncountables = [], [], []
76
+ else
77
+ instance_variable_set "@#{scope}", []
78
+ end
79
+ end
80
+ end
81
+
82
+ extend self
83
+
84
+ def inflections
85
+ if block_given?
86
+ yield Inflections.instance
87
+ else
88
+ Inflections.instance
89
+ end
90
+ end
91
+
92
+ # Returns the plural form of the word in the string.
93
+ #
94
+ # Examples
95
+ # "post".pluralize #=> "posts"
96
+ # "octopus".pluralize #=> "octopi"
97
+ # "sheep".pluralize #=> "sheep"
98
+ # "words".pluralize #=> "words"
99
+ # "the blue mailman".pluralize #=> "the blue mailmen"
100
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
101
+ def pluralize(word)
102
+ result = word.to_s.dup
103
+
104
+ if inflections.uncountables.include?(result.downcase)
105
+ result
106
+ else
107
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
108
+ result
109
+ end
110
+ end
111
+
112
+ # The reverse of pluralize, returns the singular form of a word in a string.
113
+ #
114
+ # Examples
115
+ # "posts".singularize #=> "post"
116
+ # "octopi".singularize #=> "octopus"
117
+ # "sheep".singluarize #=> "sheep"
118
+ # "word".singluarize #=> "word"
119
+ # "the blue mailmen".singularize #=> "the blue mailman"
120
+ # "CamelOctopi".singularize #=> "CamelOctopus"
121
+ def singularize(word)
122
+ result = word.to_s.dup
123
+
124
+ if inflections.uncountables.include?(result.downcase)
125
+ result
126
+ else
127
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
128
+ result
129
+ end
130
+ end
131
+
132
+ # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
133
+ # is set to ":lower" then camelize produces lowerCamelCase.
134
+ #
135
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
136
+ #
137
+ # Examples
138
+ # "active_record".camelize #=> "ActiveRecord"
139
+ # "active_record".camelize(:lower) #=> "activeRecord"
140
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
141
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
142
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
143
+ if first_letter_in_uppercase
144
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
145
+ else
146
+ lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
147
+ end
148
+ end
149
+
150
+ # Capitalizes all the words and replaces some characters in the string to create
151
+ # a nicer looking title. Titleize is meant for creating pretty output. It is not
152
+ # used in the Rails internals.
153
+ #
154
+ # titleize is also aliased as as titlecase
155
+ #
156
+ # Examples
157
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
158
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
159
+ def titleize(word)
160
+ humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
161
+ end
162
+
163
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
164
+ #
165
+ # Changes '::' to '/' to convert namespaces to paths.
166
+ #
167
+ # Examples
168
+ # "ActiveRecord".underscore #=> "active_record"
169
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
170
+ def underscore(camel_cased_word)
171
+ camel_cased_word.to_s.gsub(/::/, '/').
172
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
173
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
174
+ tr("-", "_").
175
+ downcase
176
+ end
177
+
178
+ # Replaces underscores with dashes in the string.
179
+ #
180
+ # Example
181
+ # "puni_puni" #=> "puni-puni"
182
+ def dasherize(underscored_word)
183
+ underscored_word.gsub(/_/, '-')
184
+ end
185
+
186
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
187
+ # Like titleize, this is meant for creating pretty output.
188
+ #
189
+ # Examples
190
+ # "employee_salary" #=> "Employee salary"
191
+ # "author_id" #=> "Author"
192
+ def humanize(lower_case_and_underscored_word)
193
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
194
+ end
195
+
196
+ # Removes the module part from the expression in the string
197
+ #
198
+ # Examples
199
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
200
+ # "Inflections".demodulize #=> "Inflections"
201
+ def demodulize(class_name_in_module)
202
+ class_name_in_module.to_s.gsub(/^.*::/, '')
203
+ end
204
+
205
+ # Create the name of a table like Rails does for models to table names. This method
206
+ # uses the pluralize method on the last word in the string.
207
+ #
208
+ # Examples
209
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
210
+ # "egg_and_ham".tableize #=> "egg_and_hams"
211
+ # "fancyCategory".tableize #=> "fancy_categories"
212
+ def tableize(class_name)
213
+ pluralize(underscore(class_name))
214
+ end
215
+
216
+ # Create a class name from a table name like Rails does for table names to models.
217
+ # Note that this returns a string and not a Class. (To convert to an actual class
218
+ # follow classify with constantize.)
219
+ #
220
+ # Examples
221
+ # "egg_and_hams".classify #=> "EggAndHam"
222
+ # "post".classify #=> "Post"
223
+ def classify(table_name)
224
+ # strip out any leading schema name
225
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
226
+ end
227
+
228
+ # Creates a foreign key name from a class name.
229
+ # +separate_class_name_and_id_with_underscore+ sets whether
230
+ # the method should put '_' between the name and 'id'.
231
+ #
232
+ # Examples
233
+ # "Message".foreign_key #=> "message_id"
234
+ # "Message".foreign_key(false) #=> "messageid"
235
+ # "Admin::Post".foreign_key #=> "post_id"
236
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
237
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
238
+ end
239
+
240
+ # Constantize tries to find a declared constant with the name specified
241
+ # in the string. It raises a NameError when the name is not in CamelCase
242
+ # or is not initialized.
243
+ #
244
+ # Examples
245
+ # "Module".constantize #=> Module
246
+ # "Class".constantize #=> Class
247
+ def constantize(camel_cased_word)
248
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
249
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
250
+ end
251
+
252
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
253
+ end
254
+
255
+ # Ordinalize turns a number into an ordinal string used to denote the
256
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
257
+ #
258
+ # Examples
259
+ # ordinalize(1) # => "1st"
260
+ # ordinalize(2) # => "2nd"
261
+ # ordinalize(1002) # => "1002nd"
262
+ # ordinalize(1003) # => "1003rd"
263
+ def ordinalize(number)
264
+ if (11..13).include?(number.to_i % 100)
265
+ "#{number}th"
266
+ else
267
+ case number.to_i % 10
268
+ when 1: "#{number}st"
269
+ when 2: "#{number}nd"
270
+ when 3: "#{number}rd"
271
+ else "#{number}th"
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ require File.dirname(__FILE__) + '/inflections'
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "SuperModel::Base #from_json method, with many attributes" do
4
+ before(:all) do
5
+ class Hotel < SuperModel::Base
6
+ has :name, :which_is => :text, :with_default_value => "Swissotel The Stamford"
7
+ has :star_rating, :which_is => :decimal, :with_default_value => 5.0
8
+ has :rooms, :which_is => :number, :with_default_value => 100
9
+ end
10
+
11
+ class Hospital < SuperModel::Base
12
+ has :name
13
+ end
14
+
15
+ class CrazyPerson < SuperModel::Base
16
+ has :name, :which_is => :text, :with_default_value => "Crazed McLovin"
17
+ has_many :hospitals
18
+ end
19
+
20
+ class Town < SuperModel::Base
21
+ has :name
22
+
23
+ has_one :hospital
24
+ end
25
+ end
26
+
27
+ after(:all) do
28
+ Object.send(:remove_const, :Hotel)
29
+ Object.send(:remove_const, :Hospital)
30
+ Object.send(:remove_const, :CrazyPerson)
31
+ end
32
+
33
+ it "should have the from_json method" do
34
+ Hotel.should respond_to(:from_json)
35
+ Hospital.should respond_to(:from_json)
36
+ CrazyPerson.should respond_to(:from_json)
37
+ Hospital.should respond_to(:from_json)
38
+ end
39
+
40
+ it "should instantiate an object when sent the from_json method with valid json as a parameter" do
41
+ h = Hotel.from_json("{\"name\":\"Swissotel The Stamford\",\"rooms\":200,\"star_rating\":4.0}")
42
+ h.class.should == Hotel
43
+ # Check whether all attributes are set correctly
44
+ h.name.should == "Swissotel The Stamford"
45
+ h.rooms.should == 200
46
+ h.star_rating.should == 4.0
47
+ end
48
+
49
+ it "should instantiate an object when sent the from_json method with valid JSON (containing a has_many association) as a parameter" do
50
+ crazy = CrazyPerson.from_json('{"name":"Crazed McLovin","hospitals":[{"name":"Crazy Hospital 1"},{"name":"Crazy Hospital 2"}]}')
51
+
52
+ crazy.name == "Crazed McLovin"
53
+ crazy.hospitals.size.should == 2
54
+
55
+ hospitals = crazy.hospitals.collect{|h| h.name }
56
+ hospitals.sort!
57
+
58
+ hospitals.first.should == 'Crazy Hospital 1'
59
+ hospitals.last.should == 'Crazy Hospital 2'
60
+ end
61
+
62
+ it "should instantiate an object when sent the from_json method with valid JSON (containing a has_one association) as a parameter" do
63
+ town = Town.from_json('{"name":"Cochin","hospital":{"name":"Crazy Hospital 1"}}')
64
+
65
+ town.name == "Cochin"
66
+ town.hospital.should_not == nil
67
+
68
+ town.hospital.name.should == 'Crazy Hospital 1'
69
+ end
70
+ end
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe "A class which is a subclass of SuperModel::Base with a has_many association" do
4
+ before(:each) do
5
+ class Person < SuperModel::Base
6
+ has :name, :which_is => :text
7
+ end
8
+
9
+ class AgedPerson < SuperModel::Base
10
+ has :name
11
+ has :age, :which_is => :number, :with_default_value => 10
12
+ end
13
+
14
+ class Contact < SuperModel::Base
15
+ has_many :people
16
+ end
17
+
18
+ @c = Contact.new
19
+ @p1 = Person.new
20
+ @a1 = AgedPerson.new
21
+ end
22
+
23
+ after(:each) do
24
+ Object.send(:remove_const, :Person)
25
+ Object.send(:remove_const, :AgedPerson)
26
+ Object.send(:remove_const, :Contact)
27
+ end
28
+
29
+ it "should have an instance variable called has_many_associations which is a Hash with the key being :people" do
30
+ Contact.has_many_associations.class.should == Hash
31
+ Contact.has_many_associations.keys.should == [:people]
32
+ end
33
+
34
+ it "should have methods called people and add_person" do
35
+ @c.should respond_to(:people)
36
+ @c.should respond_to(:add_person)
37
+ end
38
+
39
+ it "should have a method called people which returns an empty array" do
40
+ @c.people.should == []
41
+ end
42
+
43
+ it "should be able to add a Person object to the association" do
44
+ @c.add_person(@p1)
45
+ @c.people.should == [@p1]
46
+ end
47
+
48
+ end
49
+
50
+ describe "An object instantiated from class which is a subclass of SuperModel::Base" do
51
+ before(:each) do
52
+ class Comment < SuperModel::Base
53
+ has :body
54
+ end
55
+
56
+ class Blog < SuperModel::Base
57
+ has :title
58
+ has_many :comments
59
+ end
60
+
61
+ @comment1 = Comment.new(:body => "I can haz redbull?")
62
+ @comment2 = Comment.new(:body => 'k thx bai')
63
+ @blog = Blog.new(:title => 'Lolcats Primer', :comments => [@comment1, @comment2])
64
+ @blog1 = Blog.new(:title => 'Lolcats Primer The Sequel', :comments => [{:body => 'can'}, {:body => 'haz'}])
65
+ end
66
+
67
+ after(:each) do
68
+ Object.send(:remove_const, :Comment)
69
+ Object.send(:remove_const, :Blog)
70
+ end
71
+
72
+ it "should be able to initialize with a hash which contains descendents of SuperModel::Base" do
73
+ @comment1.body.should == "I can haz redbull?"
74
+ @comment2.body.should == "k thx bai"
75
+
76
+ @blog.title.should == 'Lolcats Primer'
77
+ @blog.comments.should == [@comment1, @comment2]
78
+ end
79
+
80
+ it "should be able to initialize from a hash which contains only Strings" do
81
+ @blog1.title.should == 'Lolcats Primer The Sequel'
82
+
83
+ comment_bodies = @blog1.comments.collect{|c| c.body }
84
+ comment_bodies.sort!
85
+
86
+ comment_bodies.first.should == 'can'
87
+ comment_bodies.last.should == 'haz'
88
+ end
89
+ end