From 4049b89e4f707293bf7eed4ea998df57583ba0d9 Mon Sep 17 00:00:00 2001 From: debbbbie Date: Mon, 26 Jan 2015 10:29:55 +0800 Subject: [PATCH 1/3] Support type JSON --- lib/rails_param/param.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rails_param/param.rb b/lib/rails_param/param.rb index f79d16c..e1875d8 100644 --- a/lib/rails_param/param.rb +++ b/lib/rails_param/param.rb @@ -84,13 +84,14 @@ def coerce(param, type, options = {}) return DateTime.parse(param) if type == DateTime return Array(param.split(options[:delimiter] || ",")) if type == Array return Hash[param.split(options[:delimiter] || ",").map { |c| c.split(options[:separator] || ":") }] if type == Hash + return JSON.parse(param).symbolize_keys if type == JSON return (/(false|f|no|n|0)$/i === param.to_s ? false : (/(true|t|yes|y|1)$/i === param.to_s ? true : nil)) if type == TrueClass || type == FalseClass || type == :boolean if type == BigDecimal param = param.delete('$,').strip.to_f if param.is_a?(String) return BigDecimal.new(param, (options[:precision] || DEFAULT_PRECISION)) end return nil - rescue ArgumentError + rescue ArgumentError, JSON::ParserError raise InvalidParameterError, "'#{param}' is not a valid #{type}" end end From 828bec4f447830743344e6a476a1675e60bafd09 Mon Sep 17 00:00:00 2001 From: debbbbie Date: Thu, 5 Feb 2015 21:38:31 +0800 Subject: [PATCH 2/3] JSON support and rspec tested. --- lib/rails_param/param.rb | 3 +-- spec/rails_param/param_spec.rb | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/rails_param/param.rb b/lib/rails_param/param.rb index e1875d8..39b8668 100644 --- a/lib/rails_param/param.rb +++ b/lib/rails_param/param.rb @@ -1,6 +1,5 @@ module RailsParam module Param - DEFAULT_PRECISION = 14 class InvalidParameterError < StandardError @@ -84,7 +83,7 @@ def coerce(param, type, options = {}) return DateTime.parse(param) if type == DateTime return Array(param.split(options[:delimiter] || ",")) if type == Array return Hash[param.split(options[:delimiter] || ",").map { |c| c.split(options[:separator] || ":") }] if type == Hash - return JSON.parse(param).symbolize_keys if type == JSON + return JSON(param) if type == JSON return (/(false|f|no|n|0)$/i === param.to_s ? false : (/(true|t|yes|y|1)$/i === param.to_s ? true : nil)) if type == TrueClass || type == FalseClass || type == :boolean if type == BigDecimal param = param.delete('$,').strip.to_f if param.is_a?(String) diff --git a/spec/rails_param/param_spec.rb b/spec/rails_param/param_spec.rb index 6502bcd..512ee03 100644 --- a/spec/rails_param/param_spec.rb +++ b/spec/rails_param/param_spec.rb @@ -83,6 +83,12 @@ def params; expect(controller.params["foo"]).to eql({"key1" => "foo", "key2" => "bar"}) end + it "converts to JSON" do + allow(controller).to receive(:params).and_return({"foo" => '{ "key1": "foo", "key2": "bar"}'.gsub("\"", '"') }) + controller.param! 'foo', JSON + expect(controller.params["foo"]).to eql({"key1" => "foo", "key2" => "bar"}) + end + it "converts to Date" do allow(controller).to receive(:params).and_return({"foo" => "1984-01-10"}) controller.param! :foo, Date @@ -221,6 +227,47 @@ def params; end end + describe 'validating nested json' do + it 'typecasts nested attributes' do + allow(controller).to receive(:params).and_return('foo' => '{ "bar" : 1, "baz" : 2}'.gsub("\"", '"')) + controller.param! :foo, JSON do |p| + p.param! :bar, BigDecimal + p.param! :baz, Float + end + expect(controller.params['foo']['bar']).to be_instance_of BigDecimal + expect(controller.params['foo']['baz']).to be_instance_of Float + end + + it 'does not raise exception if hash is not required but nested attributes are, and no hash is provided' do + allow(controller).to receive(:params).and_return(foo: nil) + controller.param! :foo, JSON do |p| + p.param! :bar, BigDecimal, required: true + p.param! :baz, Float, required: true + end + expect(controller.params['foo']).to be_nil + end + + it 'raises exception if hash is required, nested attributes are not required, and no hash is provided' do + allow(controller).to receive(:params).and_return(foo: nil) + expect { + controller.param! :foo, JSON, required: true do |p| + p.param! :bar, BigDecimal + p.param! :baz, Float + end + }.to raise_exception + end + + it 'raises exception if hash is not required but nested attributes are, and hash has missing attributes' do + allow(controller).to receive(:params).and_return("{'foo' : {'bar' : 1, 'baz' : nil}}") + expect { + controller.param! :foo, JSON do |p| + p.param! :bar, BigDecimal, required: true + p.param! :baz, Float, required: true + end + }.to raise_exception + end + end + describe 'validating arrays' do it 'typecasts array of primitive elements' do allow(controller).to receive(:params).and_return({'array' => ['1', '2']}) From a5c99404c28932d201bb06af6f98d79419aaff9d Mon Sep 17 00:00:00 2001 From: debbbbie Date: Thu, 5 Feb 2015 21:41:38 +0800 Subject: [PATCH 3/3] README for JSON type. --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 87d0a9b..eecae7d 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ By declaring parameter types, incoming parameters will automatically be transfor - `:boolean/TrueClass/FalseClass` _("1/0", "true/false", "t/f", "yes/no", "y/n")_ - `Array` _("1,2,3,4,5")_ - `Hash` _("key1:value1,key2:value2")_ +- `JSON` _("{ "key1": "foo", "key2": "bar"}")_ - `Date`, `Time`, & `DateTime` - `BigDecimal` _("$1,000,000")_ @@ -101,6 +102,19 @@ param! :book, Hash do |b| end ``` +And JSON also: + +```ruby +param! :book, JSON do |b| + b.param! :title, String, blank: false + b.param! :price, BigDecimal, precision: 4, required: true + b.param! :author, JSON, required: true do |a| + a.param! :first_name, String + a.param! :last_name, String, blank: false + end +end +``` + ### Arrays Validate every element of your array, including nested hashes and arrays: