diff options
Diffstat (limited to 'guides/source/active_support_core_extensions.md')
-rw-r--r-- | guides/source/active_support_core_extensions.md | 449 |
1 files changed, 259 insertions, 190 deletions
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 1d79ee2565..faad34d021 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -10,7 +10,7 @@ After reading this guide, you will know: * What Core Extensions are. * How to load all extensions. * How to cherry-pick just the extensions you want. -* What extensions ActiveSupport provides. +* What extensions Active Support provides. -------------------------------------------------------------------------------- @@ -37,9 +37,10 @@ For every single method defined as a core extension this guide has a note that s NOTE: Defined in `active_support/core_ext/object/blank.rb`. -That means that this single call is enough: +That means that you can require it like this: ```ruby +require 'active_support' require 'active_support/core_ext/object/blank' ``` @@ -52,6 +53,7 @@ The next level is to simply load all extensions to `Object`. As a rule of thumb, Thus, to load all extensions to `Object` (including `blank?`): ```ruby +require 'active_support' require 'active_support/core_ext/object' ``` @@ -60,6 +62,7 @@ require 'active_support/core_ext/object' You may prefer just to load all core extensions, there is a file for that: ```ruby +require 'active_support' require 'active_support/core_ext' ``` @@ -96,12 +99,13 @@ INFO: The predicate for strings uses the Unicode-aware character class `[:space: WARNING: Note that numbers are not mentioned. In particular, 0 and 0.0 are **not** blank. -For example, this method from `ActionDispatch::Session::AbstractStore` uses `blank?` for checking whether a session key is present: +For example, this method from `ActionController::HttpAuthentication::Token::ControllerMethods` uses `blank?` for checking whether a token is present: ```ruby -def ensure_session_key! - if @key.blank? - raise ArgumentError, 'A key is required...' +def authenticate(controller, &login_procedure) + token, options = token_and_options(controller.request) + unless token.blank? + login_procedure.call(token, options) end end ``` @@ -153,12 +157,12 @@ Active Support provides `duplicable?` to programmatically query an object about ```ruby "foo".duplicable? # => true -"".duplicable? # => true +"".duplicable? # => true 0.0.duplicable? # => false -false.duplicable? # => false +false.duplicable? # => false ``` -By definition all objects are `duplicable?` except `nil`, `false`, `true`, symbols, numbers, class, and module objects. +By definition all objects are `duplicable?` except `nil`, `false`, `true`, symbols, numbers, class, module, and method objects. WARNING: Any class can disallow duplication by removing `dup` and `clone` or raising exceptions from them. Thus only `rescue` can tell whether a given arbitrary object is duplicable. `duplicable?` depends on the hard-coded list above, but it is much faster than `rescue`. Use it only if you know the hard-coded list is enough in your use case. @@ -166,7 +170,7 @@ NOTE: Defined in `active_support/core_ext/object/duplicable.rb`. ### `deep_dup` -The `deep_dup` method returns deep copy of a given object. Normally, when you `dup` an object that contains other objects, ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: +The `deep_dup` method returns deep copy of a given object. Normally, when you `dup` an object that contains other objects, Ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: ```ruby array = ['string'] @@ -175,14 +179,14 @@ duplicate = array.dup duplicate.push 'another-string' # the object was duplicated, so the element was added only to the duplicate -array #=> ['string'] -duplicate #=> ['string', 'another-string'] +array # => ['string'] +duplicate # => ['string', 'another-string'] duplicate.first.gsub!('string', 'foo') # first element was not duplicated, it will be changed in both arrays -array #=> ['foo'] -duplicate #=> ['foo', 'another-string'] +array # => ['foo'] +duplicate # => ['foo', 'another-string'] ``` As you can see, after duplicating the `Array` instance, we got another object, therefore we can modify it and the original object will stay unchanged. This is not true for array's elements, however. Since `dup` does not make deep copy, the string inside the array is still the same object. @@ -195,8 +199,8 @@ duplicate = array.deep_dup duplicate.first.gsub!('string', 'foo') -array #=> ['string'] -duplicate #=> ['foo'] +array # => ['string'] +duplicate # => ['foo'] ``` If the object is not duplicable, `deep_dup` will just return it: @@ -418,13 +422,20 @@ TIP: Since `with_options` forwards calls to its receiver they can be nested. Eac NOTE: Defined in `active_support/core_ext/object/with_options.rb`. +### JSON support + +Active Support provides a better implementation of `to_json` than the `json` gem ordinarily provides for Ruby objects. This is because some classes, like `Hash`, `OrderedHash` and `Process::Status` need special handling in order to provide a proper JSON representation. + +NOTE: Defined in `active_support/core_ext/object/json.rb`. + ### Instance Variables Active Support provides several methods to ease access to instance variables. -#### `instance_variable_names` +#### `instance_values` -Ruby 1.8 and 1.9 have a method called `instance_variables` that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines `instance_variable_names` as a portable way to obtain them as strings: +The method `instance_values` returns a hash that maps instance variable names without "@" to their +corresponding values. Keys are strings: ```ruby class C @@ -433,17 +444,14 @@ class C end end -C.new(0, 1).instance_variable_names # => ["@y", "@x"] +C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} ``` -WARNING: The order in which the names are returned is unspecified, and it indeed depends on the version of the interpreter. - NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`. -#### `instance_values` +#### `instance_variable_names` -The method `instance_values` returns a hash that maps instance variable names without "@" to their -corresponding values. Keys are strings: +The method `instance_variable_names` returns an array. Each name includes the "@" sign. ```ruby class C @@ -452,7 +460,7 @@ class C end end -C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} +C.new(0, 1).instance_variable_names # => ["@x", "@y"] ``` NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`. @@ -494,12 +502,11 @@ NOTE: Defined in `active_support/core_ext/kernel/reporting.rb`. ### `in?` -The predicate `in?` tests if an object is included in another object or a list of objects. An `ArgumentError` exception will be raised if a single argument is passed and it does not respond to `include?`. +The predicate `in?` tests if an object is included in another object. An `ArgumentError` exception will be raised if the argument passed does not respond to `include?`. Examples of `in?`: ```ruby -1.in?(1,2) # => true 1.in?([1,2]) # => true "lo".in?("hello") # => true 25.in?(30..50) # => false @@ -565,12 +572,12 @@ NOTE: Defined in `active_support/core_ext/module/aliasing.rb`. #### `alias_attribute` -Model attributes have a reader, a writer, and a predicate. You can alias a model attribute having the corresponding three methods defined for you in one shot. As in other aliasing methods, the new name is the first argument, and the old name is the second (my mnemonic is they go in the same order as if you did an assignment): +Model attributes have a reader, a writer, and a predicate. You can alias a model attribute having the corresponding three methods defined for you in one shot. As in other aliasing methods, the new name is the first argument, and the old name is the second (one mnemonic is that they go in the same order as if you did an assignment): ```ruby class User < ActiveRecord::Base - # let me refer to the email column as "login", - # possibly meaningful for authentication code + # You can refer to the email column as "login". + # This can be meaningful for authentication code. alias_attribute :login, :email end ``` @@ -617,7 +624,7 @@ NOTE: Defined in `active_support/core_ext/module/attr_internal.rb`. #### Module Attributes -The macros `mattr_reader`, `mattr_writer`, and `mattr_accessor` are analogous to the `cattr_*` macros defined for class. Check [Class Attributes](#class-attributes). +The macros `mattr_reader`, `mattr_writer`, and `mattr_accessor` are the same as the `cattr_*` macros defined for class. In fact, the `cattr_*` macros are just aliases for the `mattr_*` macros. Check [Class Attributes](#class-attributes). For example, the dependencies mechanism uses them: @@ -728,7 +735,7 @@ X.local_constants # => [:X1, :X2, :Y] X::Y.local_constants # => [:Y1, :X1] ``` -The names are returned as symbols. (The deprecated method `local_constant_names` returns strings.) +The names are returned as symbols. NOTE: Defined in `active_support/core_ext/module/introspection.rb`. @@ -754,7 +761,7 @@ Arguments may be bare constant names: Math.qualified_const_get("E") # => 2.718281828459045 ``` -These methods are analogous to their builtin counterparts. In particular, +These methods are analogous to their built-in counterparts. In particular, `qualified_constant_defined?` accepts an optional second argument to be able to say whether you want the predicate to look in the ancestors. This flag is taken into account for each constant in the expression while @@ -785,7 +792,7 @@ N.qualified_const_defined?("C::X") # => true As the last example implies, the second argument defaults to true, as in `const_defined?`. -For coherence with the builtin methods only relative paths are accepted. +For coherence with the built-in methods only relative paths are accepted. Absolute qualified constant names like `::Math::PI` raise `NameError`. NOTE: Defined in `active_support/core_ext/module/qualified_const.rb`. @@ -881,7 +888,7 @@ class User < ActiveRecord::Base end ``` -With that configuration you get a user's name via his profile, `user.profile.name`, but it could be handy to still be able to access such attribute directly: +With that configuration you get a user's name via their profile, `user.profile.name`, but it could be handy to still be able to access such attribute directly: ```ruby class User < ActiveRecord::Base @@ -920,7 +927,7 @@ When interpolated into a string, the `:to` option should become an expression th delegate :logger, to: :Rails # delegates to the receiver's class -delegate :table_name, to: 'self.class' +delegate :table_name, to: :class ``` WARNING: If the `:prefix` option is `true` this is less generic, see below. @@ -957,20 +964,7 @@ NOTE: Defined in `active_support/core_ext/module/delegation.rb` There are cases where you need to define a method with `define_method`, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either. -The method `redefine_method` prevents such a potential warning, removing the existing method before if needed. Rails uses it in a few places, for instance when it generates an association's API: - -```ruby -redefine_method("#{reflection.name}=") do |new_value| - association = association_instance_get(reflection.name) - - if association.nil? || association.target != new_value - association = association_proxy_class.new(self, reflection) - end - - association.replace(new_value) - association_instance_set(reflection.name, new_value.nil? ? nil : association) -end -``` +The method `redefine_method` prevents such a potential warning, removing the existing method before if needed. NOTE: Defined in `active_support/core_ext/module/remove_method.rb` @@ -1017,7 +1011,7 @@ self.default_params = { }.freeze ``` -They can be also accessed and overridden at the instance level. +They can also be accessed and overridden at the instance level. ```ruby A.x = 1 @@ -1057,6 +1051,8 @@ For convenience `class_attribute` also defines an instance predicate which is th When `:instance_reader` is `false`, the instance predicate returns a `NoMethodError` just like the reader method. +If you do not want the instance predicate, pass `instance_predicate: false` and it will not be defined. + NOTE: Defined in `active_support/core_ext/class/attribute.rb` #### `cattr_reader`, `cattr_writer`, and `cattr_accessor` @@ -1084,6 +1080,15 @@ end we can access `field_error_proc` in views. +Also, you can pass a block to `cattr_*` to set up the attribute with a default value: + +```ruby +class MysqlAdapter < AbstractAdapter + # Generates class methods to access @@emulate_booleans with default value of true. + cattr_accessor(:emulate_booleans) { true } +end +``` + The generation of the reader instance method can be prevented by setting `:instance_reader` to `false` and the generation of the writer instance method can be prevented by setting `:instance_writer` to `false`. Generation of both methods can be prevented by setting `:instance_accessor` to `false`. In all cases, the value must be exactly `false` and not any false value. ```ruby @@ -1101,7 +1106,7 @@ end A model may find it useful to set `:instance_accessor` to `false` as a way to prevent mass-assignment from setting the attribute. -NOTE: Defined in `active_support/core_ext/class/attribute_accessors.rb`. +NOTE: Defined in `active_support/core_ext/module/attribute_accessors.rb`. ### Subclasses & Descendants @@ -1160,9 +1165,9 @@ Inserting data into HTML templates needs extra care. For example, you can't just #### Safe Strings -Active Support has the concept of <i>(html) safe</i> strings. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. +Active Support has the concept of _(html) safe_ strings. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. -Strings are considered to be <i>unsafe</i> by default: +Strings are considered to be _unsafe_ by default: ```ruby "".html_safe? # => false @@ -1241,6 +1246,18 @@ Calling `to_s` on a safe string returns a safe string, but coercion with `to_str Calling `dup` or `clone` on safe strings yields safe strings. +### `remove` + +The method `remove` will remove all occurrences of the pattern: + +```ruby +"Hello World".remove(/Hello /) => "World" +``` + +There's also the destructive version `String#remove!`. + +NOTE: Defined in `active_support/core_ext/string/filters.rb`. + ### `squish` The method `squish` strips leading and trailing whitespace, and substitutes runs of whitespace with a single space each: @@ -1251,6 +1268,8 @@ The method `squish` strips leading and trailing whitespace, and substitutes runs There's also the destructive version `String#squish!`. +Note that it handles both ASCII and Unicode whitespace. + NOTE: Defined in `active_support/core_ext/string/filters.rb`. ### `truncate` @@ -1291,6 +1310,38 @@ In above examples "dear" gets cut first, but then `:separator` prevents it. NOTE: Defined in `active_support/core_ext/string/filters.rb`. +### `truncate_words` + +The method `truncate_words` returns a copy of its receiver truncated after a given number of words: + +```ruby +"Oh dear! Oh dear! I shall be late!".truncate_words(4) +# => "Oh dear! Oh dear!..." +``` + +Ellipsis can be customized with the `:omission` option: + +```ruby +"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '…') +# => "Oh dear! Oh dear!…" +``` + +Pass a `:separator` to truncate the string at a natural break: + +```ruby +"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!') +# => "Oh dear! Oh dear! I shall be late..." +``` + +The option `:separator` can be a regexp: + +```ruby +"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/) +# => "Oh dear! Oh dear!..." +``` + +NOTE: Defined in `active_support/core_ext/string/filters.rb`. + ### `inquiry` The `inquiry` method converts a string into a `StringInquirer` object making equality checks prettier. @@ -1360,7 +1411,7 @@ The second argument, `indent_string`, specifies which indent string to use. The "foo".indent(2, "\t") # => "\t\tfoo" ``` -While `indent_string` is tipically one space or tab, it may be any string. +While `indent_string` is typically one space or tab, it may be any string. The third argument, `indent_empty_lines`, is a flag that says whether empty lines should be indented. Default is false. @@ -1371,6 +1422,8 @@ The third argument, `indent_empty_lines`, is a flag that says whether empty line The `indent!` method performs indentation in-place. +NOTE: Defined in `active_support/core_ext/string/indent.rb`. + ### Access #### `at(position)` @@ -1394,7 +1447,7 @@ Returns the substring of the string starting at position `position`: "hello".from(0) # => "hello" "hello".from(2) # => "llo" "hello".from(-2) # => "lo" -"hello".from(10) # => "" if < 1.9, nil in 1.9 +"hello".from(10) # => nil ``` NOTE: Defined in `active_support/core_ext/string/access.rb`. @@ -1438,7 +1491,7 @@ The method `pluralize` returns the plural of its receiver: As the previous example shows, Active Support knows some irregular plurals and uncountable nouns. Built-in rules can be extended in `config/initializers/inflections.rb`. That file is generated by the `rails` command and has instructions in comments. -`pluralize` can also take an optional `count` parameter. If `count == 1` the singular form will be returned. For any other value of `count` the plural form will be returned: +`pluralize` can also take an optional `count` parameter. If `count == 1` the singular form will be returned. For any other value of `count` the plural form will be returned: ```ruby "dude".pluralize(0) # => "dudes" @@ -1449,11 +1502,10 @@ As the previous example shows, Active Support knows some irregular plurals and u Active Record uses this method to compute the default table name that corresponds to a model: ```ruby -# active_record/base.rb +# active_record/model_schema.rb def undecorated_table_name(class_name = base_class.name) table_name = class_name.to_s.demodulize.underscore - table_name = table_name.pluralize if pluralize_table_names - table_name + pluralize_table_names ? table_name.pluralize : table_name end ``` @@ -1523,7 +1575,7 @@ ActiveSupport::Inflector.inflections do |inflect| inflect.acronym 'SSL' end -"SSLError".underscore.camelize #=> "SSLError" +"SSLError".underscore.camelize # => "SSLError" ``` `camelize` is aliased to `camelcase`. @@ -1611,6 +1663,9 @@ Given a string with a qualified constant name, `demodulize` returns the very con "Product".demodulize # => "Product" "Backoffice::UsersController".demodulize # => "UsersController" "Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils" +"::Inflections".demodulize # => "Inflections" +"".demodulize # => "" + ``` Active Record for example uses this method to compute the name of a counter cache column: @@ -1745,28 +1800,47 @@ NOTE: Defined in `active_support/core_ext/string/inflections.rb`. #### `humanize` -The method `humanize` gives you a sensible name for display out of an attribute name. To do so it replaces underscores with spaces, removes any "_id" suffix, and capitalizes the first word: +The method `humanize` tweaks an attribute name for display to end users. + +Specifically performs these transformations: + + * Applies human inflection rules to the argument. + * Deletes leading underscores, if any. + * Removes a "_id" suffix if present. + * Replaces underscores with spaces, if any. + * Downcases all words except acronyms. + * Capitalizes the first word. + +The capitalization of the first word can be turned off by setting the ++:capitalize+ option to false (default is true). ```ruby -"name".humanize # => "Name" -"author_id".humanize # => "Author" -"comments_count".humanize # => "Comments count" +"name".humanize # => "Name" +"author_id".humanize # => "Author" +"author_id".humanize(capitalize: false) # => "author" +"comments_count".humanize # => "Comments count" +"_id".humanize # => "Id" ``` -The helper method `full_messages` uses `humanize` as a fallback to include attribute names: +If "SSL" was defined to be an acronym: ```ruby -def full_messages - full_messages = [] +'ssl_error'.humanize # => "SSL error" +``` - each do |attribute, messages| - ... - attr_name = attribute.to_s.gsub('.', '_').humanize - attr_name = @base.class.human_attribute_name(attribute, default: attr_name) - ... - end +The helper method `full_messages` uses `humanize` as a fallback to include +attribute names: - full_messages +```ruby +def full_messages + map { |attribute, message| full_message(attribute, message) } +end + +def full_message + ... + attr_name = attribute.to_s.tr('.', '_').humanize + attr_name = @base.class.human_attribute_name(attribute, default: attr_name) + ... end ``` @@ -1876,24 +1950,6 @@ as well as adding or subtracting their results from a Time object. For example: (4.months + 5.years).from_now ``` -While these methods provide precise calculation when used as in the examples above, care -should be taken to note that this is not true if the result of `months', `years', etc is -converted before use: - -```ruby -# equivalent to 30.days.to_i.from_now -1.month.to_i.from_now - -# equivalent to 365.25.days.to_f.from_now -1.year.to_f.from_now -``` - -In such cases, Ruby's core [Date](http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html) and -[Time](http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html) should be used for precision -date and time arithmetic. - -NOTE: Defined in `active_support/core_ext/numeric/time.rb`. - ### Formatting Enables the formatting of numbers in a variety of ways. @@ -1979,7 +2035,7 @@ Produce a string representation of a number in human-readable words: 1234567890123456.to_s(:human) # => "1.23 Quadrillion" ``` -NOTE: Defined in `active_support/core_ext/numeric/formatting.rb`. +NOTE: Defined in `active_support/core_ext/numeric/conversions.rb`. Extensions to `Integer` ----------------------- @@ -2027,8 +2083,33 @@ NOTE: Defined in `active_support/core_ext/integer/inflections.rb`. Extensions to `BigDecimal` -------------------------- +### `to_s` -... +The method `to_s` is aliased to `to_formatted_s`. This provides a convenient way to display a BigDecimal value in floating-point notation: + +```ruby +BigDecimal.new(5.00, 6).to_s # => "5.0" +``` + +### `to_formatted_s` + +Te method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s # => "5.0" +``` + +and that symbol specifiers are also supported: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s(:db) # => "5.0" +``` + +Engineering notation is still supported: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s("e") # => "0.5E1" +``` Extensions to `Enumerable` -------------------------- @@ -2215,7 +2296,7 @@ This method accepts three options: * `:words_connector`: What is used to join the elements of arrays with 3 or more elements, except for the last two. Default is ", ". * `:last_word_connector`: What is used to join the last items of an array with 3 or more elements. Default is ", and ". -The defaults for these options can be localised, their keys are: +The defaults for these options can be localized, their keys are: | Option | I18n key | | ---------------------- | ----------------------------------- | @@ -2223,15 +2304,15 @@ The defaults for these options can be localised, their keys are: | `:words_connector` | `support.array.words_connector` | | `:last_word_connector` | `support.array.last_word_connector` | -Options `:connector` and `:skip_last_comma` are deprecated. - NOTE: Defined in `active_support/core_ext/array/conversions.rb`. #### `to_formatted_s` The method `to_formatted_s` acts like `to_s` by default. -If the array contains items that respond to `id`, however, it may be passed the symbol `:db` as argument. That's typically used with collections of ARs. Returned strings are: +If the array contains items that respond to `id`, however, the symbol +`:db` may be passed as argument. That's typically used with +collections of Active Record objects. Returned strings are: ```ruby [].to_formatted_s(:db) # => "null" @@ -2387,7 +2468,8 @@ NOTE: Defined in `active_support/core_ext/array/wrap.rb`. ### Duplicating -The method `Array.deep_dup` duplicates itself and all objects inside recursively with ActiveSupport method `Object#deep_dup`. It works like `Array#map` with sending `deep_dup` method to each object inside. +The method `Array.deep_dup` duplicates itself and all objects inside +recursively with Active Support method `Object#deep_dup`. It works like `Array#map` with sending `deep_dup` method to each object inside. ```ruby array = [1, [2, 3]] @@ -2396,7 +2478,7 @@ dup[1][2] = 4 array[1][2] == nil # => true ``` -NOTE: Defined in `active_support/core_ext/array/deep_dup.rb`. +NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`. ### Grouping @@ -2413,9 +2495,9 @@ or yields them in turn if a block is passed: ```html+erb <% sample.in_groups_of(3) do |a, b, c| %> <tr> - <td><%=h a %></td> - <td><%=h b %></td> - <td><%=h c %></td> + <td><%= a %></td> + <td><%= b %></td> + <td><%= c %></td> </tr> <% end %> ``` @@ -2608,7 +2690,8 @@ NOTE: Defined in `active_support/core_ext/hash/deep_merge.rb`. ### Deep duplicating -The method `Hash.deep_dup` duplicates itself and all keys and values inside recursively with ActiveSupport method `Object#deep_dup`. It works like `Enumerator#each_with_object` with sending `deep_dup` method to each pair inside. +The method `Hash.deep_dup` duplicates itself and all keys and values +inside recursively with Active Support method `Object#deep_dup`. It works like `Enumerator#each_with_object` with sending `deep_dup` method to each pair inside. ```ruby hash = { a: 1, b: { c: 2, d: [3, 4] } } @@ -2621,45 +2704,7 @@ hash[:b][:e] == nil # => true hash[:b][:d] == [3, 4] # => true ``` -NOTE: Defined in `active_support/core_ext/hash/deep_dup.rb`. - -### Diffing - -The method `diff` returns a hash that represents a diff of the receiver and the argument with the following logic: - -* Pairs `key`, `value` that exist in both hashes do not belong to the diff hash. - -* If both hashes have `key`, but with different values, the pair in the receiver wins. - -* The rest is just merged. - -```ruby -{a: 1}.diff(a: 1) -# => {}, first rule - -{a: 1}.diff(a: 2) -# => {:a=>1}, second rule - -{a: 1}.diff(b: 2) -# => {:a=>1, :b=>2}, third rule - -{a: 1, b: 2, c: 3}.diff(b: 1, c: 3, d: 4) -# => {:a=>1, :b=>2, :d=>4}, all rules - -{}.diff({}) # => {} -{a: 1}.diff({}) # => {:a=>1} -{}.diff(a: 1) # => {:a=>1} -``` - -An important property of this diff hash is that you can retrieve the original hash by applying `diff` twice: - -```ruby -hash.diff(hash2).diff(hash2) == hash -``` - -Diffing hashes may be useful for error messages related to expected option hashes for example. - -NOTE: Defined in `active_support/core_ext/hash/diff.rb`. +NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`. ### Working with Keys @@ -2678,13 +2723,6 @@ If the receiver responds to `convert_key`, the method is called on each of the a {a: 1}.with_indifferent_access.except("a") # => {} ``` -The method `except` may come in handy for example when you want to protect some parameter that can't be globally protected with `attr_protected`: - -```ruby -params[:account] = params[:account].except(:plan_id) unless admin? -@account.update_attributes(params[:account]) -``` - There's also the bang variant `except!` that removes keys in the very receiver. NOTE: Defined in `active_support/core_ext/hash/except.rb`. @@ -2694,26 +2732,29 @@ NOTE: Defined in `active_support/core_ext/hash/except.rb`. The method `transform_keys` accepts a block and returns a hash that has applied the block operations to each of the keys in the receiver: ```ruby -{nil => nil, 1 => 1, a: :a}.transform_keys{ |key| key.to_s.upcase } +{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase } # => {"" => nil, "A" => :a, "1" => 1} ``` -The result in case of collision is undefined: +In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: ```ruby -{"a" => 1, a: 2}.transform_keys{ |key| key.to_s.upcase } -# => {"A" => 2}, in my test, can't rely on this result though +{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase } +# The result could either be +# => {"A"=>2} +# or +# => {"A"=>1} ``` This method may be useful for example to build specialized conversions. For instance `stringify_keys` and `symbolize_keys` use `transform_keys` to perform their key conversions: ```ruby def stringify_keys - transform_keys{ |key| key.to_s } + transform_keys { |key| key.to_s } end ... def symbolize_keys - transform_keys{ |key| key.to_sym rescue key } + transform_keys { |key| key.to_sym rescue key } end ``` @@ -2722,7 +2763,7 @@ There's also the bang variant `transform_keys!` that applies the block operation Besides that, one can use `deep_transform_keys` and `deep_transform_keys!` to perform the block operation on all the keys in the given hash and all the hashes nested into it. An example of the result is: ```ruby -{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys{ |key| key.to_s.upcase } +{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys { |key| key.to_s.upcase } # => {""=>nil, "1"=>1, "NESTED"=>{"A"=>3, "5"=>5}} ``` @@ -2737,11 +2778,14 @@ The method `stringify_keys` returns a hash that has a stringified version of the # => {"" => nil, "a" => :a, "1" => 1} ``` -The result in case of collision is undefined: +In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: ```ruby {"a" => 1, a: 2}.stringify_keys -# => {"a" => 2}, in my test, can't rely on this result though +# The result could either be +# => {"a"=>2} +# or +# => {"a"=>1} ``` This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionView::Helpers::FormHelper` defines: @@ -2778,11 +2822,14 @@ The method `symbolize_keys` returns a hash that has a symbolized version of the WARNING. Note in the previous example only one key was symbolized. -The result in case of collision is undefined: +In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: ```ruby {"a" => 1, a: 2}.symbolize_keys -# => {:a=>2}, in my test, can't rely on this result though +# The result could either be +# => {:a=>2} +# or +# => {:a=>1} ``` This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionController::UrlRewriter` defines @@ -2827,6 +2874,20 @@ Active Record does not accept unknown options when building associations, for ex NOTE: Defined in `active_support/core_ext/hash/keys.rb`. +### Working with Values + +#### `transform_values` && `transform_values!` + +The method `transform_values` accepts a block and returns a hash that has applied the block operations to each of the values in the receiver. + +```ruby +{ nil => nil, 1 => 1, :x => :a }.transform_values { |value| value.to_s.upcase } +# => {nil=>"", 1=>"1", :x=>"A"} +``` +There's also the bang variant `transform_values!` that applies the block operations to values in the very receiver. + +NOTE: Defined in `active_support/core_text/hash/transform_values.rb`. + ### Slicing Ruby has built-in support for taking slices out of strings and arrays. Active Support extends slicing to hashes: @@ -2888,6 +2949,16 @@ The method `with_indifferent_access` returns an `ActiveSupport::HashWithIndiffer NOTE: Defined in `active_support/core_ext/hash/indifferent_access.rb`. +### Compacting + +The methods `compact` and `compact!` return a Hash without items with `nil` value. + +```ruby +{a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2} +``` + +NOTE: Defined in `active_support/core_ext/hash/compact.rb`. + Extensions to `Regexp` ---------------------- @@ -3344,7 +3415,25 @@ date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010 `beginning_of_hour` is aliased to `at_beginning_of_hour`. -INFO: `beginning_of_hour` and `end_of_hour` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour on a `Date` instance. +##### `beginning_of_minute`, `end_of_minute` + +The method `beginning_of_minute` returns a timestamp at the beginning of the minute (hh:mm:00): + +```ruby +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010 +``` + +The method `end_of_minute` returns a timestamp at the end of the minute (hh:mm:59): + +```ruby +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010 +``` + +`beginning_of_minute` is aliased to `at_beginning_of_minute`. + +INFO: `beginning_of_hour`, `end_of_hour`, `beginning_of_minute` and `end_of_minute` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour or minute on a `Date` instance. ##### `ago`, `since` @@ -3599,7 +3688,7 @@ Time.zone_default # => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> # In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST. -t = Time.local_time(2010, 3, 28, 1, 59, 59) +t = Time.local(2010, 3, 28, 1, 59, 59) # => Sun Mar 28 01:59:59 +0100 2010 t.advance(seconds: 1) # => Sun Mar 28 03:00:00 +0200 2010 @@ -3609,9 +3698,9 @@ t.advance(seconds: 1) #### `Time.current` -Active Support defines `Time.current` to be today in the current time zone. That's like `Time.now`, except that it honors the user time zone, if defined. It also defines `Time.yesterday` and `Time.tomorrow`, and the instance predicates `past?`, `today?`, and `future?`, all of them relative to `Time.current`. +Active Support defines `Time.current` to be today in the current time zone. That's like `Time.now`, except that it honors the user time zone, if defined. It also defines the instance predicates `past?`, `today?`, and `future?`, all of them relative to `Time.current`. -When making Time comparisons using methods which honor the user time zone, make sure to use `Time.current` and not `Time.now`. There are cases where the user time zone might be in the future compared to the system time zone, which `Time.today` uses by default. This means `Time.now` may equal `Time.yesterday`. +When making Time comparisons using methods which honor the user time zone, make sure to use `Time.current` instead of `Time.now`. There are cases where the user time zone might be in the future compared to the system time zone, which `Time.now` uses by default. This means `Time.now.to_date` may equal `Date.yesterday`. #### `all_day`, `all_week`, `all_month`, `all_quarter` and `all_year` @@ -3654,26 +3743,6 @@ Time.current Analogously to `DateTime`, the predicates `past?`, and `future?` are relative to `Time.current`. -Use the `local_time` class method to create time objects honoring the user time zone: - -```ruby -Time.zone_default -# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> -Time.local_time(2010, 8, 15) -# => Sun Aug 15 00:00:00 +0200 2010 -``` - -The `utc_time` class method returns a time in UTC: - -```ruby -Time.zone_default -# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> -Time.utc_time(2010, 8, 15) -# => Sun Aug 15 00:00:00 UTC 2010 -``` - -Both `local_time` and `utc_time` accept up to seven positional arguments: year, month, day, hour, min, sec, usec. Year is mandatory, month and day default to 1, and the rest default to 0. - If the time to be constructed lies beyond the range supported by `Time` in the runtime platform, usecs are discarded and a `DateTime` object is returned instead. #### Durations @@ -3692,7 +3761,7 @@ now - 1.week They translate to calls to `since` or `advance`. For example here we get the correct jump in the calendar reform: ```ruby -Time.utc_time(1582, 10, 3) + 5.days +Time.utc(1582, 10, 3) + 5.days # => Mon Oct 18 00:00:00 UTC 1582 ``` @@ -3795,7 +3864,7 @@ The name may be given as a symbol or string. A symbol is tested against the bare TIP: A symbol can represent a fully-qualified constant name as in `:"ActiveRecord::Base"`, so the behavior for symbols is defined for convenience, not because it has to be that way technically. -For example, when an action of `PostsController` is called Rails tries optimistically to use `PostsHelper`. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that `posts_helper.rb` raises a `NameError` due to an actual unknown constant. That should be reraised. The method `missing_name?` provides a way to distinguish both cases: +For example, when an action of `ArticlesController` is called Rails tries optimistically to use `ArticlesHelper`. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that `articles_helper.rb` raises a `NameError` due to an actual unknown constant. That should be reraised. The method `missing_name?` provides a way to distinguish both cases: ```ruby def default_helper_module! @@ -3803,7 +3872,7 @@ def default_helper_module! module_path = module_name.underscore helper module_path rescue MissingSourceFile => e - raise e unless e.is_missing? "#{module_path}_helper" + raise e unless e.is_missing? "helpers/#{module_path}_helper" rescue NameError => e raise e unless e.missing_name? "#{module_name}Helper" end @@ -3818,7 +3887,7 @@ Active Support adds `is_missing?` to `LoadError`, and also assigns that class to Given a path name `is_missing?` tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension). -For example, when an action of `PostsController` is called Rails tries to load `posts_helper.rb`, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method `is_missing?` provides a way to distinguish both cases: +For example, when an action of `ArticlesController` is called Rails tries to load `articles_helper.rb`, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method `is_missing?` provides a way to distinguish both cases: ```ruby def default_helper_module! |