Skip to content

Instantly share code, notes, and snippets.

@ismasan
Created April 20, 2017 16:12
Show Gist options
  • Select an option

  • Save ismasan/dfc528037d07c0db873771d2d4e1ec5c to your computer and use it in GitHub Desktop.

Select an option

Save ismasan/dfc528037d07c0db873771d2d4e1ec5c to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
source "https://rubygems.org"
gem "rspec"
class Rels
class Rel
TEMPLATED_QUERY_EXP = /{(\?|\&)([^}]+)}/.freeze
EXISTING_QUERY_EXP = /\?(\w+=[^{.+]&?)+/.freeze
attr_reader :name, :verb, :path, :title, :params
def initialize(name:, verb:, path:, title: nil, params: {})
@name, @verb, @path, @title, @params = name, verb, path, title, string_keys(params)
build
end
def templated?
!!(path =~ /{/)
end
private
def build
clean_path = params.reduce(@path) do |str, (k, v)|
str.gsub("{#{k}}", v.to_s)
end
templated_query_match = clean_path.match(TEMPLATED_QUERY_EXP)
clean_path.gsub!(TEMPLATED_QUERY_EXP, '') if templated_query_match
existing_query_match = clean_path.match(EXISTING_QUERY_EXP)
query = {}
if existing_query_match
clean_path.gsub!(EXISTING_QUERY_EXP, '')
query = parse_query(existing_query_match[0])
end
templated_query_params = templated_query_match ? (templated_query_match[2] || "").split(",") : []
# templated params with provided values
provided_query_params = templated_query_params.find_all{|f| params.key?(f) }
# remaining params
remaining_query_params = templated_query_params - provided_query_params
# populate available query params
provided_query_params.each do |k|
query[k] = params[k]
end
@path = clean_path
@path += "?#{to_query(query)}" if query.any?
init = query.any? ? "&" : "?"
@path += "{#{init}#{remaining_query_params.join(",")}}" if remaining_query_params.any?
end
def parse_query(q)
q.to_s.sub("?", "").split("&").each_with_object({}) do |pair, memo|
k, v = pair.split("=")
memo[k] = v
end
end
def string_keys(hash)
hash.each_with_object({}){|(k, v), m| m[k.to_s] = v}
end
def to_query(hash)
hash.each_with_object([]){ |(k, v), memo|
memo << [k, v].join("=")
}.join("&")
end
end
end
require_relative "./rel"
class Rels
class RelBuilder
attr_reader :name, :verb, :path, :title
def initialize(name, verb, path, query_names: [], title: nil, rel_domain: nil)
@name, @verb, @path, @query_names = name, verb, path, query_names
@path = add_query(@path, @query_names) if @verb == :get && @query_names.any?
@title = title
@rel_domain = rel_domain
templatize
end
def build(params = {})
Rel.new(
name: [rel_domain, name].compact.join(':'),
verb: verb,
path: path,
title: title,
params: params
)
end
private
attr_reader :path, :query_names, :rel_domain
EXP = /:\w+/.freeze
def templatize
@path = @path.gsub(EXP) do |m|
m = m.sub(/^:/, '')
"{#{m}}"
end
end
def add_query(path, query_names)
p, q = path.split('?')
if q
q += "{&#{query_names.join(",")}}"
else
q = "{?#{query_names.join(",")}}"
end
[p, q].join
end
end
end
# run with:
# bundle exec rspec spec.rb
require_relative "./rel_builder"
describe Rels::RelBuilder do
let(:builder) {
Rels::RelBuilder.new(
"foo",
:get,
"http://api.com/foos/:id",
query_names: [:q,:page],
title: "this is a foo",
rel_domain: "btc"
)
}
it "builds rel object with templated URIs" do
rel = builder.build
expect(rel.templated?).to be true
expect(rel.title).to eq "this is a foo"
expect(rel.params).to eq({})
expect(rel.name).to eq "btc:foo"
expect(rel.path).to eq "http://api.com/foos/{id}{?q,page}"
end
it "populates segment and query-string parameters" do
rel = builder.build(id: 123, page: 2)
expect(rel.templated?).to be true
expect(rel.params).to eq({"id" => 123, "page" => 2})
expect(rel.path).to eq "http://api.com/foos/123?page=2{&q}"
end
it "expands URIs fully if all params provided" do
rel = builder.build(id: 123, page: 2, q: "foo")
expect(rel.templated?).to be false
expect(rel.params).to eq({"id" => 123, "page" => 2, "q" => "foo"})
expect(rel.path).to eq "http://api.com/foos/123?q=foo&page=2"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment