ddp-server 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96ce685d7db62b76f32cdd90b85c204d7893742d
4
- data.tar.gz: 610b8c0c7175efb662fa4b3116c0a5f7d37d0261
3
+ metadata.gz: 950aa0c33840f83ddc2708dba9b8be77b6bbed71
4
+ data.tar.gz: 602b119c4b1c339c3f5395bc2ce23aa69d152d83
5
5
  SHA512:
6
- metadata.gz: 07322599a18df3dd8e9c8c61280cc72c97d3c24afd5b95c2b8e64482d510a75c17abcbd61af5dcaac48a70130aad14f8c0bae06b398f5b6e3b1b82e684685a01
7
- data.tar.gz: fa7ebf54092ca0f93e413c7e13b3dc7a600c73ab5e4a5716594da9d4d3fb3a61d341602493d2072371b92dab6f9e53a94dd2a808a4438d7a5b8e365afb9fe0d8
6
+ metadata.gz: 7e2a9fb98f64ed089af30ee95f06bda60d29c61d69955ef62e90b78819f6e88033c2b413fd1bf2c54a9bb39671ed77aca511a475d5ff095131a61964da132f72
7
+ data.tar.gz: 15cebf56397143b80badf733327f2c6a17850ed8789c1f667d8d0d53d15a01a9dd92f6c505c942f093042f03bc8951207db61c6978fd655cba118ad8f6d99695
data/.rubocop.yml CHANGED
@@ -6,3 +6,5 @@ Metrics/LineLength:
6
6
  Max: 120
7
7
  Style/SignalException:
8
8
  Enabled: false
9
+ Style/MultilineOperationIndentation:
10
+ Enabled: false
data/lib/ddp/ejson.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'json'
2
+ require 'base64'
3
+
4
+ module DDP
5
+ # EJSON is a way of embedding more than the built-in JSON types in JSON.
6
+ # It supports all types built into JSON as plain JSON, plus some custom
7
+ # types identified by a key prefixed with '$'.
8
+ class EJSON
9
+ def self.parse(string)
10
+ parsed = JSON.parse string
11
+
12
+ deserialize(parsed)
13
+ end
14
+
15
+ def self.generate(object)
16
+ JSON.generate as_ejson(object)
17
+ end
18
+
19
+ def self.deserialize(object)
20
+ if object.is_a? Hash
21
+ deserialize_hash(object)
22
+ elsif object.is_a? Array
23
+ object.map { |e| deserialize(e) }
24
+ else
25
+ object
26
+ end
27
+ end
28
+
29
+ def self.as_ejson(object)
30
+ if object.respond_to? :as_ejson
31
+ object.as_ejson
32
+ elsif object.is_a? Hash
33
+ hash_as_ejson(object)
34
+ elsif object.is_a? Array
35
+ object.map { |i| as_ejson(i) }
36
+ else
37
+ object
38
+ end
39
+ end
40
+
41
+ # Hashes can contain keys that need to be escaped
42
+ def self.hash_as_ejson(hash)
43
+ result = hash.map do |k, v|
44
+ if k.is_a?(String) && k[0] == '$'
45
+ ['$escape', { k => as_ejson(v) }]
46
+ else
47
+ [k, as_ejson(v)]
48
+ end
49
+ end
50
+ Hash[result]
51
+ end
52
+
53
+ def self.deserialize_hash(hash)
54
+ deserialize_operation(hash) || hash.map do |k, v|
55
+ [k, deserialize(v)]
56
+ end.to_h
57
+ end
58
+
59
+ def self.deserialize_operation(hash)
60
+ if hash['$escape']
61
+ return deserialize_escape(hash['$escape'])
62
+ elsif hash['$date']
63
+ return Time.at(hash['$date'] / 1000.0)
64
+ elsif hash['$binary']
65
+ return Base64.decode64(hash['$binary'])
66
+ elsif hash['$type']
67
+ return deserialize_type(hash)
68
+ end
69
+ false
70
+ end
71
+
72
+ def self.deserialize_escape(hash)
73
+ hash.map do |k, v|
74
+ [k, deserialize(v)]
75
+ end.to_h
76
+ end
77
+
78
+ def self.deserialize_type(hash)
79
+ klass = @classes[hash['$type']]
80
+ if klass
81
+ klass.from_ejson(hash['$value'])
82
+ else
83
+ raise UnknownTypeError, "Don't know how to deserialize #{hash['$type']}"
84
+ end
85
+ end
86
+
87
+ def self.add_serializable_class(klass)
88
+ @classes ||= {}
89
+ @classes[klass.name] = klass
90
+ end
91
+
92
+ def self.rename_serializable_class(klass, name)
93
+ @classes.delete(klass.name)
94
+ @classes[name] = klass
95
+ end
96
+
97
+ # Classes can include this module to be picked up by the EJSON parser
98
+ module Serializable
99
+ def self.extended(klass)
100
+ EJSON.add_serializable_class(klass)
101
+ end
102
+
103
+ def ejson_type_name(name)
104
+ EJSON.rename_serializable_class(self, name)
105
+ end
106
+
107
+ def from_ejson(_object)
108
+ raise InvalidSerializableClassError, "Class #{name} must override from_ejson."
109
+ end
110
+ end
111
+
112
+ # Raised when parsing an unknown type
113
+ class UnknownTypeError < StandardError
114
+ end
115
+
116
+ # Raised when serializable class does not implement from_ejson
117
+ class InvalidSerializableClassError < StandardError
118
+ end
119
+ end
120
+ end
121
+
122
+ # Builtin EJSON types:
123
+ class Time
124
+ def as_ejson
125
+ # milliseconds since epoch
126
+ { '$date' => (to_f * 1000).to_i }
127
+ end
128
+ end
data/lib/ddp/server.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'ddp/server/version'
2
2
  require 'ddp/server/protocol'
