Skip to content

Commit

Permalink
feat: standardize method naming
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Objects including HTTP::Params::Serializable now get `.from_query` and `#to_query` methods,
while all other objects (e.g. scalars) should implement `.from_http_param` and `#to_http_param` methods.

Closes #22
  • Loading branch information
vladfaust committed Feb 12, 2019
1 parent b80fdee commit 5bf36ce
Show file tree
Hide file tree
Showing 17 changed files with 81 additions and 80 deletions.
16 changes: 8 additions & 8 deletions README.md
Expand Up @@ -48,13 +48,13 @@ struct MyParams
getter id : Int32
end
params = MyParams.new("id=42")
params = MyParams.from_query("id=42")
pp params.id.class # => Int32
MyParams.new("")
MyParams.from_query("")
# HTTP::Params::Serializable::ParamMissingError: Parameter "id" is missing
MyParams.new("id=foo")
MyParams.from_query("id=foo")
# HTTP::Params::Serializable::ParamTypeCastError: Parameter "id" cannot be cast from "foo" to Int32
```

Expand All @@ -66,7 +66,7 @@ struct MyParams
getter id : Int32 | Nil
end
params = MyParams.new("id=")
params = MyParams.from_query("id=")
pp params.id # => nil
```

Expand All @@ -78,7 +78,7 @@ struct MyParams
getter foo : Array(Float32)
end
params = MyParams.new("foo[]=42.0&foo[]=43.5")
params = MyParams.from_query("foo[]=42.0&foo[]=43.5")
pp params.foo[1] # => 43.5
```

Expand All @@ -95,7 +95,7 @@ struct MyParams
end
end
params = MyParams.new("nested[foo]=true")
params = MyParams.from_query("nested[foo]=true")
pp params.nested.foo # => true
```

Expand All @@ -112,7 +112,7 @@ struct MyParams
end
end
params = MyParams.new("nested[0][foo][]=1&nested[0][foo][]=2")
params = MyParams.from_query("nested[0][foo][]=1&nested[0][foo][]=2")
pp params.nested.first.foo.first # => [1, 2]
```

Expand All @@ -131,7 +131,7 @@ end
get "/" do |env|
if query = env.request.query
query_params = MyParams.new(query)
query_params = MyParams.from_query(query)
if query_params.id > 0
"#{query_params.id} is positive\n"
Expand Down
4 changes: 2 additions & 2 deletions spec/http-params-serializable/default_value_spec.cr
Expand Up @@ -9,13 +9,13 @@ end

describe ParamsWithDefaultValue do
it do
v = ParamsWithDefaultValue.new("required=41")
v = ParamsWithDefaultValue.from_query("required=41")
v.required.should eq 41
v.optional.should eq 42
end

it do
v = ParamsWithDefaultValue.new("required=41&optional=43")
v = ParamsWithDefaultValue.from_query("required=41&optional=43")
v.required.should eq 41
v.optional.should eq 43
end
Expand Down
4 changes: 2 additions & 2 deletions spec/http-params-serializable/nested_spec.cr
Expand Up @@ -21,7 +21,7 @@ end
describe NestedParams do
describe "(sub)nested" do
it do
v = NestedParams.new("nested[required]=true&nested[required_array][]=42&nested[subnested][0][unknown]=foo")
v = NestedParams.from_query("nested[required]=true&nested[required_array][]=42&nested[subnested][0][unknown]=foo")

v.nested.should be_a(NestedParams::Nested)
nested = v.nested.not_nil!
Expand All @@ -36,7 +36,7 @@ describe NestedParams do
subnested.optional.should be_nil

# Subnested is not nil itself, but it's not rendered due to being empty
v.to_http_param.should eq escape("nested[required]=true&nested[requiredArray][]=42")
v.to_query.should eq escape("nested[required]=true&nested[requiredArray][]=42")
end

# The keys are checked de-facto, thus "nested[]=" key is never validated, therefore skipped
Expand Down
32 changes: 16 additions & 16 deletions spec/http-params-serializable/simple_spec.cr
Expand Up @@ -11,7 +11,7 @@ end

describe SimpleParams do
describe "required" do
v = SimpleParams.new("required=42&unknown=foo")
v = SimpleParams.from_query("required=42&unknown=foo")

describe "parsing" do
it do
Expand Down Expand Up @@ -52,57 +52,57 @@ describe SimpleParams do

describe "serializing" do
it do
v.to_http_param.should eq "required=42"
v.to_query.should eq "required=42"
end
end
end

describe "optional" do
it "casts to Bool" do
v = SimpleParams.new("required=42&fooBar=true")
v = SimpleParams.from_query("required=42&fooBar=true")
v.optional.should be_a(Bool)
v.optional.should eq true
v.to_http_param.should eq "required=42&fooBar=true"
v.to_query.should eq "required=42&fooBar=true"
end

it "casts to Char" do
v = SimpleParams.new("required=42&fooBar=t")
v = SimpleParams.from_query("required=42&fooBar=t")
v.optional.should be_a(Char)
v.optional.should eq 't'
v.to_http_param.should eq "required=42&fooBar=t"
v.to_query.should eq "required=42&fooBar=t"
end

it "casts to String" do
v = SimpleParams.new("required=42&fooBar=foo")
v = SimpleParams.from_query("required=42&fooBar=foo")
v.optional.should be_a(String)
v.optional.should eq "foo"
v.to_http_param.should eq "required=42&fooBar=foo"
v.to_query.should eq "required=42&fooBar=foo"
end

it "casts to Float32 instead of Int32" do
v = SimpleParams.new("required=42&fooBar=42")
v = SimpleParams.from_query("required=42&fooBar=42")
v.optional.should be_a(Float32)
v.optional.should eq 42.0
v.to_http_param.should eq "required=42&fooBar=42.0"
v.to_query.should eq "required=42&fooBar=42.0"
end

it "casts to Float32" do
v = SimpleParams.new("required=42&fooBar=-42.1")
v = SimpleParams.from_query("required=42&fooBar=-42.1")
v.optional.should be_a(Float32)
v.optional.should eq -42.1_f32
v.to_http_param.should eq "required=42&fooBar=-42.1"
v.to_query.should eq "required=42&fooBar=-42.1"
end

it "stays nil on empty" do
v = SimpleParams.new("required=42&fooBar=")
v = SimpleParams.from_query("required=42&fooBar=")
v.optional.should be_nil
v.to_http_param.should eq "required=42"
v.to_query.should eq "required=42"
end

it "stays nil on missing" do
v = SimpleParams.new("required=42")
v = SimpleParams.from_query("required=42")
v.optional.should be_nil
v.to_http_param.should eq "required=42"
v.to_query.should eq "required=42"
end
end
end
4 changes: 2 additions & 2 deletions spec/http-params-serializable/time_spec.cr
Expand Up @@ -26,7 +26,7 @@ end

describe TimeParams do
it do
v = TimeParams.new("epoch_time=1544958806&nilableEpochTime=1544958806&ArrayEpochTime[]=1544958806&nilable-array-epoch-time[0]=1544958806&Nilable-Array-Nilable-Epoch-Time[]=1544958806&array_nilable_epoch_time[0]=")
v = TimeParams.from_query("epoch_time=1544958806&nilableEpochTime=1544958806&ArrayEpochTime[]=1544958806&nilable-array-epoch-time[0]=1544958806&Nilable-Array-Nilable-Epoch-Time[]=1544958806&array_nilable_epoch_time[0]=")

v.epoch_time.should eq Time.unix(1544958806)
v.nilable_epoch_time.should eq Time.unix(1544958806)
Expand All @@ -35,7 +35,7 @@ describe TimeParams do
v.array_nilable_epoch_time.should eq [nil]
v.nilable_array_nilable_epoch_time.should eq [Time.unix(1544958806)]

v.to_http_param.should eq escape("epochTime=1544958806&nilableEpochTime=1544958806&arrayEpochTime[]=1544958806&nilableArrayEpochTime[]=1544958806&nilableArrayNilableEpochTime[]=1544958806")
v.to_query.should eq escape("epochTime=1544958806&nilableEpochTime=1544958806&arrayEpochTime[]=1544958806&nilableArrayEpochTime[]=1544958806&nilableArrayNilableEpochTime[]=1544958806")
end

it "raises on type mismatch" do
Expand Down
4 changes: 2 additions & 2 deletions spec/http-params-serializable/uri_spec.cr
Expand Up @@ -16,14 +16,14 @@ end

describe URIParams do
it do
v = URIParams.new("uri=https://example.com&nilable_uri=foo&arrayUri[0]=bar&nilableArrayUri[]=baz&arrayNilableUri[]=&nilableArrayNilableUri[0]=")
v = URIParams.from_query("uri=https://example.com&nilable_uri=foo&arrayUri[0]=bar&nilableArrayUri[]=baz&arrayNilableUri[]=&nilableArrayNilableUri[0]=")
v.uri.should eq URI.parse("https://example.com")
v.nilable_uri.should eq URI.parse("foo")
v.array_uri.should eq [URI.parse("bar")]
v.nilable_array_uri.should eq [URI.parse("baz")]
v.array_nilable_uri.should eq [nil]
v.nilable_array_nilable_uri.should eq [nil]

v.to_http_param.should eq escape("uri=https://example.com&nilableUri=foo&arrayUri[]=bar&nilableArrayUri[]=baz")
v.to_query.should eq escape("uri=https://example.com&nilableUri=foo&arrayUri[]=bar&nilableArrayUri[]=baz")
end
end
2 changes: 1 addition & 1 deletion spec/spec_helper.cr
Expand Up @@ -6,7 +6,7 @@ require "../src/http-params-serializable"
macro assert_raise(object, query, error, message, path)
expect_raises {{error}} do
begin
{{object}}.new({{query}})
{{object}}.from_query({{query}})
rescue ex : {{error}}
ex.message.should eq {{message}}
ex.path.should eq {{path}}
Expand Down
37 changes: 19 additions & 18 deletions src/http-params-serializable.cr
Expand Up @@ -2,7 +2,7 @@ require "http/params"
require "./http-params-serializable/*"

# Including this module will make an object serializable to and from HTTP params query.
# It adds a `.new(http_param : String)` and `#to_http_param` methods.
# It adds a `.from_query(query : String)` and `#to_query : String` methods.
module HTTP::Params::Serializable
# Build query path from a tuple of *path* elements.
#
Expand Down Expand Up @@ -54,8 +54,7 @@ module HTTP::Params::Serializable
end

# Serialalize `self` into an HTTP params query with the *builder* at *key*.
# Instance variables are by default seralized under "camelCased" keys,
# unless explicitly specified with `@[HTTP::Param(key: "mykey")` (see `HTTP::Param`).
# See `#to_query` for instance variable rules.
def to_http_param(builder : Builder, key : String? = nil)
{% for ivar, i in @type.instance_vars %}
if var = @{{ivar.name}}
Expand Down Expand Up @@ -87,7 +86,9 @@ module HTTP::Params::Serializable
end

# Serialalize `self` into an HTTP params query, returning a `String`.
def to_http_param : String
# Instance variables are by default seralized under camelCased keys,
# unless explicitly specified with `@[HTTP::Param(key: "mykey")`.
def to_query : String
builder = HTTP::Params::Builder.new
to_http_param(builder)
builder.to_s
Expand All @@ -97,23 +98,23 @@ module HTTP::Params::Serializable
# These methods are copied from `JSON::Serializable`
#

def self.new(http_param : String, path : Tuple)
def self.from_http_param(query : String, path : Tuple)
instance = allocate
instance.initialize(_http_params_query: http_param, _http_params_path: path)
instance.initialize(_http_params_query: query, _http_params_path: path)
GC.add_finalizer(instance) if instance.responds_to?(:finalize)
instance
end

