conject 0.0.1

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 (61) hide show
  1. data/.gitignore +4 -0
  2. data/.rvmrc +2 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +32 -0
  5. data/NOTES.txt +61 -0
  6. data/README.md +8 -0
  7. data/Rakefile +12 -0
  8. data/TODO +9 -0
  9. data/conject.gemspec +21 -0
  10. data/lib/conject.rb +40 -0
  11. data/lib/conject/borrowed_active_support_inflector.rb +525 -0
  12. data/lib/conject/class_ext_construct_with.rb +123 -0
  13. data/lib/conject/class_finder.rb +11 -0
  14. data/lib/conject/composition_error.rb +33 -0
  15. data/lib/conject/dependency_resolver.rb +16 -0
  16. data/lib/conject/extended_metaid.rb +33 -0
  17. data/lib/conject/object_context.rb +61 -0
  18. data/lib/conject/object_definition.rb +10 -0
  19. data/lib/conject/object_factory.rb +28 -0
  20. data/lib/conject/utilities.rb +8 -0
  21. data/lib/conject/version.rb +3 -0
  22. data/rake_tasks/rspec.rake +25 -0
  23. data/spec/acceptance/dev/README +7 -0
  24. data/spec/acceptance/regression/README +12 -0
  25. data/spec/acceptance/regression/basic_composition_spec.rb +29 -0
  26. data/spec/acceptance/regression/basic_object_creation_spec.rb +42 -0
  27. data/spec/acceptance/regression/nested_contexts_spec.rb +86 -0
  28. data/spec/conject/borrowed_active_support_inflector_spec.rb +28 -0
  29. data/spec/conject/class_ext_construct_with_spec.rb +226 -0
  30. data/spec/conject/class_finder_spec.rb +36 -0
  31. data/spec/conject/composition_error_spec.rb +124 -0
  32. data/spec/conject/dependency_resolver_spec.rb +32 -0
  33. data/spec/conject/extended_metaid_spec.rb +90 -0
  34. data/spec/conject/object_context_spec.rb +186 -0
  35. data/spec/conject/object_definition_spec.rb +31 -0
  36. data/spec/conject/object_factory_spec.rb +89 -0
  37. data/spec/conject/utilities_spec.rb +30 -0
  38. data/spec/spec_helper.rb +24 -0
  39. data/spec/support/SPEC_HELPERS_GO_HERE +0 -0
  40. data/spec/support/load_path_helpers.rb +27 -0
  41. data/spec/test_data/basic_composition/fence.rb +5 -0
  42. data/spec/test_data/basic_composition/front_desk.rb +2 -0
  43. data/spec/test_data/basic_composition/grass.rb +2 -0
  44. data/spec/test_data/basic_composition/guest.rb +5 -0
  45. data/spec/test_data/basic_composition/lobby.rb +5 -0
  46. data/spec/test_data/basic_composition/nails.rb +2 -0
  47. data/spec/test_data/basic_composition/tv.rb +2 -0
  48. data/spec/test_data/basic_composition/wood.rb +2 -0
  49. data/spec/test_data/simple_stuff/some_random_class.rb +2 -0
  50. data/spike/arity_funny_business_in_different_ruby_versions.rb +34 -0
  51. data/spike/depends_on_spike.rb +146 -0
  52. data/spike/donkey_fail.rb +48 -0
  53. data/spike/donkey_journey.rb +50 -0
  54. data/spike/go.rb +11 -0
  55. data/spike/metaid.rb +28 -0
  56. data/spike/object_definition.rb +125 -0
  57. data/spike/sample.rb +125 -0
  58. data/src/user_model.rb +4 -0
  59. data/src/user_presenter.rb +10 -0
  60. data/src/user_view.rb +3 -0
  61. metadata +165 -0
@@ -0,0 +1,4 @@
1
+ /coverage
2
+ tags
3
+ *.swp
4
+ /README.html
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm_gemset_create_on_use_flag=1
2
+ rvm 1.9.3@object_context
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in conject.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ conject (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ multi_json (1.0.4)
11
+ rake (0.9.2.2)
12
+ rspec (2.8.0)
13
+ rspec-core (~> 2.8.0)
14
+ rspec-expectations (~> 2.8.0)
15
+ rspec-mocks (~> 2.8.0)
16
+ rspec-core (2.8.0)
17
+ rspec-expectations (2.8.0)
18
+ diff-lcs (~> 1.1.2)
19
+ rspec-mocks (2.8.0)
20
+ simplecov (0.5.4)
21
+ multi_json (~> 1.0.3)
22
+ simplecov-html (~> 0.5.3)
23
+ simplecov-html (0.5.3)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ conject!
30
+ rake
31
+ rspec
32
+ simplecov
@@ -0,0 +1,61 @@
1
+
2
+ # Jan 2012
3
+
4
+ # Requesting an object from a Context
5
+ # if object exists in current context
6
+ # return it
7
+ # else
8
+ # if a super context has the object
9
+ # use the object from the super context
10
+ # else
11
+ # create the object
12
+ # store in this Context as a singleton
13
+ # return the object
14
+ #
15
+ # Special case: an object being created within a subcontext requires a
16
+ # component that SHOULD be defined in the supercontext
17
+ # BUT has not yet been created.
18
+ #
19
+ # Support a request-time option :from_super => [ :obj1, :obj2]
20
+ #
21
+ #
22
+ # Special case: an object being created within a subcontext requires a
23
+ # component that SHOULD be defined in that SAME subcontext,
24
+ # but whose name overlaps with an object already defined in
25
+ # the SUPER context.
26
+ #
27
+ # Suppoer a request-time option :define_own => [ :obj1, :obj2]
28
+ # (:hide_in_super ? name debatable)
29
+ #
30
+ #
31
+ #
32
+
33
+ # Dec 2011
34
+
35
+ # Term: REGULAR OBJECT
36
+ # Instance of some class with 0 or more components
37
+
38
+ #
39
+ # Object definitions
40
+ # Manually add to context by name
41
+ # Indirectly added to context by meta programming in concerned class
42
+ # Generated defaults
43
+ #
44
+ # If no def exists
45
+ # if require_on_demand is true
46
+ # require guessed library name
47
+ # if def still not exist
48
+ # generate default def
49
+ #
50
+ # Use def to construct object
51
+ #
52
+ # ? when a class uses meta programming to define aspects of itself
53
+ # we are not yet in a context for certain.
54
+ # We could assume global context but that isn't always right.
55
+ #
56
+ # REAL QUESTION:
57
+ # How do we define non-global contexts conveniently?
58
+ # :w
59
+ #
60
+
61
+
@@ -0,0 +1,8 @@
1
+ # Object Context #
2
+
3
+ Retrieve and relate objects within contexts. Provides dependency injection convenience inspired by the simplicity of Google's Guice.
4
+
5
+ # VERY ALPHA #
6
+
7
+
8
+
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ HERE = File.dirname(__FILE__)
4
+
5
+ Dir[File.expand_path(HERE) + "/rake_tasks/*.rake"].each do |rake_file|
6
+ import rake_file
7
+ end
8
+
9
+ # desc 'Default: run specs and cucumber features'
10
+ # task :default => [ "spec", "cuc:features" ]
11
+
12
+ task :default => [ "spec" ]
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ Sketch out some more use cases
2
+
3
+ Move / copy test data (loadable classes) into spec/acceptance/testdata
4
+ Make helpers for accessing that folder
5
+ Make helper to set load path for a specific test
6
+
7
+ Move basic object creation spec to regression
8
+
9
+
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/conject/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David Crosby"]
6
+ gem.email = ["david.crosby@atomicobject.com"]
7
+ gem.description = %q{Enable Guice-like dependency injection and contextual object interactions.}
8
+ gem.summary = %q{Enable Guice-like dependency injection and contextual object interactions.}
9
+ gem.homepage = "https://github.com/dcrosby42/conject"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "conject"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Conject::VERSION
17
+
18
+ gem.add_development_dependency "rake"
19
+ gem.add_development_dependency "rspec"
20
+ gem.add_development_dependency "simplecov"
21
+ end
@@ -0,0 +1,40 @@
1
+ require "conject/version"
2
+
3
+ module Conject
4
+ #
5
+ # Provide access to the default ObjectContext.
6
+ # This context is created on first use, and can
7
+ # serve as the root of all other ObjectContexts.
8
+ #
9
+ def self.default_object_context
10
+ @default_object_context ||= create_object_context(nil)
11
+ end
12
+
13
+ def self.default_object_factory
14
+ @default_object_factory ||= Conject::ObjectFactory.new(
15
+ :class_finder => Conject::ClassFinder.new,
16
+ :dependency_resolver => Conject::DependencyResolver.new
17
+ )
18
+ end
19
+
20
+ def self.create_object_context(parent_context, object_factory=nil)
21
+ object_factory ||= default_object_factory
22
+ Conject::ObjectContext.new(
23
+ :parent_context => parent_context,
24
+ :object_factory => object_factory
25
+ )
26
+ end
27
+ end
28
+
29
+ # The rest of the libraries namespace themselves under Conject so
30
+ # they must be required AFTER the initial definition of Conject.
31
+ require 'conject/object_definition'
32
+ require 'conject/extended_metaid'
33
+ require 'conject/class_ext_construct_with'
34
+ require 'conject/object_context'
35
+ require 'conject/object_factory'
36
+ require 'conject/class_finder'
37
+ require 'conject/dependency_resolver'
38
+ require 'conject/utilities'
39
+ require 'conject/composition_error'
40
+ require 'conject/borrowed_active_support_inflector'
@@ -0,0 +1,525 @@
1
+ # in case active_support/inflector is required without the rest of active_support
2
+ module BorrowedActiveSupport
3
+ module Inflector
4
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
5
+ # inflection rules. Examples:
6
+ #
7
+ # BorrowedActiveSupport::Inflector.inflections do |inflect|
8
+ # inflect.plural /^(ox)$/i, '\1\2en'
9
+ # inflect.singular /^(ox)en/i, '\1'
10
+ #
11
+ # inflect.irregular 'octopus', 'octopi'
12
+ #
13
+ # inflect.uncountable "equipment"
14
+ # end
15
+ #
16
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
17
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
18
+ # already have been loaded.
19
+ class Inflections
20
+ def self.instance
21
+ @__instance__ ||= new
22
+ end
23
+
24
+ attr_reader :plurals, :singulars, :uncountables, :humans
25
+
26
+ def initialize
27
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
28
+ end
29
+
30
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
31
+ # The replacement should always be a string that may include references to the matched data from the rule.
32
+ def plural(rule, replacement)
33
+ @uncountables.delete(rule) if rule.is_a?(String)
34
+ @uncountables.delete(replacement)
35
+ @plurals.insert(0, [rule, replacement])
36
+ end
37
+
38
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
39
+ # The replacement should always be a string that may include references to the matched data from the rule.
40
+ def singular(rule, replacement)
41
+ @uncountables.delete(rule) if rule.is_a?(String)
42
+ @uncountables.delete(replacement)
43
+ @singulars.insert(0, [rule, replacement])
44
+ end
45
+
46
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
47
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
48
+ #
49
+ # Examples:
50
+ # irregular 'octopus', 'octopi'
51
+ # irregular 'person', 'people'
52
+ def irregular(singular, plural)
53
+ @uncountables.delete(singular)
54
+ @uncountables.delete(plural)
55
+ if singular[0,1].upcase == plural[0,1].upcase
56
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
57
+ plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
58
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
59
+ else
60
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
61
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
62
+ plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
63
+ plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
64
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
65
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
66
+ end
67
+ end
68
+
69
+ # Add uncountable words that shouldn't be attempted inflected.
70
+ #
71
+ # Examples:
72
+ # uncountable "money"
73
+ # uncountable "money", "information"
74
+ # uncountable %w( money information rice )
75
+ def uncountable(*words)
76
+ (@uncountables << words).flatten!
77
+ end
78
+
79
+ # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
80
+ # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
81
+ # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
82
+ #
83
+ # Examples:
84
+ # human /_cnt$/i, '\1_count'
85
+ # human "legacy_col_person_name", "Name"
86
+ def human(rule, replacement)
87
+ @humans.insert(0, [rule, replacement])
88
+ end
89
+
90
+ # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
91
+ # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
92
+ # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
93
+ #
94
+ # Examples:
95
+ # clear :all
96
+ # clear :plurals
97
+ def clear(scope = :all)
98
+ case scope
99
+ when :all
100
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
101
+ else
102
+ instance_variable_set "@#{scope}", []
103
+ end
104
+ end
105
+ end
106
+
107
+ # Yields a singleton instance of Inflector::Inflections so you can specify additional
108
+ # inflector rules.
109
+ #
110
+ # Example:
111
+ # BorrowedActiveSupport::Inflector.inflections do |inflect|
112
+ # inflect.uncountable "rails"
113
+ # end
114
+ def inflections
115
+ if block_given?
116
+ yield Inflections.instance
117
+ else
118
+ Inflections.instance
119
+ end
120
+ end
121
+
122
+ # Returns the plural form of the word in the string.
123
+ #
124
+ # Examples:
125
+ # "post".pluralize # => "posts"
126
+ # "octopus".pluralize # => "octopi"
127
+ # "sheep".pluralize # => "sheep"
128
+ # "words".pluralize # => "words"
129
+ # "CamelOctopus".pluralize # => "CamelOctopi"
130
+ def pluralize(word)
131
+ result = word.to_s.dup
132
+
133
+ if word.empty? || inflections.uncountables.include?(result.downcase)
134
+ result
135
+ else
136
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
137
+ result
138
+ end
139
+ end
140
+
141
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
142
+ #
143
+ # Examples:
144
+ # "posts".singularize # => "post"
145
+ # "octopi".singularize # => "octopus"
146
+ # "sheep".singularize # => "sheep"
147
+ # "word".singularize # => "word"
148
+ # "CamelOctopi".singularize # => "CamelOctopus"
149
+ def singularize(word)
150
+ result = word.to_s.dup
151
+
152
+ if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
153
+ result
154
+ else
155
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
156
+ result
157
+ end
158
+ end
159
+
160
+ # Capitalizes the first word and turns underscores into spaces and strips a
161
+ # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
162
+ #
163
+ # Examples:
164
+ # "employee_salary" # => "Employee salary"
165
+ # "author_id" # => "Author"
166
+ def humanize(lower_case_and_underscored_word)
167
+ result = lower_case_and_underscored_word.to_s.dup
168
+
169
+ inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
170
+ result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
171
+ end
172
+
173
+ # Capitalizes all the words and replaces some characters in the string to create
174
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
175
+ # used in the Rails internals.
176
+ #
177
+ # +titleize+ is also aliased as as +titlecase+.
178
+ #
179
+ # Examples:
180
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
181
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
182
+ def titleize(word)
183
+ humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
184
+ end
185
+
186
+ # Create the name of a table like Rails does for models to table names. This method
187
+ # uses the +pluralize+ method on the last word in the string.
188
+ #
189
+ # Examples
190
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
191
+ # "egg_and_ham".tableize # => "egg_and_hams"
192
+ # "fancyCategory".tableize # => "fancy_categories"
193
+ def tableize(class_name)
194
+ pluralize(underscore(class_name))
195
+ end
196
+
197
+ # Create a class name from a plural table name like Rails does for table names to models.
198
+ # Note that this returns a string and not a Class. (To convert to an actual class
199
+ # follow +classify+ with +constantize+.)
200
+ #
201
+ # Examples:
202
+ # "egg_and_hams".classify # => "EggAndHam"
203
+ # "posts".classify # => "Post"
204
+ #
205
+ # Singular names are not handled correctly:
206
+ # "business".classify # => "Busines"
207
+ def classify(table_name)
208
+ # strip out any leading schema name
209
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
210
+ end
211
+ end
212
+
213
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
214
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
215
+ # in inflections.rb.
216
+ #
217
+ # The Rails core team has stated patches for the inflections library will not be accepted
218
+ # in order to avoid breaking legacy applications which may be relying on errant inflections.
219
+ # If you discover an incorrect inflection and require it for your application, you'll need
220
+ # to correct it yourself (explained below).
221
+ module Inflector
222
+ extend self
223
+
224
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
225
+ # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
226
+ #
227
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
228
+ #
229
+ # Examples:
230
+ # "active_record".camelize # => "ActiveRecord"
231
+ # "active_record".camelize(:lower) # => "activeRecord"
232
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
233
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
234
+ #
235
+ # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
236
+ # though there are cases where that does not hold:
237
+ #
238
+ # "SSLError".underscore.camelize # => "SslError"
239
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
240
+ if first_letter_in_uppercase
241
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
242
+ else
243
+ lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
244
+ end
245
+ end
246
+
247
+ # Makes an underscored, lowercase form from the expression in the string.
248
+ #
249
+ # Changes '::' to '/' to convert namespaces to paths.
250
+ #
251
+ # Examples:
252
+ # "ActiveRecord".underscore # => "active_record"
253
+ # "ActiveRecord::Errors".underscore # => active_record/errors
254
+ #
255
+ # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
256
+ # though there are cases where that does not hold:
257
+ #
258
+ # "SSLError".underscore.camelize # => "SslError"
259
+ def underscore(camel_cased_word)
260
+ word = camel_cased_word.to_s.dup
261
+ word.gsub!(/::/, '/')
262
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
263
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
264
+ word.tr!("-", "_")
265
+ word.downcase!
266
+ word
267
+ end
268
+
269
+ # Replaces underscores with dashes in the string.
270
+ #
271
+ # Example:
272
+ # "puni_puni" # => "puni-puni"
273
+ def dasherize(underscored_word)
274
+ underscored_word.gsub(/_/, '-')
275
+ end
276
+
277
+ # Removes the module part from the expression in the string.
278
+ #
279
+ # Examples:
280
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
281
+ # "Inflections".demodulize # => "Inflections"
282
+ def demodulize(class_name_in_module)
283
+ class_name_in_module.to_s.gsub(/^.*::/, '')
284
+ end
285
+
286
+ # Creates a foreign key name from a class name.
287
+ # +separate_class_name_and_id_with_underscore+ sets whether
288
+ # the method should put '_' between the name and 'id'.
289
+ #
290
+ # Examples:
291
+ # "Message".foreign_key # => "message_id"
292
+ # "Message".foreign_key(false) # => "messageid"
293
+ # "Admin::Post".foreign_key # => "post_id"
294
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
295
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
296
+ end
297
+
298
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
299
+ # #const_defined? and changes their default behavior.
300
+ if Module.method(:const_get).arity == 1
301
+ # Tries to find a constant with the name specified in the argument string:
302
+ #
303
+ # "Module".constantize # => Module
304
+ # "Test::Unit".constantize # => Test::Unit
305
+ #
306
+ # The name is assumed to be the one of a top-level constant, no matter whether
307
+ # it starts with "::" or not. No lexical context is taken into account:
308
+ #
309
+ # C = 'outside'
310
+ # module M
311
+ # C = 'inside'
312
+ # C # => 'inside'
313
+ # "C".constantize # => 'outside', same as ::C
314
+ # end
315
+ #
316
+ # NameError is raised when the name is not in CamelCase or the constant is
317
+ # unknown.
318
+ def constantize(camel_cased_word)
319
+ names = camel_cased_word.split('::')
320
+ names.shift if names.empty? || names.first.empty?
321
+
322
+ constant = Object
323
+ names.each do |name|
324
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
325
+ end
326
+ constant
327
+ end
328
+ else
329
+ def constantize(camel_cased_word) #:nodoc:
330
+ names = camel_cased_word.split('::')
331
+ names.shift if names.empty? || names.first.empty?
332
+
333
+ constant = Object
334
+ names.each do |name|
335
+ constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
336
+ end
337
+ constant
338
+ end
339
+ end
340
+
341
+ # Turns a number into an ordinal string used to denote the position in an
342
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
343
+ #
344
+ # Examples:
345
+ # ordinalize(1) # => "1st"
346
+ # ordinalize(2) # => "2nd"
347
+ # ordinalize(1002) # => "1002nd"
348
+ # ordinalize(1003) # => "1003rd"
349
+ # ordinalize(-11) # => "-11th"
350
+ # ordinalize(-1021) # => "-1021st"
351
+ def ordinalize(number)
352
+ if (11..13).include?(number.to_i.abs % 100)
353
+ "#{number}th"
354
+ else
355
+ case number.to_i.abs % 10
356
+ when 1; "#{number}st"
357
+ when 2; "#{number}nd"
358
+ when 3; "#{number}rd"
359
+ else "#{number}th"
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ # String inflections define new methods on the String class to transform names for different purposes.
367
+ # For instance, you can figure out the name of a table from the name of a class.
368
+ #
369
+ # "ScaleScore".tableize # => "scale_scores"
370
+ #
371
+ class String
372
+ # Returns the plural form of the word in the string.
373
+ #
374
+ # "post".pluralize # => "posts"
375
+ # "octopus".pluralize # => "octopi"
376
+ # "sheep".pluralize # => "sheep"
377
+ # "words".pluralize # => "words"
378
+ # "the blue mailman".pluralize # => "the blue mailmen"
379
+ # "CamelOctopus".pluralize # => "CamelOctopi"
380
+ def pluralize
381
+ BorrowedActiveSupport::Inflector.pluralize(self)
382
+ end
383
+
384
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
385
+ #
386
+ # "posts".singularize # => "post"
387
+ # "octopi".singularize # => "octopus"
388
+ # "sheep".singularize # => "sheep"
389
+ # "word".singularize # => "word"
390
+ # "the blue mailmen".singularize # => "the blue mailman"
391
+ # "CamelOctopi".singularize # => "CamelOctopus"
392
+ def singularize
393
+ BorrowedActiveSupport::Inflector.singularize(self)
394
+ end
395
+
396
+ # +constantize+ tries to find a declared constant with the name specified
397
+ # in the string. It raises a NameError when the name is not in CamelCase
398
+ # or is not initialized.
399
+ #
400
+ # Examples
401
+ # "Module".constantize # => Module
402
+ # "Class".constantize # => Class
403
+ def constantize
404
+ BorrowedActiveSupport::Inflector.constantize(self)
405
+ end
406
+
407
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
408
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
409
+ #
410
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
411
+ #
412
+ # "active_record".camelize # => "ActiveRecord"
413
+ # "active_record".camelize(:lower) # => "activeRecord"
414
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
415
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
416
+ def camelize(first_letter = :upper)
417
+ case first_letter
418
+ when :upper then BorrowedActiveSupport::Inflector.camelize(self, true)
419
+ when :lower then BorrowedActiveSupport::Inflector.camelize(self, false)
420
+ end
421
+ end
422
+ alias_method :camelcase, :camelize
423
+
424
+ # Capitalizes all the words and replaces some characters in the string to create
425
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
426
+ # used in the Rails internals.
427
+ #
428
+ # +titleize+ is also aliased as +titlecase+.
429
+ #
430
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
431
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
432
+ def titleize
433
+ BorrowedActiveSupport::Inflector.titleize(self)
434
+ end
435
+ alias_method :titlecase, :titleize
436
+
437
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
438
+ #
439
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
440
+ #
441
+ # "ActiveRecord".underscore # => "active_record"
442
+ # "ActiveRecord::Errors".underscore # => active_record/errors
443
+ def underscore
444
+ BorrowedActiveSupport::Inflector.underscore(self)
445
+ end
446
+
447
+ # Replaces underscores with dashes in the string.
448
+ #
449
+ # "puni_puni" # => "puni-puni"
450
+ def dasherize
451
+ BorrowedActiveSupport::Inflector.dasherize(self)
452
+ end
453
+
454
+ # Removes the module part from the constant expression in the string.
455
+ #
456
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
457
+ # "Inflections".demodulize # => "Inflections"
458
+ def demodulize
459
+ BorrowedActiveSupport::Inflector.demodulize(self)
460
+ end
461
+
462
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
463
+ #
464
+ # ==== Examples
465
+ #
466
+ # class Person
467
+ # def to_param
468
+ # "#{id}-#{name.parameterize}"
469
+ # end
470
+ # end
471
+ #
472
+ # @person = Person.find(1)
473
+ # # => #<Person id: 1, name: "Donald E. Knuth">
474
+ #
475
+ # <%= link_to(@person.name, person_path %>
476
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
477
+ def parameterize(sep = '-')
478
+ BorrowedActiveSupport::Inflector.parameterize(self, sep)
479
+ end
480
+
481
+ # Creates the name of a table like Rails does for models to table names. This method
482
+ # uses the +pluralize+ method on the last word in the string.
483
+ #
484
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
485
+ # "egg_and_ham".tableize # => "egg_and_hams"
486
+ # "fancyCategory".tableize # => "fancy_categories"
487
+ def tableize
488
+ BorrowedActiveSupport::Inflector.tableize(self)
489
+ end
490
+
491
+ # Create a class name from a plural table name like Rails does for table names to models.
492
+ # Note that this returns a string and not a class. (To convert to an actual class
493
+ # follow +classify+ with +constantize+.)
494
+ #
495
+ # "egg_and_hams".classify # => "EggAndHam"
496
+ # "posts".classify # => "Post"
497
+ #
498
+ # Singular names are not handled correctly.
499
+ #
500
+ # "business".classify # => "Busines"
501
+ def classify
502
+ BorrowedActiveSupport::Inflector.classify(self)
503
+ end
504
+
505
+ # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
506
+ # Like +titleize+, this is meant for creating pretty output.
507
+ #
508
+ # "employee_salary" # => "Employee salary"
509
+ # "author_id" # => "Author"
510
+ def humanize
511
+ BorrowedActiveSupport::Inflector.humanize(self)
512
+ end
513
+
514
+ # Creates a foreign key name from a class name.
515
+ # +separate_class_name_and_id_with_underscore+ sets whether
516
+ # the method should put '_' between the name and 'id'.
517
+ #
518
+ # Examples
519
+ # "Message".foreign_key # => "message_id"
520
+ # "Message".foreign_key(false) # => "messageid"
521
+ # "Admin::Post".foreign_key # => "post_id"
522
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
523
+ BorrowedActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
524
+ end
525
+ end