opal-factory_girl 4.5.0.3

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/factory_girl/lib/factory_girl.rb +132 -0
  3. data/factory_girl/lib/factory_girl/aliases.rb +18 -0
  4. data/factory_girl/lib/factory_girl/attribute.rb +62 -0
  5. data/factory_girl/lib/factory_girl/attribute/association.rb +27 -0
  6. data/factory_girl/lib/factory_girl/attribute/dynamic.rb +24 -0
  7. data/factory_girl/lib/factory_girl/attribute/sequence.rb +16 -0
  8. data/factory_girl/lib/factory_girl/attribute/static.rb +16 -0
  9. data/factory_girl/lib/factory_girl/attribute_assigner.rb +97 -0
  10. data/factory_girl/lib/factory_girl/attribute_list.rb +67 -0
  11. data/factory_girl/lib/factory_girl/callback.rb +40 -0
  12. data/factory_girl/lib/factory_girl/callbacks_observer.rb +21 -0
  13. data/factory_girl/lib/factory_girl/configuration.rb +33 -0
  14. data/factory_girl/lib/factory_girl/declaration.rb +23 -0
  15. data/factory_girl/lib/factory_girl/declaration/association.rb +28 -0
  16. data/factory_girl/lib/factory_girl/declaration/dynamic.rb +26 -0
  17. data/factory_girl/lib/factory_girl/declaration/implicit.rb +33 -0
  18. data/factory_girl/lib/factory_girl/declaration/static.rb +26 -0
  19. data/factory_girl/lib/factory_girl/declaration_list.rb +49 -0
  20. data/factory_girl/lib/factory_girl/decorator.rb +21 -0
  21. data/factory_girl/lib/factory_girl/decorator/attribute_hash.rb +17 -0
  22. data/factory_girl/lib/factory_girl/decorator/class_key_hash.rb +27 -0
  23. data/factory_girl/lib/factory_girl/decorator/disallows_duplicates_registry.rb +13 -0
  24. data/factory_girl/lib/factory_girl/decorator/invocation_tracker.rb +19 -0
  25. data/factory_girl/lib/factory_girl/decorator/new_constructor.rb +12 -0
  26. data/factory_girl/lib/factory_girl/definition.rb +141 -0
  27. data/factory_girl/lib/factory_girl/definition_hierarchy.rb +48 -0
  28. data/factory_girl/lib/factory_girl/definition_proxy.rb +174 -0
  29. data/factory_girl/lib/factory_girl/errors.rb +25 -0
  30. data/factory_girl/lib/factory_girl/evaluation.rb +23 -0
  31. data/factory_girl/lib/factory_girl/evaluator.rb +76 -0
  32. data/factory_girl/lib/factory_girl/evaluator_class_definer.rb +20 -0
  33. data/factory_girl/lib/factory_girl/factory.rb +162 -0
  34. data/factory_girl/lib/factory_girl/factory_runner.rb +33 -0
  35. data/factory_girl/lib/factory_girl/find_definitions.rb +25 -0
  36. data/factory_girl/lib/factory_girl/linter.rb +47 -0
  37. data/factory_girl/lib/factory_girl/null_factory.rb +18 -0
  38. data/factory_girl/lib/factory_girl/null_object.rb +24 -0
  39. data/factory_girl/lib/factory_girl/registry.rb +38 -0
  40. data/factory_girl/lib/factory_girl/reload.rb +8 -0
  41. data/factory_girl/lib/factory_girl/sequence.rb +62 -0
  42. data/factory_girl/lib/factory_girl/strategy/attributes_for.rb +13 -0
  43. data/factory_girl/lib/factory_girl/strategy/build.rb +15 -0
  44. data/factory_girl/lib/factory_girl/strategy/create.rb +18 -0
  45. data/factory_girl/lib/factory_girl/strategy/null.rb +11 -0
  46. data/factory_girl/lib/factory_girl/strategy/stub.rb +73 -0
  47. data/factory_girl/lib/factory_girl/strategy_calculator.rb +26 -0
  48. data/factory_girl/lib/factory_girl/strategy_syntax_method_registrar.rb +46 -0
  49. data/factory_girl/lib/factory_girl/syntax.rb +7 -0
  50. data/factory_girl/lib/factory_girl/syntax/default.rb +76 -0
  51. data/factory_girl/lib/factory_girl/syntax/methods.rb +95 -0
  52. data/factory_girl/lib/factory_girl/syntax_runner.rb +6 -0
  53. data/factory_girl/lib/factory_girl/trait.rb +30 -0
  54. data/factory_girl/lib/factory_girl/version.rb +3 -0
  55. data/lib/opal-factory_girl.rb +16 -0
  56. data/lib/opal/factory_girl/version.rb +8 -0
  57. data/opal/opal-factory_girl.rb +184 -0
  58. data/opal/opal/active_support/inflector/methods.rb +395 -0
  59. metadata +142 -0
@@ -0,0 +1,7 @@
1
+ require 'factory_girl/syntax/methods'
2
+ require 'factory_girl/syntax/default'
3
+
4
+ module FactoryGirl
5
+ module Syntax
6
+ end
7
+ end
@@ -0,0 +1,76 @@
1
+ module FactoryGirl
2
+ module Syntax
3
+ module Default
4
+ include Methods
5
+
6
+ def define(&block)
7
+ DSL.run(block)
8
+ end
9
+
10
+ def modify(&block)
11
+ ModifyDSL.run(block)
12
+ end
13
+
14
+ class DSL
15
+ def factory(name, options = {}, &block)
16
+ factory = Factory.new(name, options)
17
+ proxy = FactoryGirl::DefinitionProxy.new(factory.definition)
18
+ proxy.instance_eval(&block) if block_given?
19
+
20
+ FactoryGirl.register_factory(factory)
21
+
22
+ proxy.child_factories.each do |(child_name, child_options, child_block)|
23
+ parent_factory = child_options.delete(:parent) || name
24
+ factory(child_name, child_options.merge(parent: parent_factory), &child_block)
25
+ end
26
+ end
27
+
28
+ def sequence(name, *args, &block)
29
+ FactoryGirl.register_sequence(Sequence.new(name, *args, &block))
30
+ end
31
+
32
+ def trait(name, &block)
33
+ FactoryGirl.register_trait(Trait.new(name, &block))
34
+ end
35
+
36
+ def to_create(&block)
37
+ FactoryGirl.to_create(&block)
38
+ end
39
+
40
+ def skip_create
41
+ FactoryGirl.skip_create
42
+ end
43
+
44
+ def initialize_with(&block)
45
+ FactoryGirl.initialize_with(&block)
46
+ end
47
+
48
+ def self.run(block)
49
+ new.instance_eval(&block)
50
+ end
51
+
52
+ delegate :before, :after, :callback, to: :configuration
53
+
54
+ private
55
+
56
+ def configuration
57
+ FactoryGirl.configuration
58
+ end
59
+ end
60
+
61
+ class ModifyDSL
62
+ def factory(name, options = {}, &block)
63
+ factory = FactoryGirl.factory_by_name(name)
64
+ proxy = FactoryGirl::DefinitionProxy.new(factory.definition.overridable)
65
+ proxy.instance_eval(&block)
66
+ end
67
+
68
+ def self.run(block)
69
+ new.instance_eval(&block)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ extend Syntax::Default
76
+ end
@@ -0,0 +1,95 @@
1
+ module FactoryGirl
2
+ module Syntax
3
+ ## This module is a container for all strategy methods provided by
4
+ ## FactoryGirl. This includes all the default strategies provided ({Methods#build},
5
+ ## {Methods#create}, {Methods#build_stubbed}, and {Methods#attributes_for}), as well as
6
+ ## the complementary *_list methods.
7
+ ## @example singular factory execution
8
+ ## # basic use case
9
+ ## build(:completed_order)
10
+ ##
11
+ ## # factory yielding its result to a block
12
+ ## create(:post) do |post|
13
+ ## create(:comment, post: post)
14
+ ## end
15
+ ##
16
+ ## # factory with attribute override
17
+ ## attributes_for(:post, title: "I love Ruby!")
18
+ ##
19
+ ## # factory with traits and attribute override
20
+ ## build_stubbed(:user, :admin, :male, name: "John Doe")
21
+ ##
22
+ ## @example multiple factory execution
23
+ ## # basic use case
24
+ ## build_list(:completed_order, 2)
25
+ ## create_list(:completed_order, 2)
26
+ ##
27
+ ## # factory with attribute override
28
+ ## attributes_for_list(:post, 4, title: "I love Ruby!")
29
+ ##
30
+ ## # factory with traits and attribute override
31
+ ## build_stubbed_list(:user, 15, :admin, :male, name: "John Doe")
32
+ module Methods
33
+ # @!parse FactoryGirl.register_default_strategies
34
+ # @!method build(name, *traits_and_overrides, &block)
35
+ # (see #strategy_method)
36
+ # Builds a registered factory by name.
37
+ # @return [Object] instantiated object defined by the factory
38
+
39
+ # @!method create(name, *traits_and_overrides, &block)
40
+ # (see #strategy_method)
41
+ # Creates a registered factory by name.
42
+ # @return [Object] instantiated object defined by the factory
43
+
44
+ # @!method build_stubbed(name, *traits_and_overrides, &block)
45
+ # (see #strategy_method)
46
+ # Builds a stubbed registered factory by name.
47
+ # @return [Object] instantiated object defined by the factory
48
+
49
+ # @!method attributes_for(name, *traits_and_overrides, &block)
50
+ # (see #strategy_method)
51
+ # Generates a hash of attributes for a registered factory by name.
52
+ # @return [Hash] hash of attributes for the factory
53
+
54
+ # @!method build_list(name, amount, *traits_and_overrides)
55
+ # (see #strategy_method_list)
56
+ # @return [Array] array of built objects defined by the factory
57
+
58
+ # @!method create_list(name, amount, *traits_and_overrides)
59
+ # (see #strategy_method_list)
60
+ # @return [Array] array of created objects defined by the factory
61
+
62
+ # @!method build_stubbed_list(name, amount, *traits_and_overrides)
63
+ # (see #strategy_method_list)
64
+ # @return [Array] array of stubbed objects defined by the factory
65
+
66
+ # @!method attributes_for_list(name, amount, *traits_and_overrides)
67
+ # (see #strategy_method_list)
68
+ # @return [Array<Hash>] array of attribute hashes for the factory
69
+
70
+ # @!method strategy_method
71
+ # @!visibility private
72
+ # @param [Symbol] name name of the factory to build
73
+ # @param [Array<Symbol, Symbol, Hash>] traits_and_overrides splat args traits and a hash of overrides
74
+ # @param [Proc] block block to be executed
75
+
76
+ # @!method strategy_method_list
77
+ # @!visibility private
78
+ # @param [Symbol] name name of the factory to execute
79
+ # @param [Integer] amount the number of instances to execute
80
+ # @param [Array<Symbol, Symbol, Hash>] traits_and_overrides splat args traits and a hash of overrides
81
+
82
+ # Generates and returns the next value in a sequence.
83
+ #
84
+ # Arguments:
85
+ # name: (Symbol)
86
+ # The name of the sequence that a value should be generated for.
87
+ #
88
+ # Returns:
89
+ # The next value in the sequence. (Object)
90
+ def generate(name)
91
+ FactoryGirl.sequence_by_name(name).next
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,6 @@
1
+ module FactoryGirl
2
+ # @api private
3
+ class SyntaxRunner
4
+ include Syntax::Methods
5
+ end
6
+ end
@@ -0,0 +1,30 @@
1
+ module FactoryGirl
2
+ # @api private
3
+ class Trait
4
+ attr_reader :name, :definition
5
+
6
+ def initialize(name, &block)
7
+ @name = name
8
+ @block = block
9
+ @definition = Definition.new(@name)
10
+
11
+ proxy = FactoryGirl::DefinitionProxy.new(@definition)
12
+ proxy.instance_eval(&@block) if block_given?
13
+ end
14
+
15
+ delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
16
+ :callbacks, :attributes, to: :@definition
17
+
18
+ def names
19
+ [@name]
20
+ end
21
+
22
+ def ==(other)
23
+ name == other.name &&
24
+ block == other.block
25
+ end
26
+
27
+ protected
28
+ attr_reader :block
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module FactoryGirl
2
+ VERSION = '4.5.0'
3
+ end
@@ -0,0 +1,16 @@
1
+ [
2
+ 'jruby/synchronized',
3
+ 'monitor',
4
+ 'thread_safe/jruby_cache_backend',
5
+ 'atomic',
6
+ 'jruby',
7
+ 'logger',
8
+ 'active_support/inflector/methods', # has an incompatible regex in it
9
+ ].each { |r| Opal::Processor.stub_file r }
10
+
11
+
12
+ # Just register our opal code path with opal build tools
13
+ Opal.append_path File.expand_path('../../opal', __FILE__)
14
+ Opal.append_path File.expand_path('../../factory_girl/lib', __FILE__)
15
+ require 'opal-activesupport'
16
+ Opal.use_gem 'activesupport'
@@ -0,0 +1,8 @@
1
+ require 'factory_girl/version'
2
+
3
+ module Opal
4
+ module FactoryGirl
5
+ # Add a minor version to the end in case there are opal specific issues to fix
6
+ VERSION = "#{::FactoryGirl::VERSION}.3"
7
+ end
8
+ end
@@ -0,0 +1,184 @@
1
+ module Mutex_m
2
+ def synchronize
3
+ yield
4
+ end
5
+ end
6
+
7
+ require 'active_support/core_ext/module/delegation'
8
+ require 'active_support/inflector'
9
+ require 'opal/active_support/inflector/methods'
10
+ require 'active_support/core_ext/string/inflections'
11
+ require 'active_support/core_ext/string'
12
+ require 'active_support/notifications/fanout'
13
+
14
+ class String
15
+ def humanize
16
+ ActiveSupport::Inflector.humanize(self)
17
+ end
18
+ end
19
+
20
+ class ActiveSupport::Inflector::Inflections
21
+ def humans
22
+ @humans ||= []
23
+ end
24
+
25
+ def acronyms
26
+ @acronyms ||= {}
27
+ end
28
+
29
+ def acronym_regex
30
+ @acronym_regex ||= /(?=a)b/
31
+ end
32
+
33
+ def human(rule, replacement)
34
+ @humans.prepend([rule, replacement])
35
+ end
36
+
37
+ def acronym(word)
38
+ a = acronyms
39
+ _ = acronym_regex
40
+ a[word.downcase] = word
41
+ @acronym_regex = /#{a.values.join("|")}/
42
+ end
43
+ end
44
+
45
+ module ActiveSupport
46
+ module Notifications
47
+ # This is a default queue implementation that ships with Notifications.
48
+ # It just pushes events to all registered log subscribers.
49
+ #
50
+ # This class is thread safe. All methods are reentrant.
51
+ class Fanout
52
+ #def subscribe(pattern = nil, block = Proc.new)
53
+ # Opal doesn't like block without &block and Proc.new isn't valid anyways
54
+ def subscribe(pattern = nil, &block)
55
+ subscriber = Subscribers.new pattern, block
56
+ synchronize do
57
+ @subscribers << subscriber
58
+ @listeners_for.clear
59
+ end
60
+ subscriber
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ require 'factory_girl'
67
+
68
+ # Opal didn't seem like like synchronize { super }, so removed sync, probably super w/ args inside a block
69
+ module ThreadSafe
70
+ class SynchronizedCacheBackend
71
+ def [](key)
72
+ super
73
+ end
74
+
75
+ def []=(key, value)
76
+ super
77
+ end
78
+
79
+ def compute_if_absent(key)
80
+ super
81
+ end
82
+
83
+ def compute_if_present(key)
84
+ super
85
+ end
86
+
87
+ def compute(key)
88
+ super
89
+ end
90
+
91
+ def merge_pair(key, value)
92
+ super
93
+ end
94
+
95
+ def replace_pair(key, old_value, new_value)
96
+ super
97
+ end
98
+
99
+ def replace_if_exists(key, new_value)
100
+ super
101
+ end
102
+
103
+ def get_and_set(key, value)
104
+ super
105
+ end
106
+
107
+ def key?(key)
108
+ super
109
+ end
110
+
111
+ def value?(value)
112
+ super
113
+ end
114
+
115
+ def delete(key)
116
+ super
117
+ end
118
+
119
+ def delete_pair(key, value)
120
+ super
121
+ end
122
+
123
+ def clear
124
+ super
125
+ end
126
+
127
+ def size
128
+ super
129
+ end
130
+
131
+ def get_or_default(key, default_value)
132
+ super
133
+ end
134
+
135
+ private
136
+ def dupped_backend
137
+ super
138
+ end
139
+ end
140
+ end
141
+
142
+ module FactoryGirl
143
+ class Decorator
144
+ class NewConstructor < Decorator
145
+ def build_class_accessor
146
+ @build_class
147
+ end
148
+
149
+ # For some reason, the instance_variable_get was not working
150
+ #delegate :new, to: :@build_class
151
+ delegate :new, to: :build_class_accessor
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+ module FactoryGirl
158
+ class AttributeAssigner
159
+ def instance_builder_set(&instance_builder)
160
+ @instance_builder = instance_builder
161
+ end
162
+ end
163
+
164
+ # @api private
165
+ class Factory
166
+ def run(build_strategy, overrides, &block)
167
+ block ||= ->(result) { result }
168
+ compile
169
+
170
+ strategy = StrategyCalculator.new(build_strategy).strategy.new
171
+
172
+ evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
173
+ constr = compiled_constructor
174
+ attribute_assigner = AttributeAssigner.new(evaluator, build_class, &constr)
175
+ # Opal issues with passing a block reference in a constructor
176
+ attribute_assigner.instance_builder_set(&constr)
177
+
178
+ evaluation = Evaluation.new(attribute_assigner, compiled_to_create)
179
+ evaluation.add_observer(CallbacksObserver.new(callbacks, evaluator))
180
+
181
+ strategy.result(evaluation).tap(&block)
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,395 @@
1
+ require 'active_support/inflections'
2
+
3
+ module ActiveSupport
4
+ # The Inflector transforms words from singular to plural, class names to table
5
+ # names, modularized class names to ones without, and class names to foreign
6
+ # keys. The default inflections for pluralization, singularization, and
7
+ # uncountable words are kept in inflections.rb.
8
+ #
9
+ # The Rails core team has stated patches for the inflections library will not
10
+ # be accepted in order to avoid breaking legacy applications which may be
11
+ # relying on errant inflections. If you discover an incorrect inflection and
12
+ # require it for your application or wish to define rules for languages other
13
+ # than English, please correct or add them yourself (explained below).
14
+ module Inflector
15
+ extend self
16
+
17
+ # Returns the plural form of the word in the string.
18
+ #
19
+ # If passed an optional +locale+ parameter, the word will be
20
+ # pluralized using rules defined for that language. By default,
21
+ # this parameter is set to <tt>:en</tt>.
22
+ #
23
+ # pluralize('post') # => "posts"
24
+ # pluralize('octopus') # => "octopi"
25
+ # pluralize('sheep') # => "sheep"
26
+ # pluralize('words') # => "words"
27
+ # pluralize('CamelOctopus') # => "CamelOctopi"
28
+ # pluralize('ley', :es) # => "leyes"
29
+ def pluralize(word, locale = :en)
30
+ apply_inflections(word, inflections(locale).plurals)
31
+ end
32
+
33
+ # The reverse of #pluralize, returns the singular form of a word in a
34
+ # string.
35
+ #
36
+ # If passed an optional +locale+ parameter, the word will be
37
+ # singularized using rules defined for that language. By default,
38
+ # this parameter is set to <tt>:en</tt>.
39
+ #
40
+ # singularize('posts') # => "post"
41
+ # singularize('octopi') # => "octopus"
42
+ # singularize('sheep') # => "sheep"
43
+ # singularize('word') # => "word"
44
+ # singularize('CamelOctopi') # => "CamelOctopus"
45
+ # singularize('leyes', :es) # => "ley"
46
+ def singularize(word, locale = :en)
47
+ apply_inflections(word, inflections(locale).singulars)
48
+ end
49
+
50
+ # Converts strings to UpperCamelCase.
51
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
52
+ # lowerCamelCase.
53
+ #
54
+ # Also converts '/' to '::' which is useful for converting
55
+ # paths to namespaces.
56
+ #
57
+ # camelize('active_model') # => "ActiveModel"
58
+ # camelize('active_model', false) # => "activeModel"
59
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
60
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
61
+ #
62
+ # As a rule of thumb you can think of +camelize+ as the inverse of
63
+ # #underscore, though there are cases where that does not hold:
64
+ #
65
+ # camelize(underscore('SSLError')) # => "SslError"
66
+ def camelize(term, uppercase_first_letter = true)
67
+ string = term.to_s
68
+ if uppercase_first_letter
69
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
70
+ else
71
+ string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
72
+ end
73
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
74
+ string.gsub!('/'.freeze, '::'.freeze)
75
+ string
76
+ end
77
+
78
+ # Makes an underscored, lowercase form from the expression in the string.
79
+ #
80
+ # Changes '::' to '/' to convert namespaces to paths.
81
+ #
82
+ # underscore('ActiveModel') # => "active_model"
83
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
84
+ #
85
+ # As a rule of thumb you can think of +underscore+ as the inverse of
86
+ # #camelize, though there are cases where that does not hold:
87
+ #
88
+ # camelize(underscore('SSLError')) # => "SslError"
89
+ def underscore(camel_cased_word)
90
+ return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
91
+ word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
92
+ word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
93
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
94
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
95
+ word.tr!("-".freeze, "_".freeze)
96
+ word.downcase!
97
+ word
98
+ end
99
+
100
+ # Tweaks an attribute name for display to end users.
101
+ #
102
+ # Specifically, performs these transformations:
103
+ #
104
+ # * Applies human inflection rules to the argument.
105
+ # * Deletes leading underscores, if any.
106
+ # * Removes a "_id" suffix if present.
107
+ # * Replaces underscores with spaces, if any.
108
+ # * Downcases all words except acronyms.
109
+ # * Capitalizes the first word.
110
+ #
111
+ # The capitalization of the first word can be turned off by setting the
112
+ # +:capitalize+ option to false (default is true).
113
+ #
114
+ # humanize('employee_salary') # => "Employee salary"
115
+ # humanize('author_id') # => "Author"
116
+ # humanize('author_id', capitalize: false) # => "author"
117
+ # humanize('_id') # => "Id"
118
+ #
119
+ # If "SSL" was defined to be an acronym:
120
+ #
121
+ # humanize('ssl_error') # => "SSL error"
122
+ #
123
+ def humanize(lower_case_and_underscored_word, options = {})
124
+ result = lower_case_and_underscored_word.to_s.dup
125
+
126
+ # no humans attr exists on inflections, need to port that over
127
+
128
+ # opal - string mutation
129
+ inflections.humans.each { |(rule, replacement)| break if (result = result.sub(rule, replacement)) }
130
+
131
+ # opal - \A and \z not supported
132
+ #result = result.sub(/\A_+/, ''.freeze)
133
+ result = result.sub(/^_+/, ''.freeze)
134
+ #result = result.sub(/_id\z/, ''.freeze)
135
+ result = result.sub(/_id$/, ''.freeze)
136
+ result = result.tr('_'.freeze, ' '.freeze)
137
+
138
+ result = result.gsub(/([a-z\d]*)/i) do |match|
139
+ "#{inflections.acronyms[match] || match.downcase}"
140
+ end
141
+
142
+ if options.fetch(:capitalize, true)
143
+ #result = result.sub(/\A\w/) { |match| match.upcase }
144
+ result = result.sub(/^\w/) { |match| match.upcase }
145
+ end
146
+
147
+ result
148
+ end
149
+
150
+ # Capitalizes all the words and replaces some characters in the string to
151
+ # create a nicer looking title. +titleize+ is meant for creating pretty
152
+ # output. It is not used in the Rails internals.
153
+ #
154
+ # +titleize+ is also aliased as +titlecase+.
155
+ #
156
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
157
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
158
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
159
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
160
+ def titleize(word)
161
+ # negative lookbehind doesn't work in Firefox / Safari
162
+ # humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
163
+ humanized = humanize(underscore(word))
164
+ humanized.reverse.gsub(/[a-z](?!['’`])\b/) { |match| match.capitalize }.reverse
165
+ end
166
+
167
+ # Creates the name of a table like Rails does for models to table names.
168
+ # This method uses the #pluralize method on the last word in the string.
169
+ #
170
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
171
+ # tableize('ham_and_egg') # => "ham_and_eggs"
172
+ # tableize('fancyCategory') # => "fancy_categories"
173
+ def tableize(class_name)
174
+ pluralize(underscore(class_name))
175
+ end
176
+
177
+ # Creates a class name from a plural table name like Rails does for table
178
+ # names to models. Note that this returns a string and not a Class (To
179
+ # convert to an actual class follow +classify+ with #constantize).
180
+ #
181
+ # classify('ham_and_eggs') # => "HamAndEgg"
182
+ # classify('posts') # => "Post"
183
+ #
184
+ # Singular names are not handled correctly:
185
+ #
186
+ # classify('calculus') # => "Calculu"
187
+ def classify(table_name)
188
+ # strip out any leading schema name
189
+ camelize(singularize(table_name.to_s.sub(/.*\./, ''.freeze)))
190
+ end
191
+
192
+ # Replaces underscores with dashes in the string.
193
+ #
194
+ # dasherize('puni_puni') # => "puni-puni"
195
+ def dasherize(underscored_word)
196
+ underscored_word.tr('_'.freeze, '-'.freeze)
197
+ end
198
+
199
+ # Removes the module part from the expression in the string.
200
+ #
201
+ # demodulize('ActiveRecord::CoreExtensions::String::Inflections') # => "Inflections"
202
+ # demodulize('Inflections') # => "Inflections"
203
+ # demodulize('::Inflections') # => "Inflections"
204
+ # demodulize('') # => ""
205
+ #
206
+ # See also #deconstantize.
207
+ def demodulize(path)
208
+ path = path.to_s
209
+ if i = path.rindex('::')
210
+ path[(i+2)..-1]
211
+ else
212
+ path
213
+ end
214
+ end
215
+
216
+ # Removes the rightmost segment from the constant expression in the string.
217
+ #
218
+ # deconstantize('Net::HTTP') # => "Net"
219
+ # deconstantize('::Net::HTTP') # => "::Net"
220
+ # deconstantize('String') # => ""
221
+ # deconstantize('::String') # => ""
222
+ # deconstantize('') # => ""
223
+ #
224
+ # See also #demodulize.
225
+ def deconstantize(path)
226
+ path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
227
+ end
228
+
229
+ # Creates a foreign key name from a class name.
230
+ # +separate_class_name_and_id_with_underscore+ sets whether
231
+ # the method should put '_' between the name and 'id'.
232
+ #
233
+ # foreign_key('Message') # => "message_id"
234
+ # foreign_key('Message', false) # => "messageid"
235
+ # foreign_key('Admin::Post') # => "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
+ # Tries to find a constant with the name specified in the argument string.
241
+ #
242
+ # 'Module'.constantize # => Module
243
+ # 'Foo::Bar'.constantize # => Foo::Bar
244
+ #
245
+ # The name is assumed to be the one of a top-level constant, no matter
246
+ # whether it starts with "::" or not. No lexical context is taken into
247
+ # account:
248
+ #
249
+ # C = 'outside'
250
+ # module M
251
+ # C = 'inside'
252
+ # C # => 'inside'
253
+ # 'C'.constantize # => 'outside', same as ::C
254
+ # end
255
+ #
256
+ # NameError is raised when the name is not in CamelCase or the constant is
257
+ # unknown.
258
+ def constantize(camel_cased_word)
259
+ names = camel_cased_word.split('::'.freeze)
260
+
261
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
262
+ Object.const_get(camel_cased_word) if names.empty?
263
+
264
+ # Remove the first blank element in case of '::ClassName' notation.
265
+ names.shift if names.size > 1 && names.first.empty?
266
+
267
+ names.inject(Object) do |constant, name|
268
+ if constant == Object
269
+ constant.const_get(name)
270
+ else
271
+ candidate = constant.const_get(name)
272
+ next candidate if constant.const_defined?(name, false)
273
+ next candidate unless Object.const_defined?(name)
274
+
275
+ # Go down the ancestors to check if it is owned directly. The check
276
+ # stops when we reach Object or the end of ancestors tree.
277
+ constant = constant.ancestors.inject do |const, ancestor|
278
+ break const if ancestor == Object
279
+ break ancestor if ancestor.const_defined?(name, false)
280
+ const
281
+ end
282
+
283
+ # owner is in Object, so raise
284
+ constant.const_get(name, false)
285
+ end
286
+ end
287
+ end
288
+
289
+ # Tries to find a constant with the name specified in the argument string.
290
+ #
291
+ # safe_constantize('Module') # => Module
292
+ # safe_constantize('Foo::Bar') # => Foo::Bar
293
+ #
294
+ # The name is assumed to be the one of a top-level constant, no matter
295
+ # whether it starts with "::" or not. No lexical context is taken into
296
+ # account:
297
+ #
298
+ # C = 'outside'
299
+ # module M
300
+ # C = 'inside'
301
+ # C # => 'inside'
302
+ # safe_constantize('C') # => 'outside', same as ::C
303
+ # end
304
+ #
305
+ # +nil+ is returned when the name is not in CamelCase or the constant (or
306
+ # part of it) is unknown.
307
+ #
308
+ # safe_constantize('blargle') # => nil
309
+ # safe_constantize('UnknownModule') # => nil
310
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
311
+ def safe_constantize(camel_cased_word)
312
+ constantize(camel_cased_word)
313
+ rescue NameError => e
314
+ raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
315
+ e.name.to_s == camel_cased_word.to_s)
316
+ rescue ArgumentError => e
317
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
318
+ end
319
+
320
+ # Returns the suffix that should be added to a number to denote the position
321
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
322
+ #
323
+ # ordinal(1) # => "st"
324
+ # ordinal(2) # => "nd"
325
+ # ordinal(1002) # => "nd"
326
+ # ordinal(1003) # => "rd"
327
+ # ordinal(-11) # => "th"
328
+ # ordinal(-1021) # => "st"
329
+ def ordinal(number)
330
+ abs_number = number.to_i.abs
331
+
332
+ if (11..13).include?(abs_number % 100)
333
+ "th"
334
+ else
335
+ case abs_number % 10
336
+ when 1;
337
+ "st"
338
+ when 2;
339
+ "nd"
340
+ when 3;
341
+ "rd"
342
+ else
343
+ "th"
344
+ end
345
+ end
346
+ end
347
+
348
+ # Turns a number into an ordinal string used to denote the position in an
349
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
350
+ #
351
+ # ordinalize(1) # => "1st"
352
+ # ordinalize(2) # => "2nd"
353
+ # ordinalize(1002) # => "1002nd"
354
+ # ordinalize(1003) # => "1003rd"
355
+ # ordinalize(-11) # => "-11th"
356
+ # ordinalize(-1021) # => "-1021st"
357
+ def ordinalize(number)
358
+ "#{number}#{ordinal(number)}"
359
+ end
360
+
361
+ private
362
+
363
+ # Mounts a regular expression, returned as a string to ease interpolation,
364
+ # that will match part by part the given constant.
365
+ #
366
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
367
+ # const_regexp("::") # => "::"
368
+ def const_regexp(camel_cased_word) #:nodoc:
369
+ parts = camel_cased_word.split("::".freeze)
370
+
371
+ return Regexp.escape(camel_cased_word) if parts.blank?
372
+
373
+ last = parts.pop
374
+
375
+ parts.reverse.inject(last) do |acc, part|
376
+ part.empty? ? acc : "#{part}(::#{acc})?"
377
+ end
378
+ end
379
+
380
+ # Applies inflection rules for +singularize+ and +pluralize+.
381
+ #
382
+ # apply_inflections('post', inflections.plurals) # => "posts"
383
+ # apply_inflections('posts', inflections.singulars) # => "post"
384
+ def apply_inflections(word, rules)
385
+ result = word.to_s.dup
386
+
387
+ if word.empty? || inflections.uncountables.uncountable?(result)
388
+ result
389
+ else
390
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
391
+ result
392
+ end
393
+ end
394
+ end
395
+ end