From a1318dd3ebd41d9ce8528a51a8099985fa18f763 Mon Sep 17 00:00:00 2001
From: Paul Duncan <pabs@pablotron.org>
Date: Sun, 6 Mar 2016 01:23:56 -0500
Subject: add api-methods and api-content-type

---
 src/guff/api-content-type.cr |  12 ++++
 src/guff/api-handler.cr      | 154 ++++---------------------------------------
 src/guff/api-methods.cr      | 140 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+), 143 deletions(-)
 create mode 100644 src/guff/api-content-type.cr
 create mode 100644 src/guff/api-methods.cr

diff --git a/src/guff/api-content-type.cr b/src/guff/api-content-type.cr
new file mode 100644
index 0000000..9b6c74a
--- /dev/null
+++ b/src/guff/api-content-type.cr
@@ -0,0 +1,12 @@
+module Guff
+  module APIContentType
+    CONTENT_TYPES = {
+      "development":  "text/html; charset=utf-8",
+      "production":   "application/json; charset=utf8",
+    }
+
+    private def get_content_type
+      CONTENT_TYPES[@model.config["environment"]]
+    end
+  end
+end
diff --git a/src/guff/api-handler.cr b/src/guff/api-handler.cr
index 3192447..dce351f 100644
--- a/src/guff/api-handler.cr
+++ b/src/guff/api-handler.cr
@@ -1,5 +1,6 @@
 require "json"
 require "./handler"
+require "./api-methods"
 
 private macro define_method_calls(hash)
   case namespace
@@ -25,64 +26,8 @@ end
 
 module Guff
   class APIHandler < Handler
-    API = {
-      "post": {
-        "get_posts": {
-          text: "Get posts matching query.",
-
-          args: {
-            "q": {
-              text:     "Search string.",
-              type:     :text,
-              required: false,
-              default:  "",
-            },
-
-            "page": {
-              text:     "Page number",
-              type:     :int,
-              required: false,
-              default:  "1",
-            },
-
-            "tags": {
-              text:     "Comma-separated list of tags (union)",
-              type:     :tags,
-              required: false,
-              default:  "1",
-            },
-
-            "sort": {
-              text:     "Sort order of results",
-              type:     :sort,
-              required: false,
-              default:  "date,desc",
-            },
-          },
-        },
-      },
-
-      "test": {
-        "version": {
-          text: "Get version",
-        },
-
-        "get_posts": {
-          text: "Test get posts",
-        },
-
-        "error": {
-          text: "Test error response",
-        },
-      }
-    }
-
-    TYPE_CHECKS = {
-      text: /.*/,
-      int:  /^\d+$/,
-      tags: /^[a-z0-9_,-]+$/,
-      sort: /^[a-z0-9_]+,(?:asc|desc)$/,
-    }
+    include APIMethods
+    include APIContentType
 
     PATH_RE = %r{
       ^/api
@@ -109,54 +54,6 @@ module Guff
       $
     }mx
 
-    CONTENT_TYPES = {
-      "development":  "text/html; charset=utf-8",
-      "production":   "application/json; charset=utf8",
-    }
-
-    private def get_content_type
-      CONTENT_TYPES[@model.config["environment"]]
-    end
-
-    private def get_method_args(
-      params    : HTTP::Params,
-      namespace : String,
-      method    : String
-    )
-      return {} of String => String unless (
-        API[namespace]? &&
-        API[namespace][method] &&
-        API[namespace][method][:args]?
-      )
-
-      # get method args
-      args = API[namespace][method][:args] as \
-        Hash(String, Hash(Symbol, String | Symbol | Bool))
-
-      args.keys.reduce({} of String => String) do |r, arg_name|
-        arg_data = args[arg_name] as Hash(Symbol, String|Symbol|Bool)
-
-        # check for required parameter
-        if arg_data[:required] && !params.has_key?(arg_name)
-          raise "missing required parameter: %s" % [arg_name]
-        end
-
-        # get value
-        val = params.fetch(arg_name, arg_data[:default] as String)
-
-        # check value
-        if !TYPE_CHECKS[arg_data[:type]].match(val)
-          raise "invalid parameter format: %s" % [arg_name]
-        end
-
-        # add value to result
-        r[arg_name] = val
-
-        # return result
-        r
-      end
-    end
-
     def call(context : HTTP::Server::Context)
       if md = (context.request.path || "").match(PATH_RE)
         if md["namespace"]?
@@ -176,11 +73,18 @@ module Guff
       namespace : String,
       method    : String
     )
+      # set response type
       context.response.content_type = get_content_type
 
       # method call
       json = begin
