lib/snake-eyes/interface_changes.rb in snake-eyes-0.0.5 vs lib/snake-eyes/interface_changes.rb in snake-eyes-0.0.6

- old
+ new

@@ -1,53 +1,86 @@ module SnakeEyes module InterfaceChanges def params(options = {}) validate_options(options) - untransformed_params = super() + original_params = super() - traverse_positions = [ - untransformed_params + return original_params unless original_params.any? + + # List of subtrees maintained to mark the depth-first traversal's position + # throughout the transformation of the original param's keys, whereby the + # last element is the traversal's current position and backtracking (going + # from child to parent) is achieved by popping off the last element. + + original_params_sub_trees = [ + original_params ] - nested_attributes = nested_attributes_hash(options[:nested_attributes]) + # Convert the relatively flat format used to specify the nested attributes + # (easier for specification) into a series of nested objects (easier for + # look-ups) - nested_attributes_positions = [ - nested_attributes + nested_schema = build_nested_schema(options[:nested_attributes] || {}) + + @previous_params ||= { } + + return @previous_params[nested_schema] if @previous_params[nested_schema] + + # Similar to original_params_sub_trees, a list of subtrees used to maintain + # the traversal position of nested_schema. This is kept in sync with the + # traversal of original_params, to ensure the correct leaf of + # nested_schema is checked at each point in the traversal of original_params + + nested_schema_sub_trees = [ + nested_schema ] - transformed_params = untransformed_params.deep_transform_keys do |key| - underscored_key = key.underscore + transformed_params = original_params.deep_transform_keys do |original_key| + # The matching leaf of nested_schema for this point in the traversal + nested_schema_sub_tree = nested_schema_sub_trees.last - nested_attributes_position = nested_attributes_positions.last + # Append the '_attributes' suffix if the original params key has the + # same name and is nested in the same place as one mentioned in the + # nested_attributes option + transformed_key_base = original_key.underscore + transformed_key = - if nested_attributes_position[underscored_key] - underscored_key + '_attributes' + if nested_schema_sub_tree[transformed_key_base] + transformed_key_base + '_attributes' else - underscored_key + transformed_key_base end - while traverse_positions.length > 1 && traverse_positions.last[key].nil? - traverse_positions.pop - nested_attributes_positions.pop + # Synchronise the original params sub-tree with the current key being + # transformed. We can detect that the sub-tree is stale because the key + # being transformed does not appear amongst its own. When the sub-tree is + # indeed stale, move the position to its parent for the original params + # sub-tree and the nested schema sub-tree and repeat the check. + + while original_params_sub_trees.length > 1 && original_params_sub_trees.last[original_key].nil? + original_params_sub_trees.pop + nested_schema_sub_trees.pop end - current_position = traverse_positions.last[underscored_key] + original_params_sub_tree = original_params_sub_trees.last[transformed_key_base] - if current_position.kind_of?(Hash) - traverse_positions.push(current_position) + if original_params_sub_tree.kind_of?(Hash) + original_params_sub_trees.push(original_params_sub_tree) - nested_attributes_positions.push( - nested_attributes_position[underscored_key] || nested_attributes_position['_' + underscored_key] || {} + nested_schema_sub_trees.push( + nested_schema_sub_tree[transformed_key_base] || + nested_schema_sub_tree['_' + transformed_key_base] || + {} ) end transformed_key end - @snake_eyes_params = ActionController::Parameters.new(transformed_params) + @previous_params[nested_schema] = @snake_eyes_params = ActionController::Parameters.new(transformed_params) log_snakized_params @snake_eyes_params end @@ -60,29 +93,37 @@ end end def log_snakized_params if SnakeEyes.log_snake_eyes_parameters + ignored_params = ActionController::LogSubscriber::INTERNAL_PARAMS filtered_params = request.send(:parameter_filter).filter(@snake_eyes_params.except(*ignored_params)) + logger.info " SnakeEyes Parameters: #{filtered_params.inspect}" end end - def nested_attributes_hash(attributes_list = []) + def build_nested_schema(attributes_list = []) if attributes_list.kind_of?(Array) + attributes_list.inject({}) do |memo, nested_attribute| - memo.merge(nested_attributes_hash(nested_attribute)) + memo.merge(build_nested_schema(nested_attribute)) end + elsif attributes_list.kind_of?(Hash) + attributes_list.inject({}) do |memo, key_and_value| key, value = key_and_value - memo[key.to_s] = nested_attributes_hash(value) + memo[key.to_s] = build_nested_schema(value) memo end + else + { attributes_list.to_s.underscore => {} } + end end end end