3
3
  require 'celluloid/websocket/rack'
4
- require 'json'
4
+ require 'ddp/ejson'
5
5
  require 'securerandom'
6
6
 
7
7
  module DDP
@@ -15,11 +15,11 @@ module DDP
15
15
  end
16
16
 
17
17
  def read_message
18
- JSON.parse read
18
+ EJSON.parse read
19
19
  end
20
20
 
21
21
  def write_message(message)
22
- write JSON.generate(message)
22
+ write EJSON.generate(message)
23
23
  end
24
24
  end
25
25
  end
@@ -1,6 +1,6 @@
1
1
  module DDP
2
2
  # We use semantic versioning
3
3
  module Server
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
6
6
  end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+ require 'ddp/ejson'
3
+ require 'json'
4
+ require 'base64'
5
+
6
+ # Tests in DDP Module
7
+ module DDP
8
+ describe EJSON do
9
+ describe 'generate' do
10
+ it 'generates normal json for simple structures' do
11
+ [
12
+ { 'a' => 'b' },
13
+ ['a'],
14
+ { 'a' => [1] }
15
+ ].each do |example|
16
+ expect(EJSON.generate(example)).to eq(JSON.generate(example))
17
+ end
18
+ end
19
+
20
+ it 'escapes keys that start with a dollar sign' do
21
+ example = { '$bla' => 'value' }
22
+ ejson = EJSON.generate(example)
23
+ expect(JSON.parse(ejson)['$escape']).to eq('$bla' => 'value')
24
+ end
25
+
26
+ it 'generates a special type for dates' do
27
+ time = Time.now
28
+ ms_since_epoch = (time.to_f * 1000).to_i
29
+ ejson = EJSON.generate('date' => time)
30
+ expect(JSON.parse(ejson)['date']).to eq('$date' => ms_since_epoch)
31
+ end
32
+ end
33
+
34
+ describe 'parse' do
35
+ it 'parses generic json' do
36
+ [
37
+ { 'a' => 'b' },
38
+ ['a'],
39
+ { 'a' => [1] }
40
+ ].each do |example|
41
+ json = JSON.generate(example)
42
+ expect(EJSON.parse(json)).to eq(example)
43
+ end
44
+ end
45
+
46
+ it 'parses an escaped ejson' do
47
+ example = { '$escape' => { '$date' => 'a' } }
48
+ ejson = JSON.generate(example)
49
+ expect(EJSON.parse(ejson)).to eq('$date' => 'a')
50
+ end
51
+
52
+ it 'parses a date ejson' do
53
+ time = Time.now
54
+ ms_since_epoch = (time.to_f * 1000).to_i
55
+ example = { 'date' => { '$date' => ms_since_epoch } }
56
+ ejson = JSON.generate(example)
57
+ expect(EJSON.parse(ejson)['date'].to_s).to eq(time.to_s)
58
+ end
59
+
60
+ it 'parses a binary ejson' do
61
+ example = { '$binary' => Base64.encode64('Hello World') }
62
+ ejson = JSON.generate(example)
63
+ expect(EJSON.parse(ejson)).to eq('Hello World')
64
+ end
65
+
66
+ it 'parses custom types' do
67
+ # Test class
68
+ class A
69
+ extend EJSON::Serializable
70
+
71
+ ejson_type_name 'A'
72
+
73
+ def as_ejson
74
+ { '$type' => 'A', '$value' => '1234' }
75
+ end
76
+
77
+ def self.from_ejson(object)
78
+ object.to_i
79
+ end
80
+ end
81
+
82
+ ejson = EJSON.generate(A.new)
83
+ expect(EJSON.parse(ejson)).to eq(1234)
84
+ end
85
+
86
+ it 'raises an exception when parsing an unknown type' do
87
+ ejson = JSON.generate('$type' => 'B', '$value' => '1234')
88
+ expect do
89
+ EJSON.parse(ejson)
90
+ end.to raise_error(EJSON::UnknownTypeError)
91
+ end
92
+
93
+ it 'raises an exception when a serializable class does not override from_ejson' do
94
+ expect do
95
+ # Test class
96
+ class C
97
+ extend EJSON::Serializable
98
+ end
99
+
100
+ C.from_ejson(false)
101
+ end.to raise_error(EJSON::InvalidSerializableClassError)
102
+ end
103
+ end
104
+ end
105
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddp-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tinco Andringa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-23 00:00:00.000000000 Z
11
+ date: 2015-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -68,12 +68,14 @@ files:
68
68
  - Rakefile
69
69
  - ddp-server.gemspec
70
70
  - examples/rack_example.ru
71
+ - lib/ddp/ejson.rb
71
72
  - lib/ddp/server.rb
72
73
  - lib/ddp/server/protocol.rb
73
74
  - lib/ddp/server/protocol/data.rb
74
75
  - lib/ddp/server/protocol/heartbeat.rb
75
76
  - lib/ddp/server/protocol/rpc.rb
76
77
  - lib/ddp/server/version.rb
78
+ - spec/ejson_spec.rb
77
79
  - spec/spec_helper.rb
78
80
  homepage: https://github.com/d-snp/ruby-ddp-server
79
81
  licenses:
@@ -100,4 +102,5 @@ signing_key:
100
102
  specification_version: 4
101
103
  summary: DDP Protocol server for implementing Ruby DDP backends
102
104
  test_files:
105
+ - spec/ejson_spec.rb
103
106
  - spec/spec_helper.rb