Skip to content

Commit

Permalink
Refactor of the coercion and validation logic (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
ifellinaholeonce committed Apr 30, 2021
1 parent 8235f1b commit 7ef3018
Show file tree
Hide file tree
Showing 57 changed files with 1,818 additions and 238 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ README.html
.bundle
Gemfile.lock
*.gem
.idea
.idea
tmp/
coverage/
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.3.1
2.7.1
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ source "https://rubygems.org"
gemspec

gem 'pry'
gem 'simplecov', require: false, group: :test

platforms :rbx do
gem 'racc'
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ By declaring parameter types, incoming parameters will automatically be transfor

### Validations

Encapsulate business logic in a consistent way with validations. If a parameter does not satisfy a particular condition, an exception (RailsParam::Param::InvalidParameterError) is raised.
Encapsulate business logic in a consistent way with validations. If a parameter does not satisfy a particular condition, an exception (RailsParam::InvalidParameterError) is raised.
You may use the [rescue_from](http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html#method-i-rescue_from) method in your controller to catch this kind of exception.

- `required`
Expand Down
5 changes: 4 additions & 1 deletion lib/rails_param.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
require 'rails_param/param'
Dir[File.join(__dir__, 'rails_param/validator', '*.rb')].sort.each { |file| require file }
Dir[File.join(__dir__, 'rails_param/coercion', '*.rb')].sort.reverse.each { |file| require file }
Dir[File.join(__dir__, 'rails_param', '*.rb')].sort.each { |file| require file }

ActiveSupport.on_load(:action_controller) do
include RailsParam::Param
include RailsParam
end
41 changes: 41 additions & 0 deletions lib/rails_param/coercion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module RailsParam
class Coercion
attr_reader :coercion, :param

PARAM_TYPE_MAPPING = {
Integer => IntegerParam,
Float => FloatParam,
String => StringParam,
Array => ArrayParam,
Hash => HashParam,
BigDecimal => BigDecimalParam,
Date => TimeParam,
DateTime => TimeParam,
Time => TimeParam,
TrueClass => BooleanParam,
FalseClass => BooleanParam,
boolean: BooleanParam
}.freeze

TIME_TYPES = [Date, DateTime, Time].freeze
BOOLEAN_TYPES = [TrueClass, FalseClass, :boolean].freeze

def initialize(param, type, options)
@param = param
@coercion = klass_for(type).new(param: param, options: options, type: type)
end

def klass_for(type)
klass = PARAM_TYPE_MAPPING[type]
return klass if klass

raise TypeError
end

def coerce
return nil if param.nil?

coercion.coerce
end
end
end
18 changes: 18 additions & 0 deletions lib/rails_param/coercion/array_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module RailsParam
class Coercion
class ArrayParam < VirtualParam
def coerce
return param if param.is_a?(Array)

Array(param.split(options[:delimiter] || ","))
end

private

def argument_validation
raise ArgumentError unless type == Array
raise ArgumentError unless param.respond_to?(:split)
end
end
end
end
17 changes: 17 additions & 0 deletions lib/rails_param/coercion/big_decimal_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RailsParam
class Coercion
class BigDecimalParam < VirtualParam
DEFAULT_PRECISION = 14

def coerce
stripped_param = if param.is_a?(String)
param.delete('$,').strip.to_f
else
param
end

BigDecimal(stripped_param, options[:precision] || DEFAULT_PRECISION)
end
end
end
end
15 changes: 15 additions & 0 deletions lib/rails_param/coercion/boolean_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module RailsParam
class Coercion
class BooleanParam < VirtualParam
FALSEY = /^(false|f|no|n|0)$/i.freeze
TRUTHY = /^(true|t|yes|y|1)$/i.freeze

def coerce
return false if FALSEY === param.to_s
return true if TRUTHY === param.to_s

raise ArgumentError
end
end
end
end
9 changes: 9 additions & 0 deletions lib/rails_param/coercion/float_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module RailsParam
class Coercion
class FloatParam < VirtualParam
def coerce
Float(param)
end
end
end
end
18 changes: 18 additions & 0 deletions lib/rails_param/coercion/hash_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module RailsParam
class Coercion
class HashParam < VirtualParam
def coerce
return param if param.is_a?(ActionController::Parameters)
raise ArgumentError unless param.respond_to?(:split)

Hash[param.split(options[:delimiter] || ",").map { |c| c.split(options[:separator] || ":") }]
end

private

def argument_validation
raise ArgumentError unless type == Hash
end
end
end
end
9 changes: 9 additions & 0 deletions lib/rails_param/coercion/integer_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module RailsParam
class Coercion
class IntegerParam < VirtualParam
def coerce
Integer(param)
end
end
end
end
9 changes: 9 additions & 0 deletions lib/rails_param/coercion/string_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module RailsParam
class Coercion
class StringParam < VirtualParam
def coerce
String(param)
end
end
end
end
17 changes: 17 additions & 0 deletions lib/rails_param/coercion/time_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RailsParam
class Coercion
class TimeParam < VirtualParam
def coerce
return type.strptime(param, options[:format]) if options[:format].present?

type.parse(param)
end

private

def argument_validation
raise ArgumentError unless type.respond_to?(:parse)
end
end
end
end
24 changes: 24 additions & 0 deletions lib/rails_param/coercion/virtual_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module RailsParam
class Coercion
class VirtualParam
attr_reader :param, :options, :type

def initialize(param:, options: nil, type: nil)
@param = param
@options = options
@type = type
argument_validation
end

def coerce
nil
end

private

def argument_validation
nil
end
end
end
end
17 changes: 17 additions & 0 deletions lib/rails_param/invalid_parameter_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RailsParam
class InvalidParameterError < StandardError
attr_accessor :param, :options

def initialize(message, param: nil, options: {})
self.param = param
self.options = options
super(message)
end

def message
return options[:message] if options.is_a?(Hash) && options.key?(:message)

super
end
end
end
Loading

0 comments on commit 7ef3018

Please sign in to comment.