-        # equivalent to send("do_#{namespace}_#{method}".intern, context)
+        #
+        # macro expands to equivalent of the following for each
+        # namespace and method:
+        #
+        #   args = get_method_args(context, namespace, method)
+        #   send("do_#{namespace}_#{method}".intern, context, args)
+        #
         define_method_calls({
           post: [
             get_posts,
@@ -209,41 +113,5 @@ module Guff
       context.response.content_type = page.content_type
       context.response.puts page
     end
-
-    ################
-    # post methods #
-    ################
-
-    private def do_post_get_posts(
-      context : HTTP::Server::Context,
-      args    : Hash(String, String)
-    )
-      [{foo: "bar"}, {foo: "asdf"}].to_json
-    end
-
-    ################
-    # test methods #
-    ################
-
-    private def do_test_version(
-      context : HTTP::Server::Context,
-      args    : Hash(String, String)
-    )
-      {version: Guff::VERSION}.to_json
-    end
-
-    private def do_test_get_posts(
-      context : HTTP::Server::Context,
-      args    : Hash(String, String)
-    )
-      [{foo: "bar"}, {foo: "asdf"}].to_json
-    end
-
-    private def do_test_error(
-      context : HTTP::Server::Context,
-      args    : Hash(String, String)
-    )
-      raise "some random error"
-    end
   end
 end
diff --git a/src/guff/api-methods.cr b/src/guff/api-methods.cr
new file mode 100644
index 0000000..dc0e1e8
--- /dev/null
+++ b/src/guff/api-methods.cr
@@ -0,0 +1,140 @@
+require "json"
+require "./handler"
+
+module Guff
+  module APIMethods
+    API = {
+      "post": {
+        "get_posts": {
+          text: "Get posts matching query.",
+
+          args: {
+            "q": {
+              text:     "Search string.",
+              type:     :text,
+              required: false,
+              default:  "",
+            },
+
+            "page": {
+              text:     "Page number",
+              type:     :int,
+              required: false,
+              default:  "1",
+            },
+
+            "tags": {
+              text:     "Comma-separated list of tags (union)",
+              type:     :tags,
+              required: false,
+              default:  "1",
+            },
+
+            "sort": {
+              text:     "Sort order of results",
+              type:     :sort,
+              required: false,
+              default:  "date,desc",
+            },
+          },
+        },
+      },
+
+      "test": {
+        "version": {
+          text: "Get version",
+        },
+
+        "get_posts": {
+          text: "Test get posts",
+        },
+
+        "error": {
+          text: "Test error response",
+        },
+      }
+    }
+
+    TYPE_CHECKS = {
+      text: /.*/,
+      int:  /^\d+$/,
+      tags: /^[a-z0-9_,-]+$/,
+      sort: /^[a-z0-9_]+,(?:asc|desc)$/,
+    }
+
+    private def get_method_args(
+      params    : HTTP::Params,
+      namespace : String,
+      method    : String
+    )
+      return {} of String => String unless (
+        API[namespace]? &&
+        API[namespace][method] &&
+        API[namespace][method][:args]?
+      )
+
+      # get method args
+      args = API[namespace][method][:args] as \
+        Hash(String, Hash(Symbol, String | Symbol | Bool))
+
+      args.keys.reduce({} of String => String) do |r, arg_name|
+        arg_data = args[arg_name] as Hash(Symbol, String|Symbol|Bool)
+
+        # check for required parameter
+        if arg_data[:required] && !params.has_key?(arg_name)
+          raise "missing required parameter: %s" % [arg_name]
+        end
+
+        # get value
+        val = params.fetch(arg_name, arg_data[:default] as String)
+
+        # check value
+        if !TYPE_CHECKS[arg_data[:type]].match(val)
+          raise "invalid parameter format: %s" % [arg_name]
+        end
+
+        # add value to result
+        r[arg_name] = val
+
+        # return result
+        r
+      end
+    end
+
+    ################
+    # post methods #
+    ################
+
+    private def do_post_get_posts(
+      context : HTTP::Server::Context,
+      args    : Hash(String, String)
+    )
+      [{foo: "bar"}, {foo: "asdf"}].to_json
+    end
+
+    ################
+    # test methods #
+    ################
+
+    private def do_test_version(
+      context : HTTP::Server::Context,
+      args    : Hash(String, String)
+    )
+      {version: Guff::VERSION}.to_json
+    end
+
+    private def do_test_get_posts(
+      context : HTTP::Server::Context,
+      args    : Hash(String, String)
+    )
+      [{foo: "bar"}, {foo: "asdf"}].to_json
+    end
+
+    private def do_test_error(
+      context : HTTP::Server::Context,
+      args    : Hash(String, String)
+    )
+      raise "some random error"
+    end
+  end
+end
-- 
cgit v1.2.3