Skip to content

Instantly share code, notes, and snippets.

@vmakoed
Last active March 26, 2018 15:11
Show Gist options
  • Save vmakoed/7eadd4eb4c81e6b99b145f3f0c50eebe to your computer and use it in GitHub Desktop.
Save vmakoed/7eadd4eb4c81e6b99b145f3f0c50eebe to your computer and use it in GitHub Desktop.
Strong Parameters Generator
#TODO: delete after protected attributes migration to strong params is complete
module StrongParams
module Tools
# use #model_params method from this module to get a nice hash of params ready to be
# injected into a params filter of a Rails controller.
# Works with nested_attributes. Takes reflections into account.
module Generator
module_function
#
# Example:
#
# model_params(User)
# => [ :email, :password, address: [ :street, :zipcode ] ]
#
def model_params(model)
decorated_params(model_attributes(model) + nested_params(model))
end
def decorated_params(params)
params.map(&method(:decorate_param))
end
def decorate_param(param)
return { param => [] } if is_array_param?(param)
return { param.key => decorated_params(param.value) } if is_nested_param?(param)
param
end
def is_array_param?(param)
param.to_s.end_with?('_ids')
end
def is_nested_param?(param)
param.is_a?(Array) && param.present?
end
def nested_params(model)
model.nested_attributes_options.keys.reduce([]) do |attributes, attribute_key|
nested_model = attribute_key.to_s.classify.safe_constantize
nested_model ||= model.reflections[attribute_key.to_s].options[:class_name].try(:constantize)
next attributes unless nested_model
attributes << { "#{attribute_key}_attributes".to_sym => model_params(nested_model) }
end
end
def model_attributes(model)
(model.accessible_attributes - nested_keys(model)).map(&:to_sym)
end
def nested_keys(model)
model.nested_attributes_options.keys.map { |key| "#{key}_attributes" }
end
def print_attributes(attributes)
array_attributes = attributes.select { |attr| attr.to_s.include?('_ids') }
(attributes - array_attributes).each { |attr| puts ":#{attr}," }
array_attributes.each { |attr| puts "#{attr}: []," }
end
end
end
end
#TODO: delete after protected attributes migration to strong params is complete
module StrongParams
module Tools
module Printer
# use #print_methods and #print_for_controllers methods from this module.
# Depends on StrongParams::Tools::Generator.
# See detailed description below.
# Defines a path to a folder with strong params. Used when you generate params as a module.
PATH_TO_FILE = 'app/params_whitelists/strong_params'.freeze
module_function
#
# Prints into console strong params filtering method withoud indentation.
#
# Example:
#
# print_methods(User)
#
# => def user_params
# params.require(:user).permit([
# :email,
# :password,
# address: [
# :street,
# :zipcode
# ]
# )]
# end
#
def print_methods(model)
Array.wrap(model).each(&method(:print_method))
end
#
# For ALL controllers that have corresponding models prints strong params method
# to the end of controller file without indentation.
#
def print_for_controllers
Rails.application.eager_load!
ApplicationController.descendants.map(&:name).each do |controller|
next if controller.include?('Api')
model = controller.gsub('Controller', '').singularize.safe_constantize
next unless (model && (model < ApplicationRecord))
controller_path = "#{Rails.root}/app/controllers/#{controller.underscore}.rb"
print_to_controller(model, controller_path)
end
$stdout = old_stdout
end
#
# Prints into console strong params helper module without indentation.
#
# Example:
#
# print_module(User)
#
# => module StrongParams
# module UserParams
# PARAMS = [
# :email,
# :password,
# address: [
# :street,
# :zipcode
# ]
# )]
# end
# end
#
def print_module(model, submodule = nil)
puts 'module StrongParams'
puts "module #{submodule}" if submodule
puts "module #{model}Params"
print_params_array(model, 'PARAMS')
puts 'end'
puts 'end' if submodule
puts 'end'
end
#
# Prints into file strong params helper module without indentation. Accepts singular classes and arrays.
#
def print_module_to_file(models = [], submodule = nil)
old_stdout = $stdout
Array.wrap(models).each do |model|
File.open("#{path_to_file(submodule)}#{model.name.underscore}_params.rb", 'w') do |output|
$stdout = output
print_module(model, submodule)
end
end
$stdout = old_stdout
end
def print_to_controller(model, controller_path)
old_stdout = $stdout
File.open(controller_path, 'r+') do |output|
last_line = 0
output.each {last_line = output.pos unless output.eof?}
output.seek(last_line, IO::SEEK_SET)
$stdout = output
print_method(model)
end
$stdout = old_stdout
end
def print_method(model)
puts "def #{model.name.underscore}_params"
print_params_array(model)
puts 'end'
puts
end
def print_constants(models)
models.map(&:name).sort.each { |model| puts "#{model.underscore.upcase}_PARAMS" }
end
def print_methods_to_file(models = [], submodule = nil)
old_stdout = $stdout
File.open(path_to_file(submodule), 'w') do |output|
$stdout = output
print_methods(models)
end
$stdout = old_stdout
end
def path_to_file(submodule)
submodule_path(submodule).tap { |full_path| Dir.mkdir(full_path) unless Dir.exist?(full_path) }
end
def submodule_path(submodule)
"#{PATH_TO_FILE}/#{submodule ? "#{submodule.underscore}/" : ''}"
end
def print_params(model)
Array.wrap(model).each(&method(:print_params_array))
end
def print_params_array(model, variable = nil)
puts variable ? "#{variable} = [" : '['
print_generated_params(model)
puts ']'
end
def print_generated_params(model)
StrongParams::Tools::Generator.model_params(model).tap do |params|
print_params_hash(params)
end
end
def print_params_hash(params)
plain_params = params.select { |param| param.is_a? Symbol }
plain_params.each(&method(:print_plain_param))
(params.without(*plain_params)).each { |param| print_nested_param(param) }
end
def print_plain_param(param)
puts ":#{param},"
end
def print_nested_param(param)
param.values.flatten.present? ? print_hash(param) : print_array(param)
end
def print_hash(param)
puts "#{param.keys.first}: ["
print_params_hash(param.values.first)
puts '],'
end
def print_array(param)
puts "#{param.keys.first}: [],"
end
end
end
end
@vmakoed
Copy link
Author

vmakoed commented Mar 26, 2018

This code may be helpful when migrating from Protected Attributes to Strong Parameters (one of the common problems when migrating from Rails 3 to 4).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment