jinx-json 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/History.md +4 -0
- data/lib/jinx/json/date.rb +32 -0
- data/lib/jinx/json/deserializable.rb +17 -0
- data/lib/jinx/json/deserializer.rb +30 -3
- data/lib/jinx/json/serializable.rb +88 -0
- data/lib/jinx/json/set.rb +0 -0
- data/lib/jinx/json/state.rb +24 -0
- data/lib/jinx/json/version.rb +1 -1
- data/spec/serializer_spec.rb +27 -19
- data/spec/spec_helper.rb +2 -2
- metadata +9 -8
- data/Gemfile.lock +0 -43
- data/lib/jinx/json/serializer.rb +0 -90
- data/lib/jinx/json.rb +0 -1
data/Gemfile
CHANGED
@@ -4,6 +4,6 @@ gemspec
|
|
4
4
|
group :development do
|
5
5
|
gem 'ruby-debug'
|
6
6
|
# Uncomment to use a local gem.
|
7
|
-
gem 'jinx', :path => File.dirname(__FILE__) + '/../core'
|
7
|
+
#gem 'jinx', :path => File.dirname(__FILE__) + '/../core'
|
8
8
|
gem 'jinx-json', :path => File.dirname(__FILE__), :require => 'jinx/json'
|
9
9
|
end
|
data/History.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
This history lists major release themes. See the GitHub commits (https://github.com/jinx/json)
|
2
2
|
for change details.
|
3
3
|
|
4
|
+
2.1.2 / 2012-06-12
|
5
|
+
------------------
|
6
|
+
* Support caSmall web service.
|
7
|
+
|
4
8
|
2.1.1 / 2012-04-13
|
5
9
|
------------------
|
6
10
|
* Initial public release spun off from caruby/core.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'java'
|
3
|
+
|
4
|
+
class DateTime
|
5
|
+
# @param [String] json the JSON to deserialize
|
6
|
+
# @return [Date] the deserialized object
|
7
|
+
def self.json_create(json)
|
8
|
+
parse(json['data'])
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds JSON serialization to Ruby Date.
|
12
|
+
#
|
13
|
+
# @param args the JSON serialization options
|
14
|
+
# @return [String] the serialized date
|
15
|
+
def to_json(*args)
|
16
|
+
{
|
17
|
+
'json_class' => self.class.name,
|
18
|
+
'data' => to_s
|
19
|
+
}.to_json(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Java::JavaUtil::Date
|
24
|
+
# Adds JSON serialization to Java Date.
|
25
|
+
# The JSON is deserialized as a Ruby Date.
|
26
|
+
#
|
27
|
+
# @param args the JSON serialization options
|
28
|
+
# @return [String] the serialized date
|
29
|
+
def to_json(*args)
|
30
|
+
to_ruby_date.to_json(*args)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'jinx/helpers/hashable'
|
2
|
+
|
3
|
+
module Jinx
|
4
|
+
module JSON
|
5
|
+
# This Deserializable mix-in adds a {Deserializer} to each class which extends this module.
|
6
|
+
module Deserializable
|
7
|
+
def included(other)
|
8
|
+
super
|
9
|
+
if Class === other then
|
10
|
+
other.extend(Deserializer)
|
11
|
+
else
|
12
|
+
other.extend(Deserializable)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,16 +1,43 @@
|
|
1
1
|
require 'jinx/helpers/hashable'
|
2
2
|
|
3
|
+
module JSON
|
4
|
+
# Bumps the default JSON nesting level to 100.
|
5
|
+
def self.parse(source, opts={})
|
6
|
+
visited = Thread.current[:jinx_json_deserialized] ||= {}
|
7
|
+
opts = opts.merge(:max_nesting => 100) unless opts.has_key?(:max_nesting)
|
8
|
+
begin
|
9
|
+
Parser.new(source, opts).parse
|
10
|
+
ensure
|
11
|
+
visited.clear
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
3
16
|
module Jinx
|
4
17
|
module JSON
|
5
18
|
# JSON => {Jinx::Resource} deserializer.
|
6
19
|
#
|
7
|
-
# This Deserializer reconstitutes a jinxed object from a JSON string built
|
20
|
+
# This Deserializer reconstitutes a jinxed object from a JSON string built
|
21
|
+
# by a {Serializable}.
|
8
22
|
module Deserializer
|
9
23
|
# @param [String] json the JSON to deserialize
|
10
24
|
# @return [Jinx::Resource] the deserialized object
|
11
25
|
def json_create(json)
|
12
|
-
#
|
13
|
-
|
26
|
+
# the thread-local oid => object hash
|
27
|
+
visited = Thread.current[:jinx_json_deserialized]
|
28
|
+
# the payload
|
29
|
+
vh = json['data']
|
30
|
+
# the oid
|
31
|
+
oid = vh.delete('object_id')
|
32
|
+
# If the object has already been built, then return that object.
|
33
|
+
ref = visited[oid]
|
34
|
+
if ref then
|
35
|
+
# Fill in a place-holder with content.
|
36
|
+
ref.merge_attributes(vh) unless vh.empty?
|
37
|
+
return ref
|
38
|
+
end
|
39
|
+
# Make the new object from the attribute => value hash.
|
40
|
+
visited[oid] = new(vh)
|
14
41
|
end
|
15
42
|
end
|
16
43
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'jinx/json/state'
|
3
|
+
require 'jinx/json/collection'
|
4
|
+
require 'jinx/json/date'
|
5
|
+
require 'jinx/json/deserializable'
|
6
|
+
require 'jinx/json/deserializer'
|
7
|
+
|
8
|
+
module Jinx
|
9
|
+
module JSON
|
10
|
+
# Serializable is the JSON serializer {Jinx::Resource} mix-in.
|
11
|
+
#
|
12
|
+
# {#to_json} creates a JSON string from a jinxed object. The JSON payload is
|
13
|
+
# suitable for transfer to a remote {Deserializer}. This {Serializable} module
|
14
|
+
# is included directly in the jinxed application domain module after
|
15
|
+
# including {Jinx::Resource}, e.g.:
|
16
|
+
# module MyApp
|
17
|
+
# include Jinx::JSON::Serializable, Jinx:Resource
|
18
|
+
# which includes +Resource+ followed by the +Serializable+.
|
19
|
+
#
|
20
|
+
# The module which includes Serializable is extended with the {Importer}, which
|
21
|
+
# enables deserialization of the serialized JSON.
|
22
|
+
#
|
23
|
+
# The JSON payload is built as follows:
|
24
|
+
# * non-domain properties are serialized
|
25
|
+
# * dependent references are serialized recursively
|
26
|
+
# * independent reference primary and secondary key content is serialized
|
27
|
+
# * circular references are precluded by truncating the reference with a
|
28
|
+
# surrogate object id
|
29
|
+
module Serializable
|
30
|
+
def self.included(other)
|
31
|
+
super
|
32
|
+
if Class === other then
|
33
|
+
other.extend(Deserializer)
|
34
|
+
else
|
35
|
+
other.extend(Deserializable)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [State, Hash, nil] state the JSON state or serialization options
|
40
|
+
# @return [String] the JSON representation of this {Jinx::Resource}
|
41
|
+
def to_json(state=nil)
|
42
|
+
# Make a new State from the options if this is a top-level call.
|
43
|
+
state = State.for(state) unless State === state
|
44
|
+
# the JSON content
|
45
|
+
{
|
46
|
+
'json_class' => json_class_name,
|
47
|
+
'data' => json_value_hash(state.visited)
|
48
|
+
}.to_json(state)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# The JSON class name must be scoped by the Resource package module, not the
|
54
|
+
# Java package, in order to recognize the Jinx::Resource JSON hooks.
|
55
|
+
#
|
56
|
+
# @return [String] the class name qualified by the Resource package module name context
|
57
|
+
def json_class_name
|
58
|
+
[self.class.domain_module, self.class.name.demodulize].join('::')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Builds a serializable attribute => value hash with content as follows:
|
62
|
+
# * If this domain object has not yet been visited, then the hash includes all attributes,
|
63
|
+
# as well as the +Jinx::Resource#proxy_object_id+.
|
64
|
+
# * If this domain object has already been visited, then the hash includes only the
|
65
|
+
# +proxy_object_id+.
|
66
|
+
# Each +Set+ attribute value is converted to an array, since JSON does not serialize
|
67
|
+
# a +Set+ properly.
|
68
|
+
#
|
69
|
+
# The {Deserializer} is responsible for reconstituting the domain object graph from the
|
70
|
+
# serialized content.
|
71
|
+
#
|
72
|
+
# @param [<Resource>] visited the serialized objects
|
73
|
+
# @return [{Symbol => Object}] the serializable value hash
|
74
|
+
def json_value_hash(visited)
|
75
|
+
# If already visited, then the only content is the object id.
|
76
|
+
if visited.include?(self) then
|
77
|
+
return {:object_id => proxy_object_id}
|
78
|
+
end
|
79
|
+
visited << self
|
80
|
+
vh = value_hash
|
81
|
+
vh.each { |pa, v| vh[pa] = v.to_a if Set === v }
|
82
|
+
# Add the object id.
|
83
|
+
vh[:object_id] = proxy_object_id
|
84
|
+
vh
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
File without changes
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Jinx
|
4
|
+
module JSON
|
5
|
+
# This State module bumps the JSON default +:max_nesting+ to 100 and tracks
|
6
|
+
# the visited objects in a modified +JSON::Pure::Generator::State+.
|
7
|
+
module State
|
8
|
+
# @param [Hash, nil] opts the generator options
|
9
|
+
# return [State] the state for the given options
|
10
|
+
def self.for(opts)
|
11
|
+
opts ||= {}
|
12
|
+
opts[:max_nesting] ||= 100
|
13
|
+
state = ::JSON::Pure::Generator::State.from_state(opts)
|
14
|
+
class << state
|
15
|
+
# @return [<Jinx::JSON::Serializable>] the serialized objects
|
16
|
+
def visited
|
17
|
+
@visited ||= Set.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
state
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jinx/json/version.rb
CHANGED
data/spec/serializer_spec.rb
CHANGED
@@ -2,28 +2,28 @@ require 'spec/spec_helper'
|
|
2
2
|
|
3
3
|
module Family
|
4
4
|
describe Address do
|
5
|
-
it
|
6
|
-
|
7
|
-
j = JSON[
|
8
|
-
j.street1.should ==
|
9
|
-
j.city.should ==
|
10
|
-
j.state.should ==
|
5
|
+
it 'should serialize an address' do
|
6
|
+
addr = Address.new(:street1 => '16 Elm St', :city => 'Peoria', :state => 'IL')
|
7
|
+
j = JSON[addr.to_json]
|
8
|
+
j.street1.should == addr.street1
|
9
|
+
j.city.should == addr.city
|
10
|
+
j.state.should == addr.state
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
describe Household do
|
15
|
-
it
|
16
|
-
|
17
|
-
|
18
|
-
j = JSON[
|
19
|
-
j.address.street1.should ==
|
20
|
-
j.address.city.should ==
|
21
|
-
j.address.state.should ==
|
15
|
+
it 'should serialize the address' do
|
16
|
+
addr = Address.new(:street1 => '16 Elm St', :city => 'Peoria', :state => 'IL')
|
17
|
+
hshd = Household.new(:address => addr)
|
18
|
+
j = JSON[hshd.to_json]
|
19
|
+
j.address.street1.should == addr.street1
|
20
|
+
j.address.city.should == addr.city
|
21
|
+
j.address.state.should == addr.state
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
describe Parent do
|
26
|
-
it
|
26
|
+
it 'should serialize the children' do
|
27
27
|
p = Parent.new(:name => 'Oscar')
|
28
28
|
c = Child.new(:name => 'Beauregard', :parents => [p])
|
29
29
|
p.children << c
|
@@ -33,14 +33,22 @@ module Family
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe Child do
|
36
|
-
it
|
37
|
-
|
38
|
-
p = Parent.new(:name => 'Oscar', :household =>
|
36
|
+
it 'should serialize the parent' do
|
37
|
+
hshd = Household.new
|
38
|
+
p = Parent.new(:name => 'Oscar', :household => hshd)
|
39
39
|
c = Child.new(:name => 'Beauregard', :parents => [p])
|
40
40
|
p.children << c
|
41
41
|
j = JSON[c.to_json]
|
42
|
-
j.parents.first.name.
|
43
|
-
j.parents.first.household.
|
42
|
+
j.parents.first.name.should_not be nil
|
43
|
+
j.parents.first.household.should_not be nil
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'Date' do
|
47
|
+
it 'should serialize a Java date as a Ruby date' do
|
48
|
+
date = Java.now
|
49
|
+
j = JSON[date.to_json]
|
50
|
+
j.should be_within(0.005).of(date.to_ruby_date)
|
51
|
+
end
|
44
52
|
end
|
45
53
|
end
|
46
54
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,8 +6,8 @@ Bundler.require(:test, :development)
|
|
6
6
|
require Bundler.environment.specs.detect { |s| s.name == 'jinx' }.full_gem_path + '/examples/family/lib/family'
|
7
7
|
|
8
8
|
# Add serialization
|
9
|
-
require 'jinx/json/
|
9
|
+
require 'jinx/json/serializable'
|
10
10
|
|
11
11
|
module Family
|
12
|
-
include Jinx::JSON::
|
12
|
+
include Jinx::JSON::Serializable
|
13
13
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jinx-json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.1.
|
5
|
+
version: 2.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- OHSU
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
14
|
-
default_executable:
|
13
|
+
date: 2012-06-12 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: bundler
|
@@ -81,21 +80,22 @@ files:
|
|
81
80
|
- .rspec
|
82
81
|
- .yardopts
|
83
82
|
- Gemfile
|
84
|
-
- Gemfile.lock
|
85
83
|
- History.md
|
86
84
|
- LEGAL
|
87
85
|
- LICENSE
|
88
86
|
- README.md
|
89
87
|
- Rakefile
|
90
88
|
- jinx-json.gemspec
|
91
|
-
- lib/jinx/json.rb
|
92
89
|
- lib/jinx/json/collection.rb
|
90
|
+
- lib/jinx/json/date.rb
|
91
|
+
- lib/jinx/json/deserializable.rb
|
93
92
|
- lib/jinx/json/deserializer.rb
|
94
|
-
- lib/jinx/json/
|
93
|
+
- lib/jinx/json/serializable.rb
|
94
|
+
- lib/jinx/json/set.rb
|
95
|
+
- lib/jinx/json/state.rb
|
95
96
|
- lib/jinx/json/version.rb
|
96
97
|
- spec/serializer_spec.rb
|
97
98
|
- spec/spec_helper.rb
|
98
|
-
has_rdoc: yard
|
99
99
|
homepage: http://github.com/jinx/json
|
100
100
|
licenses:
|
101
101
|
- MIT
|
@@ -119,10 +119,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
requirements: []
|
120
120
|
|
121
121
|
rubyforge_project: jinx
|
122
|
-
rubygems_version: 1.
|
122
|
+
rubygems_version: 1.8.15
|
123
123
|
signing_key:
|
124
124
|
specification_version: 3
|
125
125
|
summary: Jinx JSON plug-in.
|
126
126
|
test_files:
|
127
127
|
- spec/serializer_spec.rb
|
128
128
|
- spec/spec_helper.rb
|
129
|
+
has_rdoc: yard
|
data/Gemfile.lock
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
jinx-json (2.1.1)
|
5
|
-
bundler
|
6
|
-
jinx
|
7
|
-
json_pure
|
8
|
-
|
9
|
-
PATH
|
10
|
-
remote: /Users/loneyf/workspace/jinx/core
|
11
|
-
specs:
|
12
|
-
jinx (2.1.1)
|
13
|
-
bundler
|
14
|
-
|
15
|
-
GEM
|
16
|
-
remote: http://rubygems.org/
|
17
|
-
specs:
|
18
|
-
columnize (0.3.6)
|
19
|
-
diff-lcs (1.1.3)
|
20
|
-
json_pure (1.6.3)
|
21
|
-
rake (0.9.2.2)
|
22
|
-
rspec (2.9.0)
|
23
|
-
rspec-core (~> 2.9.0)
|
24
|
-
rspec-expectations (~> 2.9.0)
|
25
|
-
rspec-mocks (~> 2.9.0)
|
26
|
-
rspec-core (2.9.0)
|
27
|
-
rspec-expectations (2.9.1)
|
28
|
-
diff-lcs (~> 1.1.3)
|
29
|
-
rspec-mocks (2.9.0)
|
30
|
-
ruby-debug (0.10.4)
|
31
|
-
columnize (>= 0.1)
|
32
|
-
ruby-debug-base (~> 0.10.4.0)
|
33
|
-
ruby-debug-base (0.10.4-java)
|
34
|
-
|
35
|
-
PLATFORMS
|
36
|
-
java
|
37
|
-
|
38
|
-
DEPENDENCIES
|
39
|
-
jinx!
|
40
|
-
jinx-json!
|
41
|
-
rake
|
42
|
-
rspec (>= 2.6)
|
43
|
-
ruby-debug
|
data/lib/jinx/json/serializer.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'jinx/json/collection'
|
3
|
-
|
4
|
-
module Jinx
|
5
|
-
module JSON
|
6
|
-
# {Jinx::Resource} => JSON serializer.
|
7
|
-
#
|
8
|
-
# This Serializer creates a JSON string from a jinxed object. The JSON payload is
|
9
|
-
# suitable for transfer to a remote {Deserializer}. This {Serializer} is included
|
10
|
-
# directly in the jinxed application domain module after including {Jinx::Resource},
|
11
|
-
# e.g.:
|
12
|
-
# module MyApp
|
13
|
-
# include Jinx::JSON::Serializer, Jinx:Resource
|
14
|
-
# which includes +Resource+ followed by the +Serializer+.
|
15
|
-
#
|
16
|
-
# The module which includes this Serializer is extended with the {Importer}, which
|
17
|
-
# enables deserialization of the serialized JSON.
|
18
|
-
#
|
19
|
-
# The JSON payload is built as follows:
|
20
|
-
# * non-domain properties are serialized
|
21
|
-
# * dependent references are serialized recursively
|
22
|
-
# * independent reference primary and secondary key content is serialized
|
23
|
-
module Serializer
|
24
|
-
# @param args the JSON serialization options
|
25
|
-
# @return [String] the JSON representation of this {Jinx::Resource}
|
26
|
-
def to_json(*args)
|
27
|
-
{
|
28
|
-
'json_class' => json_class_name,
|
29
|
-
'data' => json_value_hash
|
30
|
-
}.to_json(*args)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# The JSON class name must be scoped by the Resource package module, not the
|
36
|
-
# Java package, in order to recognize the Jinx::Resource JSON hooks.
|
37
|
-
#
|
38
|
-
# @return [String] the class name qualified by the Resource package module name context
|
39
|
-
def json_class_name
|
40
|
-
[self.class.domain_module, self.class.name.demodulize].join('::')
|
41
|
-
end
|
42
|
-
|
43
|
-
# Builds a serializable attribute => value hash. If _key_only _ is true, then only
|
44
|
-
# the key attributes are included in the hash. Otherwise, all properties are included
|
45
|
-
# in the hash.
|
46
|
-
#
|
47
|
-
# An independent or owner attribute value is a copy of the referenced domain object
|
48
|
-
# consisting of only the primary and secondary key attributes.
|
49
|
-
#
|
50
|
-
# @return [{Symbol => Object}] the serializable value hash
|
51
|
-
def json_value_hash
|
52
|
-
vh = value_hash(self.class.nondomain_attributes)
|
53
|
-
vh.merge!(value_hash(self.class.dependent_attributes))
|
54
|
-
self.class.independent_attributes.each do |ia|
|
55
|
-
ref = send(ia)
|
56
|
-
vh[ia] = json_independent_reference(ref) unless ref.nil_or_empty?
|
57
|
-
end
|
58
|
-
vh
|
59
|
-
end
|
60
|
-
|
61
|
-
# @param [Resource] the referenced object
|
62
|
-
# @return [Resource] a copy of the referenced object which only contains
|
63
|
-
# key property values
|
64
|
-
def json_independent_reference(ref)
|
65
|
-
return ref.map { |item| json_independent_reference(item) } if ref.collection?
|
66
|
-
vh = json_key_hash(ref)
|
67
|
-
ref.copy(vh) unless vh.empty?
|
68
|
-
end
|
69
|
-
|
70
|
-
# @param (see #json_independent_reference)
|
71
|
-
# @return [{Symbol => Object}, nil] the key attribute => value hash, or nil
|
72
|
-
# if there is no complete key
|
73
|
-
def json_key_hash(ref)
|
74
|
-
vh = ref.value_hash(ref.class.primary_key_attributes)
|
75
|
-
ref.class.secondary_key_attributes.each do |ka|
|
76
|
-
value = json_key_value(ref, ka)
|
77
|
-
vh[ka] = value if value
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# @param (see #json_independent_reference)
|
82
|
-
# @param [Symbol] the key value
|
83
|
-
# @return the key attribute value
|
84
|
-
def json_key_value(ref, attribute)
|
85
|
-
value = ref.send(attribute)
|
86
|
-
Resource === value ? json_independent_reference(value) : value
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
data/lib/jinx/json.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'json'
|