Created
December 5, 2012 00:28
-
-
Save lynaghk/4210677 to your computer and use it in GitHub Desktop.
Metaprogramming EDN in ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Idiomatic record constructors in Ruby | |
#My C2PO grammar of graphics uses records (typed maps, basically) to represent pieces of the grammar. | |
#E.g., in Clojure you can specify a linear scale via a constructor function: | |
# | |
# (scale/linear :domain [0 10] :label "The X Axis") | |
# | |
#returns the appropriate scale.linear record with the two key/value pairs given above. | |
#I'm trying to implement the same thing in Ruby; basically just shell records that only know how to serialize themselves out to EDN---no validation of arguments or anything. | |
#Here's the parent class: | |
module C2PO | |
class C2PORecord < Hash | |
def initialize(h={}) | |
self.merge! h | |
end | |
def to_edn | |
_, prefix, name = self.class.name.split("::").map(&:downcase) | |
EDN.tagout "com.keminglabs/c2po$#{prefix}$#{name}", {}.merge(self) | |
end | |
end | |
end | |
#then | |
module C2PO | |
module Geom | |
class Point < C2PORecord | |
end | |
end | |
end | |
#makes something I can instantiate as such: | |
include C2PO | |
p = Geom::Point.new :radius => 2 | |
p.to_edn # => "com.keminglabs/c2po$geom$point {:radius 2}" | |
#however, the ::, capitalization, and #new feel a bit verbose. | |
#One alternative is to put constructors on the appropriate modules: | |
module C2PO | |
module Geom | |
class Point < C2PORecord | |
end | |
def self.point(h={}) | |
Point.new h | |
end | |
end | |
end | |
#and then call with the nicer syntax: | |
p = Geom.point :radius => 2 | |
#but this requires the implementation to repeat itself (saying "point" three times). | |
#Ruby friends: is there a nice way I can metaprogram my way out of this? | |
#For reference, here's my solution in Clojure using actual macros (from: https://github.com/keminglabs/c2po-clojure/blob/master/src/clj/c2po/records.clj): | |
# (ns c2po.records | |
# (:use [c2po.literals :only [print-literal]]) | |
# (:require [clojure.string :as str] | |
# clojure.pprint)) | |
# (defmacro def-c2po-record | |
# [prefix name] | |
# (let [record-name (str/capitalize (clojure.core/name name))] | |
# `(do | |
# (defrecord ~(symbol record-name) []) | |
# ;;print the record appropriately with Clojure's regular and pretty print systems | |
# (defmethod print-method ~(symbol record-name) [x# w#] | |
# (print-literal ~prefix ~(clojure.core/name name) x# w#)) | |
# (defmethod clojure.pprint/simple-dispatch ~(symbol record-name) [x#] (pr x#)) | |
# ;;record constructor | |
# (defn ~name [& kwargs#] | |
# (~(symbol (str "map->" record-name)) | |
# (apply hash-map kwargs#)))))) | |
# (ns c2po.geom | |
# (:use [c2po.records :only [def-c2po-record]])) | |
# (def-c2po-record "geom" point) | |
# (def-c2po-record "geom" line) | |
# (def-c2po-record "geom" bar) | |
# (def-c2po-record "geom" boxplot) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ping: @crnixon you have any thoughts on this?