Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 4a95beface92cf27413471bdce0b39238fa3611b 0 parents
Marcin Bunsch marcinbunsch authored
4 .gitignore
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in representer.gemspec
+gemspec
1  Rakefile
@@ -0,0 +1 @@
+require 'bundler/gem_tasks'
23 lib/representer.rb
@@ -0,0 +1,23 @@
+require 'representer/base'
+require 'representer/lightning'
+require 'representer/extensions'
+
+module Representer
+
+ class << self
+ attr_accessor :lookup_table
+ end
+
+ self.lookup_table = {}
+
+ def self.lookup(name)
+ if found = lookup_table[name]
+ found
+ else
+ found = Object.const_get("#{name.camelcase}Representer")
+ lookup_table[name] = found
+ found
+ end
+ end
+
+end
153 lib/representer/base.rb
@@ -0,0 +1,153 @@
+require "yajl"
+
+module Representer
+
+ class Base
+
+ attr_accessor :scope
+
+ class << self
+ attr_accessor :representable_attributes
+ attr_accessor :representable_methods
+ attr_accessor :representable_fields
+ attr_accessor :representable_namespace
+ attr_accessor :representable_namespace_plural
+
+ def attributes(*args)
+ self.representable_attributes = ['id'] + args
+ end
+
+ def methods(*args)
+ self.representable_methods = args
+ end
+
+ def fields(*args)
+ self.representable_fields = args
+ end
+
+ def namespace(name, plural = nil)
+ self.representable_namespace = name
+ self.representable_namespace_plural = plural ? plural : name + "s"
+ end
+
+ def representable_attributes
+ @representable_attributes ||= ['id']
+ end
+
+ def representable_methods
+ @representable_methods ||= []
+ end
+
+ def representable_fields
+ @representable_fields ||= []
+ end
+
+ end
+
+ def initialize(representable, options = {})
+ @representable = representable.is_a?(ActiveRecord::Relation) ? representable.all : representable
+ @scope = options[:scope]
+ @options = options
+ @ids = []
+ end
+
+ def before_prepare
+ end
+
+ def prepare
+ before_prepare
+ prepared = if @representable.is_a?(Array)
+ @representable.collect do |item|
+ process_single_record(item)
+ end
+ else
+ process_single_record(@representable)
+ end
+ after_prepare(prepared)
+ end
+
+ # Should we skip the second pass?
+ def skip_second_pass?
+ self.class.representable_fields.size == 0
+ end
+
+ def after_prepare(prepared)
+ # Do not perform a second pass when there is no need
+ return prepared if skip_second_pass?
+
+ if prepared.is_a?(Array)
+ prepared.collect do |item| apply_fields(item); item end
+ else
+ apply_fields(prepared)
+ prepared
+ end
+ end
+
+ def apply_fields(prepared_hash)
+ scoped_hash = if self.class.representable_namespace
+ prepared_hash[self.class.representable_namespace]
+ else
+ prepared_hash
+ end
+ self.class.representable_fields.each do |field|
+ if value = self.send(field, scoped_hash)
+ scoped_hash[field] = value
+ end
+ end
+ scoped_hash
+ end
+
+ def process_single_record(record)
+ # Extract the id into an aggregate array
+ @ids.push record.id
+ # Cache the attributes into a local variable
+ attributes = record.attributes
+ # Resulting hash
+ hash = {}
+ # Copy the attributes
+ self.class.representable_attributes.each do |attribute|
+ hash[attribute] = attributes[attribute]
+ end
+
+ self.class.representable_methods.each do |method|
+ hash[method] = record.send(method)
+ end
+
+ if self.class.representable_namespace
+ { self.class.representable_namespace => hash }
+ else
+ hash
+ end
+ end
+
+ def render(method = :json)
+ if method == :xml
+ render_xml
+ else
+ Yajl::Encoder.encode(prepare)
+ end
+ end
+
+ def render_xml
+ prepared = prepare
+ if prepared.is_a?(Hash)
+ if self.class.representable_namespace
+ prepared = prepared[self.class.representable_namespace]
+ prepared.to_xml(:root => self.class.representable_namespace)
+ else
+ prepare.to_xml
+ end
+ elsif prepared.is_a?(Array)
+ if self.class.representable_namespace
+ prepared = prepared.collect { |item| item[self.class.representable_namespace] }
+ prepared.to_xml(:root => self.class.representable_namespace_plural)
+ else
+ prepared.to_xml
+ end
+ end
+ end
+
+ end
+
+end
+
60 lib/representer/extensions.rb
@@ -0,0 +1,60 @@
+module Representer
+
+ module Extension
+
+ module InstanceMethods
+
+ def represent(format, options = {})
+ representer = if options.has_key?(:representer)
+ repr = options.delete(:representer)
+ repr.is_a?(String) ? Representer.lookup(repr) : repr
+ else
+ Representer.lookup(representer_name)
+ end
+ instance = representer.new(self, options)
+ instance.render(format)
+ end
+
+ def representer_name
+ self.class.name
+ end
+
+ end
+
+ end
+
+end
+
+class Array
+
+ include Representer::Extension::InstanceMethods
+
+ def representer_name
+ self.first ? self.first.class.name : 'Array'
+ end
+
+end
+
+if defined?(ActiveRecord::Relation)
+
+ class ActiveRecord::Relation
+
+ include Representer::Extension::InstanceMethods
+
+ def representer_name
+ self.klass.name
+ end
+
+ end
+
+end
+
+if defined?(ActiveRecord::Base)
+
+ class ActiveRecord::Base
+
+ include Representer::Extension::InstanceMethods
+
+ end
+
+end
60 lib/representer/lightning.rb
@@ -0,0 +1,60 @@
+module Representer
+
+ class Lightning < Base
+
+ def before_prepare
+ if @representable.is_a?(ActiveRecord::Relation) and
+ self.class.representable_fields.size == 0 and
+ self.class.representable_methods.size == 0
+ @representable = lightning_mode_convert
+ @lightning_mode = true
+ end
+ end
+
+ def lightning_mode_convert
+ client = @representable.connection.instance_variable_get('@connection')
+ results = client.query(@representable.to_sql)
+ headers = results.fields
+ results.collect do |row|
+ result = {}
+ headers.each_with_index do |header, index|
+ result[header] = row[index]
+ end
+ result
+ end
+ end
+
+ def process_single_record(record)
+ if @lightning_mode
+ # Extract the id into an aggregate array
+ @ids.push record['id']
+ # Cache the attributes into a local variable
+ attributes = record
+ else
+ # Extract the id into an aggregate array
+ @ids.push record.id
+ # Cache the attributes into a local variable
+ attributes = record.attributes
+ end
+ # Resulting hash
+ hash = {}
+ # Copy the attributes
+ self.class.representable_attributes.each do |attribute|
+ hash[attribute] = attributes[attribute]
+ end
+
+ self.class.representable_methods.each do |method|
+ hash[method] = record.send(method)
+ end
+
+ if self.class.representable_namespace
+ { self.class.representable_namespace => hash }
+ else
+ hash
+ end
+ end
+
+ end
+
+end
+
3  lib/representer/version.rb
@@ -0,0 +1,3 @@
+module Representer
+ VERSION = "0.0.1"
+end
20 representer.gemspec
@@ -0,0 +1,20 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "representer/version"
+
+Gem::Specification.new do |s|
+ s.name = "representer"
+ s.version = Representer::VERSION
+ s.authors = ["Marcin Bunsch"]
+ s.email = ["marcin@futuresimple.com"]
+ s.homepage = ""
+ s.summary = %q{Representers - take control of json/xml representation of your objects!}
+ s.description = %q{Representers - take control of json/xml representation of your objects!}
+
+ s.rubyforge_project = "representer"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
Please sign in to comment.
Something went wrong with that request. Please try again.