-
-
Save hannahwhy/2339394 to your computer and use it in GitHub Desktop.
I can't tell if this is smart or stupid
This file contains hidden or 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
| The problem: | |
| I want to be able to have attribute readers for a CouchDB document. Why? So I | |
| can use Action Pack's form builder with said document. | |
| Here's an example document: | |
| { "title": "A video", | |
| "audio": [ | |
| { "artist": "Dot Dot Dot", | |
| "title": "Edge Of The World" | |
| }, | |
| { "title": "Birds in my backyard" } | |
| ], | |
| "video": [ | |
| "Macross Frontier", | |
| "Original live-action footage" | |
| ] | |
| } | |
| So I want this sort of thing to work: | |
| doc.title | |
| doc.audio[0].artist | |
| doc.video[1] | |
| because Action Pack's form builder expects it to work. | |
| So far I've come up with the code below. It walks a JSON structure at class | |
| definition time and builds out attribute methods for each field it finds. If | |
| it finds a field that references an object, it creates a proxy class and walks | |
| that object. If it finds a field that references an array, it creates an object | |
| proxy class and a list proxy class. | |
| For simplicity, none of the attributes in any of the proxy classes are | |
| mutable. (And they don't need to be, ever: in CouchDB, document update is | |
| document replacement, so a document can be updated by with two PUTs of | |
| different instances of a proxy object to a given ID.) |
This file contains hidden or 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
| module AttributeWrapper | |
| extend ActiveSupport::Concern | |
| # _doc is expected to be an instance method that returns the original document hash | |
| # this requires Ruby 1.9: in 1.8, String.respond_to?(:each) => true | |
| module ClassMethods | |
| class EnumerableProxy | |
| include Enumerable | |
| def initialize(list, object_proxy) | |
| @list = list || [] | |
| @prox = list.map { |v| object_proxy.new(v) } | |
| end | |
| def each(&block) | |
| @prox.each(&block) | |
| end | |
| def [](index) | |
| @prox[index] | |
| end | |
| end | |
| class ObjectProxy | |
| include Catalog::ActiveModel | |
| def self.[](s) | |
| Class.new(self) { schema s } | |
| end | |
| end | |
| def schema(str) | |
| return schema(JSON.parse(str)) if String === str | |
| str.each do |k, v| | |
| if v.respond_to?(:keys) | |
| proxy = ObjectProxy[v] | |
| define_method(k) { proxy.new(_doc[k]) } | |
| elsif v.respond_to?(:first) && v.respond_to?(:empty?) | |
| if v.empty? | |
| define_method(k) { _doc[k] } | |
| else | |
| proxy = ObjectProxy[v.first] | |
| define_method(k) { EnumerableProxy.new(_doc[k], proxy) } | |
| end | |
| else | |
| define_method(k) { _doc[k] } | |
| end | |
| end | |
| end | |
| end | |
| end |
This file contains hidden or 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
| class Video | |
| include AttributeWrapper | |
| schema %q{ | |
| { "title": "", | |
| "banner_image": { | |
| "position": "", | |
| "url": "" | |
| }, | |
| "audio": [ | |
| { "artist": "Ellie Goulding", | |
| "title": "Animal" | |
| } | |
| ], | |
| "video": ["Macross Frontier"] | |
| } | |
| } | |
| end | |
| # v = Video.new(doc) | |
| # v.title # => "A video" | |
| # v.banner_image #= ><#<Class:0x2c0>:0x2f1 ...> | |
| # v.banner_image.position => "10% 20%" | |
| # v.banner_image.url => "http://www.example.org/image.jpg" | |
| # v.audio[0].artist => "Ellie Goulding" | |
| # v.video[0] => "Macross Frontier" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment