Skip to content

Commit

Permalink
Merge pull request #72 from hanami/form-values-to-support-hash-dig
Browse files Browse the repository at this point in the history
Form values to support `Hash#dig`
  • Loading branch information
jodosha committed Aug 12, 2016
2 parents ba2e2d7 + 5ca15ce commit 1cc6324
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ Metrics/LineLength:
Enabled: false
Style/AndOr:
Enabled: false
Style/MethodMissing:
Enabled: false
43 changes: 42 additions & 1 deletion lib/hanami/helpers/form_helper/values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,52 @@ def initialize(values, params)
# @since 0.2.0
# @api private
def get(key)
@params.get(key) || _get_from_values(key)
_get_from_params(key) || _get_from_values(key)
end

private

# Safely access nested values.
#
# <tt>Hanami::Action::Params</tt> already supports this feature with
# <tt>#get</tt>.
# But during testing phase it could happen to receive a <tt>Hash</tt>
# instead.
#
# For this purpose, we check if <tt>@params</tt> respond to
# <tt>#dig</tt>, which is a new Ruby 2.3 feature similar to
# <tt>Hanami::Action::Params#get</tt>.
# If the check is successful, we try to access the value with
# <tt>#dig</tt>, otherwise we assume that it responds to <tt>#get</tt>
# and use it.
#
# This implementation is safe to use, but it has several hidden perf costs:
#
# * The runtime check for <tt>#respond_to?</tt>, which is inelegant too
# * In case of <tt>#dig</tt> we need to transform a key like
# <tt>:'order.customer.address.street'</tt> into
# <tt>[:order, :customer, :address, :street]</tt>
# * In case of <tt>#get</tt> its internal implementation isn't
# efficient because it splits the key into an array and it uses
# recursion to access nested values
#
# Because as of Ruby 2.3 we have <tt>Hash#dig</tt>, we should use it
# both in params and here, because it's faster (written in C).
# To make this possible, we should change the key notation from a dot
# separated string to an array of symbol.
#
# FIXME: Use Hash#dig when we'll support only Ruby 2.3+
#
# @since x.x.x
# @api private
def _get_from_params(key)
if @params.respond_to?(:dig)
@params.dig(*key.to_s.split(GET_SEPARATOR).map!(&:to_sym))
else
@params.get(key)
end
end

# @since 0.2.0
# @api private
def _get_from_values(key)
Expand Down
16 changes: 15 additions & 1 deletion test/fixtures.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,21 @@ class FormHelperView
attr_reader :params

def initialize(params)
@params = Hanami::Action::BaseParams.new(params)
@params = _build_params(params)
end

private

def _build_params(params)
parameters = params.to_h

# Randomly use Hanami::Action::BaseParams or the given raw Hash in order to
# simulate Hash usage during the test setup unit tests in Hanami projects.
if parameters.respond_to?(:dig)
[true, false].sample ? Hanami::Action::BaseParams.new(parameters) : parameters
else
Hanami::Action::BaseParams.new(parameters)
end
end
end

Expand Down

0 comments on commit 1cc6324

Please sign in to comment.