def self.new(http_param : String)
new(http_param: http_param, path: Tuple.new)
def self.from_query(query : String)
from_http_param(query: query, path: Tuple.new)
end

macro inherited
def self.new(http_param : String, path : Tuple)
def self.from_http_param(query : String, path : Tuple)
super
end

def self.new(http_param : String)
def self.from_query(query : String)
super
end
end
Expand Down Expand Up @@ -183,7 +184,7 @@ module HTTP::Params::Serializable
{% else %}
# The param doesn't have a converter, try to initialize
# its explicit type from the incoming value
{{ivar.name}}_value = {{ivar.type}}.new(http_param: value)
{{ivar.name}}_value = {{ivar.type}}.from_http_param(value)
{% end %}
rescue TypeCastError
unless value.empty?
Expand Down Expand Up @@ -234,17 +235,17 @@ module HTTP::Params::Serializable
# Check if the param has a converter annotated
{% if converter = ivar.annotation(HTTP::Param) && ivar.annotation(HTTP::Param)[:converter] %}
# Initialize the type passing the `converter:` argument
{{ivar.name}}_value = {{ivar.type}}.new(
http_param: query,
path: _http_params_path + { {{ivar.name.stringify}} },
converter: {{converter}},
{{ivar.name}}_value = {{ivar.type}}.from_http_param(
query,
_http_params_path + { {{ivar.name.stringify}} },
{{converter}},
)
{% else %}
# The param doesn't have a converter, try to initialize
# its explicit type from the incoming value
{{ivar.name}}_value = {{ivar.type}}.new(
http_param: query,
path: _http_params_path + { {{ivar.name.stringify}} }
{{ivar.name}}_value = {{ivar.type}}.from_http_param(
query,
_http_params_path + { {{ivar.name.stringify}} }
)
{% end %}
rescue TypeCastError
Expand Down
18 changes: 9 additions & 9 deletions src/http-params-serializable/annotations.cr
Expand Up @@ -20,7 +20,7 @@ module HTTP
# getter time : Array(Time)
# end
#
# params = MyParams.new("theTime[]=1544958806")
# params = MyParams.from_query("theTime[]=1544958806")
# pp params.time.class # => Array(Time)
# ```
annotation Param
Expand All @@ -40,7 +40,7 @@ module HTTP
# builder.add(key, to_s)
# end
#
# def self.new(http_param value : String)
# def self.from_http_param(value : String)
# return URI.parse(value)
# end
# end
Expand All @@ -50,7 +50,7 @@ module HTTP
# getter uri : URI
# end
#
# params = MyParams.new("uri=https://example.com")
# params = MyParams.from_query("uri=https://example.com")
# pp params.uri # => <URI @host="example.com" ...>
# ```
#
Expand Down Expand Up @@ -78,29 +78,29 @@ module HTTP
# # E.g. builder.add(key, self.to_s)
# end
#
# def self.new(http_param value : String) : self
# def self.from_http_param(value : String) : self
# # E.g. value.to_s
# end
# ```
#
# Otherwise, the methods become a little more complex:
# Otherwise (if the type is not `Scalar`), the methods become a little more complex:
#
# ```
# def to_http_param(builder : HTTP::Params::Builder, key : String? = nil)
# # ditto
# # Notice the *key* is nilable
# end
#
# # Or if you're using a converter on a param:
# def to_http_param(builder : HTTP::Params::Builder, key : String? = nil, converter : C = nil) forall C
# # ditto
# end
#
# def self.new(http_param query : String, path : Tuple) : self
# # ditto
# def self.from_http_param(query : String, path : Tuple) : self
# # Notice the *path* argument
# end
#
# # Or if you're using a converter on a param:
# def self.new(http_param query : String, path : Tuple, converter : C = nil) : self forall C
# def self.from_http_param(query : String, path : Tuple, converter : C = nil) : self forall C
# # ditto
# end
# ```
Expand Down

0 comments on commit 5bf36ce

Please sign in to comment.