snowblink-factory_girl 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ We're using GitHub[http://github.com/thoughtbot/factory_girl/tree/master] and Lighthouse[http://thoughtbot.lighthouseapp.com/projects/14354], and we've been getting any combination of github pull requests, Lighthouse tickets, patches, emails, etc. We need to normalize this workflow to make sure we don't miss any fixes.
2
+
3
+ * Make sure you're accessing the source from the {official repository}[http://github.com/thoughtbot/factory_girl/tree/master].
4
+ * We prefer git branches over patches, but we can take either.
5
+ * If you're using git, please make a branch for each separate contribution. We can cherry pick your commits, but pulling from a branch is easier.
6
+ * If you're submitting patches, please cut each fix or feature into a separate patch.
7
+ * There should be a Lighthouse[http://thoughtbot.lighthouseapp.com/projects/14354] ticket for any submission. If you've found a bug and want to fix it, open a new ticket at the same time.
8
+ * Please <b>don't send pull requests</b> Just update the lighthouse ticket with the url for your fix (or attach the patch) when it's ready. The github pull requests pretty much get dropped on the floor until someone with commit rights notices them in the mailbox.
9
+ * Contributions without tests won't be accepted.
10
+ * Please don't submit patches or branches that update the Gem version.
@@ -0,0 +1,29 @@
1
+ 1.1.4 (November 28, 2008)
2
+ Factory.build now uses Factory.create for associations of the built object
3
+ Factory definitions are now detected in subdirectories, such as
4
+ factories/person_factory.rb (thanks to Josh Nichols)
5
+ Factory definitions are now loaded after the environment in a Rails project
6
+ (fixes some issues with dependencies being loaded too early) (thanks to
7
+ Josh Nichols)
8
+ Factory names ending in 's' no longer cause problems (thanks to Alex Sharp
9
+ and Josh Owens)
10
+
11
+ 1.1.3 (September 12, 2008)
12
+ Automatically pull in definitions from factories.rb, test/factories.rb, or
13
+ spec/factories.rb
14
+ 1.1.2 (July 30, 2008)
15
+ Improved error handling for invalid and undefined factories/attributes
16
+ Improved handling of strings vs symbols vs classes
17
+ Added a prettier syntax for handling associations
18
+ Updated documentation and fixed compatibility with Rails 2.1
19
+
20
+ 1.1.1 (June 23, 2008)
21
+ The attribute "name" no longer requires using #add_attribute
22
+
23
+ 1.1.0 (June 3, 2008)
24
+ Added support for dependent attributes
25
+ Fixed the attributes_for build strategy to not build associations
26
+ Added support for sequences
27
+
28
+ 1.0.0 (May 31, 208)
29
+ First version
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Joe Ferris and thoughtbot, inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ h1. factory_girl
2
+
3
+ Written by "Joe Ferris":mailto:jferris@thoughtbot.com.
4
+
5
+ Thanks to Tammer Saleh, Dan Croak, and Jon Yurek of thoughtbot, inc.
6
+
7
+ Copyright 2008 Joe Ferris and thoughtbot, inc.
8
+
9
+ h2. Download
10
+
11
+ Github: "Page":http://github.com/thoughtbot/factory_girl/tree/master "Clone":git://github.com/thoughtbot/factory_girl.git
12
+
13
+ Gem: <pre>gem install thoughtbot-factory_girl --source http://gems.github.com</pre>
14
+
15
+ Note: if you install factory_girl using the gem from Github, you'll need this
16
+ in your environment.rb if you want to use Rails 2.1's dependency manager:
17
+
18
+ config.gem "thoughtbot-factory_girl",
19
+ :lib => "factory_girl",
20
+ :source => "http://gems.github.com"
21
+
22
+ h2. More Information
23
+
24
+ "Our blog":http://giantrobots.thoughtbot.com
25
+
26
+ "factory_girl rdoc":http://dev.thoughtbot.com/factory_girl
27
+
28
+ "Mailing list":http://groups.google.com/group/factory_girl
29
+
30
+ h2. Contributing
31
+
32
+ Please read the contribution guidelines before submitting patches or pull requests.
33
+
34
+ h2. Defining factories
35
+
36
+ <pre><code># This will guess the User class
37
+ Factory.define :user do |u|
38
+ u.first_name 'John'
39
+ u.last_name 'Doe'
40
+ u.admin false
41
+ end
42
+
43
+ # This will use the User class (Admin would have been guessed)
44
+ Factory.define :admin, :class => User do |u|
45
+ u.first_name 'Admin'
46
+ u.last_name 'User'
47
+ u.admin true
48
+ end</code></pre>
49
+
50
+
51
+ It is recommended that you create a test/factories.rb file and define your
52
+ factories there. This file can be included from test_helper or directly from
53
+ your test files. Don't forget:
54
+ <pre><code>require 'factory_girl'</code></pre>
55
+
56
+
57
+ h2. Lazy Attributes
58
+
59
+ Most attributes can be added using static values that are evaluated when the
60
+ factory is defined, but some attributes (such as associations and other
61
+ attributes that must be dynamically generated) will need values assigned each
62
+ time an instance is generated. These "lazy" attributes can be added by passing
63
+ a block instead of a parameter:
64
+
65
+ <pre><code>Factory.define :user do |u|
66
+ # ...
67
+ u.activation_code { User.generate_activation_code }
68
+ end</code></pre>
69
+
70
+
71
+ h2. Dependent Attributes
72
+
73
+ Some attributes may need to be generated based on the values of other
74
+ attributes. This can be done by calling the attribute name on
75
+ Factory::AttributeProxy, which is yielded to lazy attribute blocks:
76
+
77
+ <pre><code>Factory.define :user do |u|
78
+ u.first_name 'Joe'
79
+ u.last_name 'Blow'
80
+ u.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
81
+ end
82
+
83
+ Factory(:user, :last_name => 'Doe').email
84
+ # => "joe.doe@example.com"</code></pre>
85
+
86
+
87
+ h2. Associations
88
+
89
+ Associated instances can be generated by using the association method when
90
+ defining a lazy attribute:
91
+
92
+ <pre><code>Factory.define :post do |p|
93
+ # ...
94
+ p.author {|author| author.association(:user, :last_name => 'Writely') }
95
+ end</code></pre>
96
+
97
+
98
+ When using the association method, the same build strategy (build, create, or attributes_for) will be used for all generated instances:
99
+
100
+ <pre><code># Builds and saves a User and a Post
101
+ post = Factory(:post)
102
+ post.new_record? # => false
103
+ post.author.new_record # => false
104
+
105
+ # Builds but does not save a User and a Post
106
+ Factory.build(:post)
107
+ post.new_record? # => true
108
+ post.author.new_record # => true</code></pre>
109
+
110
+ Because this pattern is so common, a prettier syntax is available for defining
111
+ associations:
112
+
113
+ <pre><code># The following definitions are equivilent:
114
+ Factory.define :post do |p|
115
+ p.author {|a| a.association(:user) }
116
+ end
117
+
118
+ Factory.define :post do |p|
119
+ p.association :author, :factory => :user
120
+ end</code></pre>
121
+
122
+ If the factory name is the same as the association name, the factory name can
123
+ be left out.
124
+
125
+
126
+ h2. Sequences
127
+
128
+ Unique values in a specific format (for example, e-mail addresses) can be
129
+ generated using sequences. Sequences are defined by calling Factory.sequence,
130
+ and values in a sequence are generated by calling Factory.next:
131
+
132
+ <pre><code># Defines a new sequence
133
+ Factory.sequence :email do |n|
134
+ "person#{n}@example.com"
135
+ end
136
+
137
+ Factory.next :email
138
+ # => "person1@example.com"
139
+
140
+ Factory.next :email
141
+ # => "person2@example.com"</code></pre>
142
+
143
+
144
+ h2. Using factories
145
+
146
+ <pre><code># Build and save a User instance
147
+ Factory(:user)
148
+
149
+ # Build a User instance and override the first_name property
150
+ Factory.build(:user, :first_name => 'Joe')
151
+
152
+ # Return an attributes Hash that can be used to build a User instance
153
+ attrs = Factory.attributes_for(:user)</code></pre>
154
+
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+ require 'rcov/rcovtask'
7
+ require 'date'
8
+
9
+ desc 'Default: run unit tests.'
10
+ task :default => :test
11
+
12
+ desc 'Test the factory_girl plugin.'
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << 'lib'
15
+ t.pattern = 'test/**/*_test.rb'
16
+ t.verbose = true
17
+ end
18
+
19
+ desc 'Performs code coverage on the factory_girl plugin.'
20
+ Rcov::RcovTask.new do |t|
21
+ t.libs << "test"
22
+ t.test_files = FileList['test/*_test.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ desc 'Generate documentation for the factory_girl plugin.'
27
+ Rake::RDocTask.new(:rdoc) do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'Factory Girl'
30
+ rdoc.options << '--line-numbers' << '--inline-source' << "--main" << "README.textile"
31
+ rdoc.rdoc_files.include('README.textile')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ desc 'Update documentation on website'
36
+ task :sync_docs => 'rdoc' do
37
+ `rsync -ave ssh rdoc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/factory_girl`
38
+ end
39
+
40
+ spec = Gem::Specification.new do |s|
41
+ s.name = %q{factory_girl}
42
+ s.version = "1.1.5"
43
+ s.summary = %q{factory_girl provides a framework and DSL for defining and
44
+ using model instance factories.}
45
+ s.description = %q{factory_girl provides a framework and DSL for defining and
46
+ using factories - less error-prone, more explicit, and
47
+ all-around easier to work with than fixtures.}
48
+
49
+ s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb']
50
+ s.require_path = 'lib'
51
+ s.test_files = Dir[*['test/**/*_test.rb']]
52
+
53
+ s.has_rdoc = true
54
+ s.extra_rdoc_files = ["README.textile"]
55
+ s.rdoc_options = ['--line-numbers', '--inline-source', "--main", "README.textile"]
56
+
57
+ s.authors = ["Joe Ferris"]
58
+ s.email = %q{jferris@thoughtbot.com}
59
+
60
+ s.platform = Gem::Platform::RUBY
61
+ end
62
+
63
+ Rake::GemPackageTask.new spec do |pkg|
64
+ pkg.need_tar = true
65
+ pkg.need_zip = true
66
+ end
67
+
68
+ desc "Clean files generated by rake tasks"
69
+ task :clobber => [:clobber_rdoc, :clobber_package]
70
+
71
+ desc "Generate a gemspec file"
72
+ task :gemspec do
73
+ File.open("#{spec.name}.gemspec", 'w') do |f|
74
+ f.write spec.to_ruby
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support'
2
+ require 'factory_girl/proxy'
3
+ require 'factory_girl/proxy/build'
4
+ require 'factory_girl/proxy/create'
5
+ require 'factory_girl/proxy/attributes_for'
6
+ require 'factory_girl/factory'
7
+ require 'factory_girl/attribute'
8
+ require 'factory_girl/attribute/static'
9
+ require 'factory_girl/attribute/dynamic'
10
+ require 'factory_girl/attribute/association'
11
+ require 'factory_girl/sequence'
12
+ require 'factory_girl/aliases'
13
+
14
+ # Shortcut for Factory.create.
15
+ #
16
+ # Example:
17
+ # Factory(:user, :name => 'Joe')
18
+ def Factory (name, attrs = {})
19
+ Factory.create(name, attrs)
20
+ end
21
+
22
+ if defined? Rails
23
+ Rails.configuration.after_initialize do
24
+ Factory.definition_file_paths = [
25
+ File.join(RAILS_ROOT, 'test', 'factories'),
26
+ File.join(RAILS_ROOT, 'spec', 'factories')
27
+ ]
28
+ Factory.find_definitions
29
+ end
30
+ else
31
+ Factory.find_definitions
32
+ end
33
+
@@ -0,0 +1,39 @@
1
+ class Factory
2
+
3
+ class << self
4
+ attr_accessor :aliases #:nodoc:
5
+ end
6
+ self.aliases = [
7
+ [/(.*)_id/, '\1'],
8
+ [/(.*)/, '\1_id']
9
+ ]
10
+
11
+ # Defines a new alias for attributes
12
+ #
13
+ # Arguments:
14
+ # pattern: (Regexp)
15
+ # A pattern that will be matched against attributes when looking for
16
+ # aliases. Contents captured in the pattern can be used in the alias.
17
+ # replace: (String)
18
+ # The alias that results from the matched pattern. Captured strings can
19
+ # be insert like String#sub.
20
+ #
21
+ # Example:
22
+ #
23
+ # Factory.alias /(.*)_confirmation/, '\1'
24
+ def self.alias (pattern, replace)
25
+ self.aliases << [pattern, replace]
26
+ end
27
+
28
+ def self.aliases_for (attribute) #:nodoc:
29
+ aliases.collect do |params|
30
+ pattern, replace = *params
31
+ if pattern.match(attribute.to_s)
32
+ attribute.to_s.sub(pattern, replace).to_sym
33
+ else
34
+ nil
35
+ end
36
+ end.compact << attribute
37
+ end
38
+
39
+ end
@@ -0,0 +1,24 @@
1
+ class Factory
2
+
3
+ class AttributeDefinitionError < RuntimeError
4
+ end
5
+
6
+ class Attribute #:nodoc:
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(name)
11
+ @name = name.to_sym
12
+
13
+ if @name.to_s =~ /=$/
14
+ raise AttributeDefinitionError,
15
+ "factory_girl uses 'f.#{@name.to_s.chop} value' syntax " +
16
+ "rather than 'f.#{@name} = value'"
17
+ end
18
+ end
19
+
20
+ def add_to(proxy)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,258 @@
1
+ class Factory
2
+
3
+ class << self
4
+ attr_accessor :factories #:nodoc:
5
+
6
+ # An Array of strings specifying locations that should be searched for
7
+ # factory definitions. By default, factory_girl will attempt to require
8
+ # "factories," "test/factories," and "spec/factories." Only the first
9
+ # existing file will be loaded.
10
+ attr_accessor :definition_file_paths
11
+ end
12
+
13
+ self.factories = {}
14
+ self.definition_file_paths = %w(factories test/factories spec/factories)
15
+
16
+ attr_reader :factory_name
17
+ attr_reader :attributes #:nodoc:
18
+
19
+ # Defines a new factory that can be used by the build strategies (create and
20
+ # build) to build new objects.
21
+ #
22
+ # Arguments:
23
+ # name: (Symbol)
24
+ # A unique name used to identify this factory.
25
+ # options: (Hash)
26
+ # class: the class that will be used when generating instances for this
27
+ # factory. If not specified, the class will be guessed from the
28
+ # factory name.
29
+ #
30
+ # Yields:
31
+ # The newly created factory (Factory)
32
+ def self.define (name, options = {})
33
+ instance = Factory.new(name, options)
34
+ yield(instance)
35
+ self.factories[instance.factory_name] = instance
36
+ end
37
+
38
+ def build_class #:nodoc:
39
+ @build_class ||= class_for(@options[:class] || factory_name)
40
+ end
41
+
42
+ def initialize (name, options = {}) #:nodoc:
43
+ assert_valid_options(options)
44
+ @factory_name = factory_name_for(name)
45
+ @options = options
46
+ @attributes = []
47
+ end
48
+
49
+ # Adds an attribute that should be assigned on generated instances for this
50
+ # factory.
51
+ #
52
+ # This method should be called with either a value or block, but not both. If
53
+ # called with a block, the attribute will be generated "lazily," whenever an
54
+ # instance is generated. Lazy attribute blocks will not be called if that
55
+ # attribute is overriden for a specific instance.
56
+ #
57
+ # When defining lazy attributes, an instance of Factory::Proxy will
58
+ # be yielded, allowing associations to be built using the correct build
59
+ # strategy.
60
+ #
61
+ # Arguments:
62
+ # name: (Symbol)
63
+ # The name of this attribute. This will be assigned using :"#{name}=" for
64
+ # generated instances.
65
+ # value: (Object)
66
+ # If no block is given, this value will be used for this attribute.
67
+ def add_attribute (name, value = nil, &block)
68
+ if block_given?
69
+ if value
70
+ raise AttributeDefinitionError, "Both value and block given"
71
+ else
72
+ attribute = Attribute::Dynamic.new(name, block)
73
+ end
74
+ else
75
+ attribute = Attribute::Static.new(name, value)
76
+ end
77
+
78
+ if attribute_defined?(attribute.name)
79
+ raise AttributeDefinitionError, "Attribute already defined: #{name}"
80
+ end
81
+
82
+ @attributes << attribute
83
+ end
84
+
85
+ # Calls add_attribute using the missing method name as the name of the
86
+ # attribute, so that:
87
+ #
88
+ # Factory.define :user do |f|
89
+ # f.name 'Billy Idol'
90
+ # end
91
+ #
92
+ # and:
93
+ #
94
+ # Factory.define :user do |f|
95
+ # f.add_attribute :name, 'Billy Idol'
96
+ # end
97
+ #
98
+ # are equivilent.
99
+ def method_missing (name, *args, &block)
100
+ add_attribute(name, *args, &block)
101
+ end
102
+
103
+ # Adds an attribute that builds an association. The associated instance will
104
+ # be built using the same build strategy as the parent instance.
105
+ #
106
+ # Example:
107
+ # Factory.define :user do |f|
108
+ # f.name 'Joey'
109
+ # end
110
+ #
111
+ # Factory.define :post do |f|
112
+ # f.association :author, :factory => :user
113
+ # end
114
+ #
115
+ # Arguments:
116
+ # name: (Symbol)
117
+ # The name of this attribute.
118
+ # options: (Hash)
119
+ # factory: (Symbol)
120
+ # The name of the factory to use when building the associated instance.
121
+ # If no name is given, the name of the attribute is assumed to be the
122
+ # name of the factory. For example, a "user" association will by
123
+ # default use the "user" factory.
124
+ def association (name, options = {})
125
+ factory_name = options.delete(:factory) || name
126
+ @attributes << Attribute::Association.new(name, factory_name, options)
127
+ end
128
+
129
+ # Generates and returns a Hash of attributes from this factory. Attributes
130
+ # can be individually overridden by passing in a Hash of attribute => value
131
+ # pairs.
132
+ #
133
+ # Arguments:
134
+ # overrides: (Hash)
135
+ # Attributes to overwrite for this set.
136
+ #
137
+ # Returns:
138
+ # A set of attributes that can be used to build an instance of the class
139
+ # this factory generates. (Hash)
140
+ def self.attributes_for (name, overrides = {})
141
+ factory_by_name(name).run(Proxy::AttributesFor, overrides)
142
+ end
143
+
144
+ # Generates and returns an instance from this factory. Attributes can be
145
+ # individually overridden by passing in a Hash of attribute => value pairs.
146
+ #
147
+ # Arguments:
148
+ # overrides: (Hash)
149
+ # See attributes_for
150
+ #
151
+ # Returns:
152
+ # An instance of the class this factory generates, with generated
153
+ # attributes assigned.
154
+ def self.build (name, overrides = {})
155
+ factory_by_name(name).run(Proxy::Build, overrides)
156
+ end
157
+
158
+ # Generates, saves, and returns an instance from this factory. Attributes can
159
+ # be individually overridden by passing in a Hash of attribute => value
160
+ # pairs.
161
+ #
162
+ # If the instance is not valid, an ActiveRecord::Invalid exception will be
163
+ # raised.
164
+ #
165
+ # Arguments:
166
+ # overrides: (Hash)
167
+ # See attributes_for
168
+ #
169
+ # Returns:
170
+ # A saved instance of the class this factory generates, with generated
171
+ # attributes assigned.
172
+ def self.create (name, overrides = {})
173
+ factory_by_name(name).run(Proxy::Create, overrides)
174
+ end
175
+
176
+ def self.find_definitions #:nodoc:
177
+ definition_file_paths.each do |path|
178
+ require("#{path}.rb") if File.exists?("#{path}.rb")
179
+
180
+ if File.directory? path
181
+ Dir[File.join(path, '*.rb')].each do |file|
182
+ require file
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ def run (proxy_class, overrides) #:nodoc:
189
+ proxy = proxy_class.new(build_class)
190
+ overrides = symbolize_keys(overrides)
191
+ overrides.each {|attr, val| proxy.set(attr, val) }
192
+ passed_keys = overrides.keys.collect {|k| Factory.aliases_for(k) }.flatten
193
+ @attributes.each do |attribute|
194
+ unless passed_keys.include?(attribute.name)
195
+ attribute.add_to(proxy)
196
+ end
197
+ end
198
+ proxy.result
199
+ end
200
+
201
+ private
202
+
203
+ def self.factory_by_name (name)
204
+ factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
205
+ end
206
+
207
+ def class_for (class_or_to_s)
208
+ if class_or_to_s.respond_to?(:to_sym)
209
+ Object.const_get(variable_name_to_class_name(class_or_to_s))
210
+ else
211
+ class_or_to_s
212
+ end
213
+ end
214
+
215
+ def factory_name_for (class_or_to_s)
216
+ if class_or_to_s.respond_to?(:to_sym)
217
+ class_or_to_s.to_sym
218
+ else
219
+ class_name_to_variable_name(class_or_to_s).to_sym
220
+ end
221
+ end
222
+
223
+ def attribute_defined? (name)
224
+ !@attributes.detect {|attr| attr.name == name }.nil?
225
+ end
226
+
227
+ def assert_valid_options(options)
228
+ invalid_keys = options.keys - [:class]
229
+ unless invalid_keys == []
230
+ raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
231
+ end
232
+ end
233
+
234
+ # Based on ActiveSupport's underscore inflector
235
+ def class_name_to_variable_name(name)
236
+ name.to_s.gsub(/::/, '/').
237
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
238
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
239
+ tr("-", "_").
240
+ downcase
241
+ end
242
+
243
+ # Based on ActiveSupport's camelize inflector
244
+ def variable_name_to_class_name(name)
245
+ name.to_s.
246
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.
247
+ gsub(/(?:^|_)(.)/) { $1.upcase }
248
+ end
249
+
250
+ # From ActiveSupport
251
+ def symbolize_keys(hash)
252
+ hash.inject({}) do |options, (key, value)|
253
+ options[(key.to_sym rescue key) || key] = value
254
+ options
255
+ end
256
+ end
257
+
258
+ end