simple-json 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ # ///////////////////////////////////////////////////////////////////////////////////////
2
+ #
3
+ # Author:: lp (mailto:lp@spiralix.org)
4
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
5
+ #
6
+ # :title:SimpleJSON
7
+ class SimpleJSON
8
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'simple_json_helpers')
9
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'simple_json_db')
10
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'simple_json_bootstrap')
11
+
12
+ def self.echo(opts=nil)
13
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( Request.parse( Body.get(env))) }}
14
+ end
15
+
16
+ def self.set(opts=nil)
17
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.set( Request.parse( Body.get(env)))) }}
18
+ end
19
+
20
+ def self.add(opts=nil)
21
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.add( Request.parse( Body.get(env)))) }}
22
+ end
23
+
24
+ def self.delete(opts=nil)
25
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.delete( Request.parse( Body.get(env)))) }}
26
+ end
27
+
28
+ def self.get(opts=nil)
29
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.get( Request.parse( Body.get(env)))) }}
30
+ end
31
+
32
+ def self.query(opts=nil)
33
+ lambda { |env| CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.query( Request.parse( Body.get(env)))) }}
34
+ end
35
+
36
+ def self.rack_mock(m,data,opts=nil)
37
+ CrashProof.wrap { Bootstrap.config(opts); Response.generate( @@store.send( m, Request.parse( data))) }
38
+ end
39
+
40
+ end
@@ -0,0 +1,46 @@
1
+ class SimpleJSON
2
+ def self.bootstrap(opts)
3
+ Bootstrap.config(opts)
4
+ end
5
+ # ///////////////////////////////////////////////////////////////////////////////////////
6
+ #
7
+ # Author:: lp (mailto:lp@spiralix.org)
8
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
9
+ #
10
+ # :title:SimpleJSON::Bootstrap
11
+ module Bootstrap
12
+ def self.config(opts)
13
+ unless opts.nil?
14
+ opts = eval(File.open(opts, 'r').read) if opts.is_a?(String)
15
+ if opts.is_a?(Hash)
16
+ change = Hash.new
17
+ if opts['AMAZON_ACCESS_KEY_ID'] and ENV['AMAZON_ACCESS_KEY_ID'] != opts['AMAZON_ACCESS_KEY_ID']
18
+ ENV['AMAZON_ACCESS_KEY_ID'] = opts['AMAZON_ACCESS_KEY_ID']
19
+ change[:@access_key_id] = opts['AMAZON_ACCESS_KEY_ID']
20
+ end
21
+ if opts['AMAZON_SECRET_ACCESS_KEY'] and ENV['AMAZON_SECRET_ACCESS_KEY'] != opts['AMAZON_SECRET_ACCESS_KEY']
22
+ ENV['AMAZON_SECRET_ACCESS_KEY'] = opts['AMAZON_SECRET_ACCESS_KEY']
23
+ change[:@secret_access_key] = opts['AMAZON_SECRET_ACCESS_KEY']
24
+ end
25
+
26
+ if SimpleJSON.class_variables.include?('@@store') and ( ! change.empty? or opts['AMAZON_DOMAIN'])
27
+ store = SimpleJSON.class_eval { class_variable_get(:@@store) }
28
+ if ! change.empty?
29
+ sdb = store.instance_variable_get(:@sdb)
30
+ change.each do |k,v|
31
+ sdb.instance_variable_set(k,v)
32
+ end
33
+ end
34
+ if opts['AMAZON_DOMAIN']
35
+ store.instance_variable_set(:@domain,opts['AMAZON_DOMAIN'])
36
+ store.instance_eval('create_domain unless domain_exist?')
37
+ end
38
+ else
39
+ store = SimpleJSON::DB.new(opts['AMAZON_DOMAIN']) if opts['AMAZON_DOMAIN']
40
+ SimpleJSON.class_eval { class_variable_set(:@@store, store) }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,108 @@
1
+ class SimpleJSON
2
+ # ///////////////////////////////////////////////////////////////////////////////////////
3
+ #
4
+ # Author:: lp (mailto:lp@spiralix.org)
5
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
6
+ #
7
+ # :title:SimpleJSON::DB
8
+ class DB
9
+ require 'aws_sdb'
10
+
11
+ # id generated in client?
12
+ # in js = < '' + date.getTime() + Math.floor(Math.random()*1000) >
13
+ def initialize(domain)
14
+ @domain = domain
15
+ @sdb = AwsSdb::Service.new(:logger=>LogDuck.new)
16
+ create_domain unless domain_exist?
17
+ end
18
+
19
+ def set(data)
20
+ # in: data = {'id' => {:key1 => value1, :key2 => value2}}
21
+ # out: data = {'id' => true} || {'id' => {"error"=>"message"}}
22
+ parse(data) {|k,v| @sdb.put_attributes(@domain,k,v) }
23
+ end
24
+
25
+ def add(data)
26
+ # in: data = {'id' => {:key1 => value1, :key2 => value2}}
27
+ # out: data = {'id' => true} || {'id' => {"error"=>"message"}}
28
+ parse(data) {|k,v| @sdb.put_attributes(@domain,k,v,false) }
29
+ end
30
+
31
+ def get(data)
32
+ # in: data = {'id' => {:key1 => nil, :key2 => nil}}
33
+ # out: data = {'id' => {:key1 => value1, :key2 => value2}}
34
+ parse(data) { |k,v| query_get(k,v) }
35
+ end
36
+
37
+ def delete(data)
38
+ # in: data = {'id' => nil}
39
+ # out: data = {'id' => true}
40
+ parse(data) {|k,v| @sdb.delete_attributes(@domain,k) }
41
+ end
42
+
43
+ def query(data)
44
+ # in: data = {'query' => {:key1 => nil, :key2 => nil}}
45
+ # out: data = {'id' => {:key1 => value1, :key2 => value2}}
46
+ parse(data) {|k,v| query_domain(k,v) }
47
+ end
48
+
49
+ private
50
+
51
+ def parse(data,&block)
52
+ data.to_a.inject(Hash.new) do |mem,obj|
53
+ begin
54
+ r_data = block.call(obj[0],obj[1])
55
+ if r_data.nil?
56
+ mem[obj[0]] = true
57
+ elsif r_data.is_a?(Hash)
58
+ mem[obj[0]] = r_data
59
+ else
60
+ mem[obj[0]] = [false, "SDB Error. Unable to 'put attributes'"]
61
+ end
62
+ rescue => ex
63
+ mem[obj[0]] = [false, "#{ex.class}: #{ex.message}"]
64
+ end
65
+ mem
66
+ end
67
+ end
68
+
69
+ def query_domain(k,v)
70
+ ids = get_id(k)
71
+ if v.nil?
72
+ return ids.inject(Hash.new) { |mem,obj| mem[obj] = nil; mem }
73
+ else
74
+ if ids.is_a?(Array)
75
+ ids.inject(Hash.new) { |mem,id| mem[id] = query_get(id,v); mem }
76
+ else
77
+ return [false,"Query Failed: v:#{v.inspect} | r:#{r_ids.inspect}"]
78
+ end
79
+ end
80
+ end
81
+
82
+ def query_get(k,v)
83
+ r_data = @sdb.get_attributes(@domain,k)
84
+ if v.nil? || v == true || v == false
85
+ r_data
86
+ else
87
+ v.to_a.inject(Hash.new) { |mem,obj| mem[obj[0]] = r_data[obj[0]]; mem }
88
+ end
89
+ end
90
+
91
+ def get_id(q,token=nil)
92
+ ids,q_id = @sdb.query(@domain,q,token)
93
+ ids += get_id(q,q_id) if q_id != ''
94
+ return ids
95
+ end
96
+
97
+ def create_domain
98
+ @sdb.create_domain(@domain)
99
+ end
100
+
101
+ def domain_exist?
102
+ domains, dummy = @sdb.list_domains
103
+ domains.include?(@domain)
104
+ end
105
+
106
+ class LogDuck; def debug(*args); end; end
107
+ end
108
+ end
@@ -0,0 +1,56 @@
1
+ class SimpleJSON
2
+ require 'json'
3
+ # ///////////////////////////////////////////////////////////////////////////////////////
4
+ #
5
+ # Author:: lp (mailto:lp@spiralix.org)
6
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
7
+ #
8
+ # :title:SimpleJSON::Request
9
+ module Request
10
+ def self.parse(data)
11
+ JSON.parse(data)
12
+ end
13
+ end
14
+
15
+ # ///////////////////////////////////////////////////////////////////////////////////////
16
+ #
17
+ # Author:: lp (mailto:lp@spiralix.org)
18
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
19
+ #
20
+ # :title:SimpleJSON::Response
21
+ module Response
22
+ def self.generate(data)
23
+ return [200, { 'Content-Type' => 'application/json' }, JSON.generate(data)]
24
+ end
25
+ end
26
+
27
+ # ///////////////////////////////////////////////////////////////////////////////////////
28
+ #
29
+ # Author:: lp (mailto:lp@spiralix.org)
30
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
31
+ #
32
+ # :title:SimpleJSON::Body
33
+ module Body
34
+ def self.get(env)
35
+ data = env['rack.input'].read
36
+ return data
37
+ end
38
+ end
39
+
40
+ # ///////////////////////////////////////////////////////////////////////////////////////
41
+ #
42
+ # Author:: lp (mailto:lp@spiralix.org)
43
+ # Copyright:: 2009 Louis-Philippe Perron - Released under the terms of the MIT license
44
+ #
45
+ # :title:SimpleJSON::CrashProof
46
+ module CrashProof
47
+ def self.wrap(&block)
48
+ begin
49
+ block.call
50
+ rescue
51
+ [200, { 'Content-Type' => 'application/json' }, "{\"ERROR\":{\"class\":\"#{$!.class}\",\"message\":\"#{$!.message}\"}}"]
52
+ end
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'json'
4
+
5
+ # require File.join( File.dirname( File.expand_path(__FILE__)), '..','aws-sdb','lib','aws_sdb')
6
+ require 'aws_sdb'
7
+ require File.join( File.dirname( File.expand_path(__FILE__)), '..','simple_json')
8
+
9
+ class TestSimpleJSONDB < Test::Unit::TestCase
10
+ BEGIN {
11
+ @@id = Time.now.to_i.to_s + rand(1000).to_s
12
+ }
13
+
14
+ def setup
15
+ SimpleJSON.bootstrap(File.join( File.dirname( File.expand_path(__FILE__)), '..','simple_json_config.rb'))
16
+ end
17
+
18
+ def test_01_bootload
19
+ @@sdb = AwsSdb::Service.new
20
+ config = eval(File.open(File.join( File.dirname( File.expand_path(__FILE__)), '..','simple_json_config.rb'), 'r').read)
21
+ assert(@@sdb.list_domains[0].include?(config['AMAZON_DOMAIN']), 'boot?')
22
+ end
23
+
24
+ def test_02_firstset
25
+ @@i_data = {@@id => {'name' => @@id, 'test' => 'done'}}
26
+
27
+ o_data = SimpleJSON.rack_mock(:set, JSON.generate(@@i_data))
28
+ assert(o_data.is_a?(Array),'return HTTP?')
29
+ assert(o_data[0] == 200,'HTTP 200?')
30
+ assert(o_data[1].is_a?(Hash),'HTTP Headers?')
31
+ assert(o_data[1]['Content-Type'] == 'application/json', 'JSON?')
32
+ assert(o_data[2].is_a?(String),'JSON String?')
33
+
34
+ p_data = JSON.parse(o_data[2])
35
+ assert(p_data.is_a?(Hash),'JSON Parse?')
36
+ assert(p_data.include?(@@id),'returned ID?')
37
+ assert(p_data[@@id] == true,'set is true?')
38
+ end
39
+
40
+ def test_03_get_one
41
+ t_data = {@@id => nil}
42
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate(t_data))[2])
43
+ @@i_data[@@id].each do |k,v|
44
+ assert(o_data[@@id][k][0] == v, "get <#{k}> in first set")
45
+ end
46
+ end
47
+
48
+ def test_04_get_one_attribute
49
+ t_data = {@@id => {'name' => nil}}
50
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate(t_data))[2])
51
+ assert(t_data[@@id].size == o_data[@@id].size, 'get one attribute')
52
+ end
53
+
54
+ def test_05_set_one_more
55
+ t_data = {@@id => {'more' => 'data'}}
56
+ o_data = JSON.parse( SimpleJSON.rack_mock(:set, JSON.generate(t_data))[2])
57
+ assert(o_data[@@id] == true,'set is true?')
58
+
59
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate({@@id => nil}))[2])
60
+ @@i_data[@@id].merge!(t_data[@@id])
61
+ @@i_data[@@id].each do |k,v|
62
+ assert(o_data[@@id][k][0] == v, "get <#{k}> in first get")
63
+ end
64
+ end
65
+
66
+ def test_06_set_overwrite
67
+ o_data = JSON.parse( SimpleJSON.rack_mock(:set, JSON.generate({@@id => {'more' => 'data2'}}))[2])
68
+ assert(o_data[@@id] == true,'set is true?')
69
+
70
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate({@@id => nil}))[2])
71
+ assert(o_data[@@id]['more'].size == 1, 'overwrite data?')
72
+ end
73
+
74
+ def test_07_set_append
75
+ o_data = JSON.parse( SimpleJSON.rack_mock(:add, JSON.generate({@@id => {'more' => 'data3'}}))[2])
76
+ assert(o_data[@@id] == true,'set is true?')
77
+
78
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate({@@id => nil}))[2])
79
+ assert(o_data[@@id]['more'].size == 2, 'append data?')
80
+ end
81
+
82
+ def test_08_query_all
83
+ o_data = JSON.parse( SimpleJSON.rack_mock(:query, JSON.generate({'' => nil}))[2])
84
+ assert(o_data.is_a?(Hash),'query all is not a Hash?')
85
+ assert(o_data.size == 1,'query all Hash contains more than one key value pair')
86
+ assert(o_data.has_key?(''),'query all Hash does not contain the query string as key')
87
+ assert(o_data[''].to_a.map { |obj| obj[1] }.uniq.size == 1,'query all Hash does not bring id => nil? ')
88
+ end
89
+
90
+ def test_09_query_specific_all
91
+ query = "['name' = '#{@@id}']"
92
+ t_data = {query => true}
93
+ o_data = JSON.parse( SimpleJSON.rack_mock(:query, JSON.generate(t_data))[2])
94
+ assert(o_data.is_a?(Hash),'query specific all is not a Hash?')
95
+ assert(o_data.size == 1,'query specific all Hash contains more than one key value pair')
96
+ assert(o_data.has_key?(query),'query specific all Hash does not contain the query string as key')
97
+ assert(o_data[query].has_key?(@@id))
98
+ assert(o_data[query][@@id].size == @@i_data[@@id].size)
99
+ end
100
+
101
+ def test_10_query_specific_none
102
+ query = "['name' = '#{@@id}']"
103
+ t_data = {query => nil}
104
+ o_data = JSON.parse( SimpleJSON.rack_mock(:query, JSON.generate(t_data))[2])
105
+ assert(o_data.is_a?(Hash))
106
+ assert(o_data.size == 1)
107
+ assert(o_data.has_key?(query))
108
+ assert(o_data[query].has_key?(@@id))
109
+ assert(o_data[query][@@id].nil?)
110
+ end
111
+
112
+ def test_11_query_specific_some
113
+ query = "['name' = '#{@@id}']"
114
+ t_data = {query => {'name' => nil, 'more' => nil}}
115
+ o_data = JSON.parse( SimpleJSON.rack_mock(:query, JSON.generate(t_data))[2])
116
+ assert(o_data.is_a?(Hash))
117
+ assert(o_data.size == 1)
118
+ assert(o_data.has_key?(query))
119
+ assert(o_data[query].has_key?(@@id))
120
+ assert(o_data[query][@@id].size == 2)
121
+ end
122
+
123
+ def test_12_delete
124
+ o_data = JSON.parse( SimpleJSON.rack_mock(:delete, JSON.generate({@@id => nil}))[2])
125
+ assert(o_data.is_a?(Hash))
126
+ assert(o_data.size == 1)
127
+ assert(o_data.has_key?(@@id))
128
+ assert(o_data[@@id] == true)
129
+
130
+ o_data = JSON.parse( SimpleJSON.rack_mock(:get, JSON.generate({@@id => nil}))[2])
131
+ assert(o_data.is_a?(Hash))
132
+ assert(o_data.size == 1)
133
+ assert(o_data.has_key?(@@id))
134
+ assert(o_data[@@id].is_a?(Hash))
135
+ assert(o_data[@@id].empty?)
136
+ end
137
+
138
+ def test_13_other_config
139
+ domain = 'other_test_domain'
140
+ o_data = JSON.parse( SimpleJSON.rack_mock(:delete, JSON.generate({@@id => nil}), {'AMAZON_DOMAIN' => domain})[2])
141
+ assert(@@sdb.list_domains[0].include?(domain), 'other domain')
142
+ @@sdb.delete_domain(domain)
143
+ end
144
+
145
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple-json
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Louis-Philippe Perron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-01 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: aws-sdb
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.0.0
44
+ version:
45
+ description: SimpleJSON is a Rack based JSON frontend server to Amazon's SimpleDB, designed to be used as a simple backend data servers for client side web apps.
46
+ email: lp@spiralix.org
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files: []
52
+
53
+ files:
54
+ - lib/simple_json.rb
55
+ - lib/simple_json_bootstrap.rb
56
+ - lib/simple_json_db.rb
57
+ - lib/simple_json_helpers.rb
58
+ - test/tc_simple_json.rb
59
+ has_rdoc: true
60
+ homepage: http://simple-json.rubyforge.org/
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options: []
65
+
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements:
81
+ - an Amazon Web Service account to access SimpleDB
82
+ rubyforge_project: SimpleJSON
83
+ rubygems_version: 1.3.5
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Rack app, JSON frontend to Amazon's SimpleDB
87
+ test_files:
88
+ - test/tc_simple_json.rb