Permalink
Browse files

Initial import, everything's up and running.

  • Loading branch information...
1 parent 518c21a commit fd54a3c2b756832b20d233bcd4526b4ee783774f Michael Bleigh committed Jun 13, 2010
View
@@ -17,5 +17,6 @@ tmtags
coverage
rdoc
pkg
+.bundle
## PROJECT::SPECIFIC
View
@@ -0,0 +1,8 @@
+source "http://rubygems.org"
+
+gem 'rspec'
+
+gem 'json', :require => nil
+gem 'json_pure', :require => nil
+gem 'yajl-ruby', :require => nil
+gem 'activesupport', :require => nil
View
@@ -1,6 +1,17 @@
-= x_to_json
+= Use X to JSON
-Description goes here.
+Lots of Ruby libraries utilize JSON parsing in some form, and everyone has their favorite JSON library. In order to best support multiple JSON parsers and libraries, <tt>x_to_json</tt> is a general-purpose swappable JSON backend library. You use it like so:
+
+ require 'x_to_json'
+
+ XToJson.engine = :yajl
+ XToJson.decode('{"abc":"def"}') # decoded using Yajl
+
+ XToJson.engine = :json_gem
+ XToJson.engine = XToJson::Engines::JsonGem # equivalent to previous line
+ XToJson.encode({:abc => 'def'}) # encoded using the JSON gem
+
+The <tt>engine</tt> setter takes either a symbol or a class (to allow for custom JSON parsers) that responds to both <tt>.decode</tt> and <tt>.encode</tt> at the class level.
== Note on Patches/Pull Requests
View
@@ -0,0 +1,53 @@
+module XToJson
+ module_function
+
+ # Get the current engine class.
+ def engine
+ return @engine if @engine
+ self.engine = self.default_engine
+ @engine
+ end
+
+ # The default engine based on what you currently
+ # have loaded. Tries Yajl first, then JSON gem,
+ # then ActiveSupport and JSON pure.
+ def default_engine
+ return :yajl if defined?(::Yajl)
+ return :json_gem if defined?(::JSON)
+ return :active_support if defined?(::ActiveSupport::JSON)
+ :json_pure
+ end
+
+ # Set the JSON parser utilizing a symbol, string, or class.
+ # Supported by default are:
+ #
+ # * <tt>:json_gem</tt>
+ # * <tt>:json_pure</tt>
+ # * <tt>:active_support</tt> (useful for inside Rails apps)
+ # * <tt>:yajl</tt>
+ def engine=(new_engine)
+ case new_engine
+ when String, Symbol
+ require "x_to_json/engines/#{new_engine}"
+ @engine = XToJson::Engines.const_get("#{new_engine.to_s.split('_').map{|s| s.capitalize}.join('')}")
+ when Class
+ @engine = new_engine
+ else
+ raise "Did not recognize your engine specification. Please specify either a symbol or a class."
+ end
+ end
+
+ # Decode a JSON string into Ruby.
+ #
+ # <b>Options</b>
+ #
+ # <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
+ def decode(string, options = {})
+ engine.decode(string, options)
+ end
+
+ # Encodes a Ruby object as JSON.
+ def encode(object)
+ engine.encode(object)
+ end
+end
@@ -0,0 +1,31 @@
+require 'active_support' unless defined?(::ActiveSupport::JSON)
+
+module XToJson
+ module Engines
+ class ActiveSupport
+ def self.decode(string, options = {})
+ hash = ::ActiveSupport::JSON.decode(string)
+ options[:symbolize_keys] ? symbolize_keys(hash) : hash
+ end
+
+ def self.encode(object)
+ ::ActiveSupport::JSON.encode(object)
+ end
+
+ def self.symbolize_keys(hash)
+ hash.inject({}){|result, (key, value)|
+ new_key = case key
+ when String then key.to_sym
+ else key
+ end
+ new_value = case value
+ when Hash then symbolize_keys(value)
+ else value
+ end
+ result[new_key] = new_value
+ result
+ }
+ end
+ end
+ end
+end
@@ -0,0 +1,17 @@
+require 'json' unless defined?(::JSON)
+
+module XToJson
+ module Engines
+ class JsonGem
+ def self.decode(string, options = {})
+ opts = {}
+ opts[:symbolize_names] = options[:symbolize_keys]
+ ::JSON.parse(string, opts)
+ end
+
+ def self.encode(object)
+ object.to_json
+ end
+ end
+ end
+end
@@ -0,0 +1,17 @@
+require 'json/pure' unless defined?(::JSON)
+
+module XToJson
+ module Engines
+ class JsonPure
+ def self.decode(string, options = {})
+ opts = {}
+ opts[:symbolize_names] = options[:symbolize_keys]
+ ::JSON.parse(string, opts)
+ end
+
+ def self.encode(object)
+ object.to_json
+ end
+ end
+ end
+end
@@ -0,0 +1,15 @@
+require 'yajl' unless defined?(Yajl)
+
+module XToJson
+ module Engines
+ class Yajl
+ def self.decode(string, options = {})
+ ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string)
+ end
+
+ def self.encode(object)
+ ::Yajl::Encoder.new.encode(object)
+ end
+ end
+ end
+end
View
@@ -1 +1,2 @@
--color
+--format=nested
View
@@ -3,6 +3,9 @@
require 'x_to_json'
require 'spec'
require 'spec/autorun'
+require 'rubygems'
+require 'bundler'
+Bundler.setup
Spec::Runner.configure do |config|
View
@@ -1,7 +1,59 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+class MockDecoder
+ def self.decode(string, options = {})
+ {'abc' => 'def'}
+ end
+
+ def self.encode(string)
+ '{"abc":"def"}'
+ end
+end
+
describe "XToJson" do
- it "fails" do
- fail "hey buddy, you should probably rename this file and start specing for real"
+ context 'engines' do
+ it 'should default to the best available gem' do
+ require 'yajl'
+ XToJson.engine.name.should == 'XToJson::Engines::Yajl'
+ end
+
+ it 'should be settable via a symbol' do
+ XToJson.engine = :yajl
+ XToJson.engine.name.should == 'XToJson::Engines::Yajl'
+ end
+
+ it 'should be settable via a class' do
+ XToJson.engine = MockDecoder
+ XToJson.engine.name.should == 'MockDecoder'
+ end
+ end
+
+ %w(active_support json_gem json_pure yajl).each do |engine|
+ context engine do
+ before do
+ XToJson.engine = engine
+ end
+
+ describe '.encode' do
+ it 'should write decodable JSON' do
+ [
+ {'abc' => 'def'},
+ [1, 2, 3, "4"]
+ ].each do |example|
+ XToJson.decode(XToJson.encode(example)).should == example
+ end
+ end
+ end
+
+ describe '.decode' do
+ it 'should properly decode some json' do
+ XToJson.decode('{"abc":"def"}').should == {'abc' => 'def'}
+ end
+
+ it 'should allow for symbolization of keys' do
+ XToJson.decode('{"abc":{"def":"hgi"}}', :symbolize_keys => true).should == {:abc => {:def => 'hgi'}}
+ end
+ end
+ end
end
end

0 comments on commit fd54a3c

Please sign in to comment.