swiss_db 0.6.8 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2313d48751d8db38ddeb8d6b35b77505b81f0e19
4
- data.tar.gz: 8b913b2cd08f1cbd529409c30babbc2d2e5f298d
3
+ metadata.gz: 04bd172ce896a99dd76a23f2670409210a16cbd3
4
+ data.tar.gz: 234b633535968c2761a3cba2bc056cec691716d9
5
5
  SHA512:
6
- metadata.gz: 531bd255a92c5b20754354c5ddc8982bbfd717af76f0a905b6eefd79e4b78064793ca5ff25746ea422dd73371aa1d7c4f71edcbdc3e2e48a888d0318da5f4833
7
- data.tar.gz: dcde38e09562c53dea3404e1f5db68d967232910f6cf8e67975fedc4b3116242a2d224fe83c22ee1b585fc4be81be4aa34ffe249ffaa73cf3cf91a7a3f6deab4
6
+ metadata.gz: 8a780767d3fed17e7b36f145a678584e48b7d835c799a9af4732d5cf8ec5ce2d7f17cae428b824b713816209f8746980414791a75e64f736f63125b4db85911a
7
+ data.tar.gz: cfa8a60bd0dfd045bc8f46823e2373afee309b59734fbe4f26a93ea7c6bf22c30b0930304ff6ca7b7d934d0e90a9f1b7340886acc452404b5f2e37d0d1106836
data/README.md CHANGED
@@ -105,6 +105,9 @@ KNOWN LIMITATION: This ORM compiles in the database name and the database versio
105
105
  DataStore.drop_db #=> true if the DB was dropped, false if not
106
106
  ```
107
107
 
108
+ ## Contributors
109
+
110
+
108
111
  ## Development
109
112
 
110
113
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -0,0 +1,69 @@
1
+ #--
2
+ # Defines the standard inflection rules. These are the starting point for
3
+ # new projects and are not considered complete. The current set of inflection
4
+ # rules is frozen. This means, we do not change them to become more complete.
5
+ # This is a safety measure to keep existing applications from breaking.
6
+ #++
7
+ module MotionSupport
8
+ Inflector.inflections do |inflect|
9
+ inflect.plural(/$/, 's')
10
+ inflect.plural(/s$/i, 's')
11
+ inflect.plural(/^(ax|test)is$/i, '\1es')
12
+ inflect.plural(/(octop|vir)us$/i, '\1i')
13
+ inflect.plural(/(octop|vir)i$/i, '\1i')
14
+ inflect.plural(/(alias|status)$/i, '\1es')
15
+ inflect.plural(/(bu)s$/i, '\1ses')
16
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
17
+ inflect.plural(/([ti])um$/i, '\1a')
18
+ inflect.plural(/([ti])a$/i, '\1a')
19
+ inflect.plural(/sis$/i, 'ses')
20
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
21
+ inflect.plural(/(hive)$/i, '\1s')
22
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
23
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
24
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
25
+ inflect.plural(/^(m|l)ouse$/i, '\1ice')
26
+ inflect.plural(/^(m|l)ice$/i, '\1ice')
27
+ inflect.plural(/^(ox)$/i, '\1en')
28
+ inflect.plural(/^(oxen)$/i, '\1')
29
+ inflect.plural(/(quiz)$/i, '\1zes')
30
+
31
+ inflect.singular(/s$/i, '')
32
+ inflect.singular(/(ss)$/i, '\1')
33
+ inflect.singular(/(n)ews$/i, '\1ews')
34
+ inflect.singular(/([ti])a$/i, '\1um')
35
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
36
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
37
+ inflect.singular(/([^f])ves$/i, '\1fe')
38
+ inflect.singular(/(hive)s$/i, '\1')
39
+ inflect.singular(/(tive)s$/i, '\1')
40
+ inflect.singular(/([lr])ves$/i, '\1f')
41
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
42
+ inflect.singular(/(s)eries$/i, '\1eries')
43
+ inflect.singular(/(m)ovies$/i, '\1ovie')
44
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
45
+ inflect.singular(/^(m|l)ice$/i, '\1ouse')
46
+ inflect.singular(/(bus)(es)?$/i, '\1')
47
+ inflect.singular(/(o)es$/i, '\1')
48
+ inflect.singular(/(shoe)s$/i, '\1')
49
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
50
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
51
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
52
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
53
+ inflect.singular(/^(ox)en/i, '\1')
54
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
55
+ inflect.singular(/(matr)ices$/i, '\1ix')
56
+ inflect.singular(/(quiz)zes$/i, '\1')
57
+ inflect.singular(/(database)s$/i, '\1')
58
+
59
+ inflect.irregular('person', 'people')
60
+ inflect.irregular('man', 'men')
61
+ inflect.irregular('child', 'children')
62
+ inflect.irregular('sex', 'sexes')
63
+ inflect.irregular('move', 'moves')
64
+ inflect.irregular('zombie', 'zombies')
65
+
66
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
67
+
68
+ end
69
+ end
@@ -158,18 +158,31 @@ module MotionSupport
158
158
  singular(/(#{s0})#{srest}$/i, '\1' + srest)
159
159
  singular(/(#{p0})#{prest}$/i, '\1' + srest)
160
160
  else
161
- plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
162
- plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
163
- plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
164
- plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
165
-
166
- singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
167
- singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
168
- singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
169
- singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
161
+ # define_plurals and define_singulars arose from a strange error where
162
+ # the app would quit with absolutely no output (not even VM Aborting!).
163
+ # The only way to turn off the error was to comment out some lines, but
164
+ # it didn't really matter which ones, as long as you commented out 4 or 5.
165
+ # So I split them out like this on a whim and it seems to have solved the problem.
166
+ # Weird!!
167
+ define_plurals(s0, srest, p0, prest)
168
+ define_singulars(s0, srest, p0, prest)
170
169
  end
171
170
  end
172
171
 
172
+ def define_plurals(s0, srest, p0, prest)
173
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
174
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
175
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
176
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
177
+ end
178
+
179
+ def define_singulars(s0, srest, p0, prest)
180
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
181
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
182
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
183
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
184
+ end
185
+
173
186
  # Add uncountable words that shouldn't be attempted inflected.
174
187
  #
175
188
  # uncountable 'money'
@@ -175,4 +175,4 @@ class String
175
175
  def foreign_key(separate_class_name_and_id_with_underscore = true)
176
176
  MotionSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
177
177
  end
178
- end
178
+ end
@@ -20,11 +20,11 @@ end
20
20
 
21
21
  class Cursor # < Array
22
22
 
23
- FIELD_TYPE_BLOB = 4
24
- FIELD_TYPE_FLOAT = 2
23
+ FIELD_TYPE_BLOB = 4
24
+ FIELD_TYPE_FLOAT = 2
25
25
  FIELD_TYPE_INTEGER = 1
26
- FIELD_TYPE_NULL = 0
27
- FIELD_TYPE_STRING = 3
26
+ FIELD_TYPE_NULL = 0
27
+ FIELD_TYPE_STRING = 3
28
28
 
29
29
  attr_accessor :cursor, :model
30
30
 
@@ -52,7 +52,7 @@ class Cursor # < Array
52
52
  # puts i
53
53
  hash_obj = {}
54
54
  cursor.moveToPosition(i)
55
- $current_schema[model.class_name].each do |k, v|
55
+ $current_schema[model.table_name].each do |k, v|
56
56
  hash_obj[k.to_sym] = self.send(k.to_sym)
57
57
  end
58
58
  arr << CursorModel.new(hash_obj)
@@ -60,33 +60,55 @@ class Cursor # < Array
60
60
  arr
61
61
  end
62
62
 
63
- def count
64
- cursor.getCount
63
+ def method_missing(methId, *args)
64
+ method_name = methId.id2name
65
+
66
+ if valid_setter_getter?(method_name)
67
+ handle_get_or_set(method_name, args)
68
+ else
69
+ super
70
+ end
65
71
  end
66
72
 
67
- def method_missing(methId, *args)
68
- str = methId.id2name
69
- if args.count == 0
70
- index = cursor.getColumnIndex(str)
71
- type = cursor.getType(index)
72
- # puts "getting field #{str} at index #{index} of type #{type}"
73
- if type == FIELD_TYPE_STRING
74
- cursor.getString(index)
75
- elsif type == FIELD_TYPE_INTEGER
76
- cursor.getInt(index)
77
- elsif type == FIELD_TYPE_NULL
78
- nil #??
79
- elsif type == FIELD_TYPE_FLOAT
80
- cursor.getFloat(index)
81
- elsif type == FIELD_TYPE_BLOB
82
- cursor.getBlob(index)
83
- end
84
- elsif args.count == 1
85
- # assignment... add to values to save
86
- @values[str.gsub!("=", "")] = args[0]
73
+ def valid_setter_getter?(method_name)
74
+ method_name.chop! if is_setter? method_name
75
+ column_names.include? method_name
76
+ end
77
+
78
+ def handle_get_or_set(method_name, args)
79
+ if is_setter? method_name
80
+ set_method(args)
81
+ else
82
+ get_method(method_name)
83
+ end
84
+ end
85
+
86
+ def is_setter?(method_name)
87
+ method_name[-1] == '='
88
+ end
89
+
90
+ def get_method(method_name)
91
+ index = cursor.getColumnIndex(method_name)
92
+ type = cursor.getType(index)
93
+ # puts "getting field #{method_name} at index #{index} of type #{type}"
94
+
95
+ if type == FIELD_TYPE_STRING
96
+ cursor.getString(index)
97
+ elsif type == FIELD_TYPE_INTEGER
98
+ cursor.getInt(index)
99
+ elsif type == FIELD_TYPE_NULL
100
+ nil #??
101
+ elsif type == FIELD_TYPE_FLOAT
102
+ cursor.getFloat(index)
103
+ elsif type == FIELD_TYPE_BLOB
104
+ cursor.getBlob(index)
87
105
  end
88
106
  end
89
107
 
108
+ def set_method(method_name, args)
109
+ @values[method_name.chop] = args[0]
110
+ end
111
+
90
112
  def save
91
113
  primary_key = model.primary_key
92
114
  pk_value = self.send(primary_key.to_sym)
@@ -100,5 +122,11 @@ class Cursor # < Array
100
122
  model.store.update(model.table_name, {key => value}, {primary_key => pk_value})
101
123
  end
102
124
 
125
+ def count
126
+ cursor.getCount
127
+ end
103
128
 
104
- end
129
+ def column_names
130
+ cursor.getColumnNames
131
+ end
132
+ end
data/lib/swiss_db/db.rb CHANGED
@@ -30,7 +30,8 @@ class Object
30
30
  puts @current_schema.inspect
31
31
  end
32
32
 
33
- def entity(table_name, &block)
33
+ def entity(class_name, &block)
34
+ table_name = class_name.tableize
34
35
  puts "adding entity #{table_name} to schema"
35
36
  @table_name = table_name
36
37
  @current_schema[@table_name] = {}
@@ -43,8 +44,10 @@ class Object
43
44
  @current_schema[@table_name][name] = type
44
45
  end
45
46
 
46
- def boolean(column_name)
47
- add_column column_name.to_s, "BOOLEAN"
47
+ %w(boolean float double integer datetime).each do |type|
48
+ define_method(type) do |column_name|
49
+ add_column column_name.to_s, type.upcase
50
+ end
48
51
  end
49
52
 
50
53
  def string(column_name)
@@ -55,8 +58,4 @@ class Object
55
58
  add_column column_name.to_s, "INTEGER"
56
59
  end
57
60
 
58
- def double(column_name)
59
- add_column column_name.to_s, "DOUBLE"
60
- end
61
-
62
61
  end
data/lib/swiss_db.rb CHANGED
@@ -14,7 +14,7 @@ if defined?(Motion) && defined?(Motion::Project::Config)
14
14
  insert_point = app.files.find_index { |file| file =~ /^(?:\.\/)?app\// } || 0
15
15
 
16
16
  # change to "swiss_db" for just swiss_db
17
- Dir.glob(File.join(lib_dir_path, "**/*.rb")).reverse.each do |file|
17
+ Dir.glob(File.join(lib_dir_path, "**/*.rb")).each do |file|
18
18
  app.files.insert(insert_point, file)
19
19
  end
20
20
 
@@ -25,4 +25,3 @@ if defined?(Motion) && defined?(Motion::Project::Config)
25
25
 
26
26
  end
27
27
  end
28
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swiss_db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.8
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Silverman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-12 00:00:00.000000000 Z
11
+ date: 2015-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -47,9 +47,9 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - README.md
49
49
  - lib/motion-support/array.rb
50
- - lib/motion-support/inflections.rb
51
- - lib/motion-support/inflector.rb
52
- - lib/motion-support/methods.rb
50
+ - lib/motion-support/default_inflections.rb
51
+ - lib/motion-support/inflector/inflections.rb
52
+ - lib/motion-support/inflector/methods.rb
53
53
  - lib/motion-support/string.rb
54
54
  - lib/swiss_db.rb
55
55
  - lib/swiss_db/cursor.rb
@@ -1,322 +0,0 @@
1
- module MotionSupport
2
- # The Inflector transforms words from singular to plural, class names to table
3
- # names, modularized class names to ones without, and class names to foreign
4
- # keys. The default inflections for pluralization, singularization, and
5
- # uncountable words are kept in inflections.rb.
6
- module Inflector
7
- class << self
8
-
9
- # Returns the plural form of the word in the string.
10
- #
11
- # 'post'.pluralize # => "posts"
12
- # 'octopus'.pluralize # => "octopi"
13
- # 'sheep'.pluralize # => "sheep"
14
- # 'words'.pluralize # => "words"
15
- # 'CamelOctopus'.pluralize # => "CamelOctopi"
16
- def pluralize(word)
17
- apply_inflections(word, inflections.plurals)
18
- end
19
-
20
- # The reverse of +pluralize+, returns the singular form of a word in a
21
- # string.
22
- #
23
- # 'posts'.singularize # => "post"
24
- # 'octopi'.singularize # => "octopus"
25
- # 'sheep'.singularize # => "sheep"
26
- # 'word'.singularize # => "word"
27
- # 'CamelOctopi'.singularize # => "CamelOctopus"
28
- def singularize(word)
29
- apply_inflections(word, inflections.singulars)
30
- end
31
-
32
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument
33
- # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
34
- # lowerCamelCase.
35
- #
36
- # +camelize+ will also convert '/' to '::' which is useful for converting
37
- # paths to namespaces.
38
- #
39
- # 'active_model'.camelize # => "ActiveModel"
40
- # 'active_model'.camelize(:lower) # => "activeModel"
41
- # 'active_model/errors'.camelize # => "ActiveModel::Errors"
42
- # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
43
- #
44
- # As a rule of thumb you can think of +camelize+ as the inverse of
45
- # +underscore+, though there are cases where that does not hold:
46
- #
47
- # 'SSLError'.underscore.camelize # => "SslError"
48
- def camelize(term, uppercase_first_letter = true)
49
- string = term.to_s
50
- if uppercase_first_letter
51
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
52
- else
53
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
54
- end
55
- string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
56
- end
57
-
58
- # Makes an underscored, lowercase form from the expression in the string.
59
- #
60
- # Changes '::' to '/' to convert namespaces to paths.
61
- #
62
- # 'ActiveModel'.underscore # => "active_model"
63
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
64
- #
65
- # As a rule of thumb you can think of +underscore+ as the inverse of
66
- # +camelize+, though there are cases where that does not hold:
67
- #
68
- # 'SSLError'.underscore.camelize # => "SslError"
69
- def underscore(camel_cased_word)
70
- word = camel_cased_word.to_s.dup
71
- word.gsub!('::', '/')
72
- word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
73
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
74
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
75
- word.gsub!("-", "_")
76
- word.downcase!
77
- word
78
- end
79
-
80
- # Capitalizes the first word and turns underscores into spaces and strips a
81
- # trailing "_id", if any. Like +titleize+, this is meant for creating pretty
82
- # output.
83
- #
84
- # 'employee_salary'.humanize # => "Employee salary"
85
- # 'author_id'.humanize # => "Author"
86
- def humanize(lower_case_and_underscored_word)
87
- result = lower_case_and_underscored_word.to_s.dup
88
- inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
89
- result.gsub!(/_id$/, "")
90
- result.gsub!('_', ' ')
91
- result.gsub(/([a-z\d]*)/i) { |match|
92
- "#{inflections.acronyms[match] || match.downcase}"
93
- }.gsub(/^\w/) { |match| match.upcase }
94
- end
95
-
96
- # Capitalizes all the words and replaces some characters in the string to
97
- # create a nicer looking title. +titleize+ is meant for creating pretty
98
- # output. It is not used in the Rails internals.
99
- #
100
- # +titleize+ is also aliased as +titlecase+.
101
- #
102
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
103
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
104
- # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
105
- # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
106
- def titleize(word)
107
- humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
108
- end
109
-
110
- # Create the name of a table like Rails does for models to table names. This
111
- # method uses the +pluralize+ method on the last word in the string.
112
- #
113
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
114
- # 'egg_and_ham'.tableize # => "egg_and_hams"
115
- # 'fancyCategory'.tableize # => "fancy_categories"
116
- def tableize(class_name)
117
- pluralize(underscore(class_name))
118
- end
119
-
120
- # Create a class name from a plural table name like Rails does for table
121
- # names to models. Note that this returns a string and not a Class (To
122
- # convert to an actual class follow +classify+ with +constantize+).
123
- #
124
- # 'egg_and_hams'.classify # => "EggAndHam"
125
- # 'posts'.classify # => "Post"
126
- #
127
- # Singular names are not handled correctly:
128
- #
129
- # 'business'.classify # => "Busines"
130
- def classify(table_name)
131
- # strip out any leading schema name
132
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
133
- end
134
-
135
- # Replaces underscores with dashes in the string.
136
- #
137
- # 'puni_puni'.dasherize # => "puni-puni"
138
- def dasherize(underscored_word)
139
- underscored_word.gsub('_', '-')
140
- end
141
-
142
- # Removes the module part from the expression in the string.
143
- #
144
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
145
- # 'Inflections'.demodulize # => "Inflections"
146
- #
147
- # See also +deconstantize+.
148
- def demodulize(path)
149
- path = path.to_s
150
- if i = path.rindex('::')
151
- path[(i+2)..-1]
152
- else
153
- path
154
- end
155
- end
156
-
157
- # Removes the rightmost segment from the constant expression in the string.
158
- #
159
- # 'Net::HTTP'.deconstantize # => "Net"
160
- # '::Net::HTTP'.deconstantize # => "::Net"
161
- # 'String'.deconstantize # => ""
162
- # '::String'.deconstantize # => ""
163
- # ''.deconstantize # => ""
164
- #
165
- # See also +demodulize+.
166
- def deconstantize(path)
167
- path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
168
- end
169
-
170
- # Creates a foreign key name from a class name.
171
- # +separate_class_name_and_id_with_underscore+ sets whether
172
- # the method should put '_' between the name and 'id'.
173
- #
174
- # 'Message'.foreign_key # => "message_id"
175
- # 'Message'.foreign_key(false) # => "messageid"
176
- # 'Admin::Post'.foreign_key # => "post_id"
177
- def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
178
- underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
179
- end
180
-
181
- # Tries to find a constant with the name specified in the argument string.
182
- #
183
- # 'Module'.constantize # => Module
184
- # 'Test::Unit'.constantize # => Test::Unit
185
- #
186
- # The name is assumed to be the one of a top-level constant, no matter
187
- # whether it starts with "::" or not. No lexical context is taken into
188
- # account:
189
- #
190
- # C = 'outside'
191
- # module M
192
- # C = 'inside'
193
- # C # => 'inside'
194
- # 'C'.constantize # => 'outside', same as ::C
195
- # end
196
- #
197
- # NameError is raised when the name is not in CamelCase or the constant is
198
- # unknown.
199
- def constantize(camel_cased_word)
200
- names = camel_cased_word.split('::')
201
- names.shift if names.empty? || names.first.empty?
202
-
203
- names.inject(Object) do |constant, name|
204
- if constant == Object
205
- constant.const_get(name)
206
- else
207
- candidate = constant.const_get(name)
208
- next candidate if constant.const_defined?(name, false)
209
- next candidate unless Object.const_defined?(name)
210
-
211
- # Go down the ancestors to check it it's owned
212
- # directly before we reach Object or the end of ancestors.
213
- constant = constant.ancestors.inject do |const, ancestor|
214
- break const if ancestor == Object
215
- break ancestor if ancestor.const_defined?(name, false)
216
- const
217
- end
218
-
219
- # owner is in Object, so raise
220
- constant.const_get(name, false)
221
- end
222
- end
223
- end
224
-
225
- # Tries to find a constant with the name specified in the argument string.
226
- #
227
- # 'Module'.safe_constantize # => Module
228
- # 'Test::Unit'.safe_constantize # => Test::Unit
229
- #
230
- # The name is assumed to be the one of a top-level constant, no matter
231
- # whether it starts with "::" or not. No lexical context is taken into
232
- # account:
233
- #
234
- # C = 'outside'
235
- # module M
236
- # C = 'inside'
237
- # C # => 'inside'
238
- # 'C'.safe_constantize # => 'outside', same as ::C
239
- # end
240
- #
241
- # +nil+ is returned when the name is not in CamelCase or the constant (or
242
- # part of it) is unknown.
243
- #
244
- # 'blargle'.safe_constantize # => nil
245
- # 'UnknownModule'.safe_constantize # => nil
246
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
247
- def safe_constantize(camel_cased_word)
248
- constantize(camel_cased_word)
249
- rescue NameError => e
250
- raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
251
- e.name.to_s == camel_cased_word.to_s
252
- rescue ArgumentError => e
253
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
254
- end
255
-
256
- # Returns the suffix that should be added to a number to denote the position
257
- # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
258
- #
259
- # ordinal(1) # => "st"
260
- # ordinal(2) # => "nd"
261
- # ordinal(1002) # => "nd"
262
- # ordinal(1003) # => "rd"
263
- # ordinal(-11) # => "th"
264
- # ordinal(-1021) # => "st"
265
- def ordinal(number)
266
- abs_number = number.to_i.abs
267
-
268
- if (11..13).include?(abs_number % 100)
269
- "th"
270
- else
271
- case abs_number % 10
272
- when 1; "st"
273
- when 2; "nd"
274
- when 3; "rd"
275
- else "th"
276
- end
277
- end
278
- end
279
-
280
- # Turns a number into an ordinal string used to denote the position in an
281
- # ordered sequence such as 1st, 2nd, 3rd, 4th.
282
- #
283
- # ordinalize(1) # => "1st"
284
- # ordinalize(2) # => "2nd"
285
- # ordinalize(1002) # => "1002nd"
286
- # ordinalize(1003) # => "1003rd"
287
- # ordinalize(-11) # => "-11th"
288
- # ordinalize(-1021) # => "-1021st"
289
- def ordinalize(number)
290
- "#{number}#{ordinal(number)}"
291
- end
292
-
293
- private
294
-
295
- # Mount a regular expression that will match part by part of the constant.
296
- # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
297
- def const_regexp(camel_cased_word) #:nodoc:
298
- parts = camel_cased_word.split("::")
299
- last = parts.pop
300
-
301
- parts.reverse.inject(last) do |acc, part|
302
- part.empty? ? acc : "#{part}(::#{acc})?"
303
- end
304
- end
305
-
306
- # Applies inflection rules for +singularize+ and +pluralize+.
307
- #
308
- # apply_inflections('post', inflections.plurals) # => "posts"
309
- # apply_inflections('posts', inflections.singulars) # => "post"
310
- def apply_inflections(word, rules)
311
- result = word.to_s.dup
312
-
313
- if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
314
- result
315
- else
316
- rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
317
- result
318
- end
319
- end
320
- end
321
- end
322
- end