tent-canonical-json 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tent-canonical-json.gemspec
4
+ gemspec
5
+
6
+ gem 'json-pointer', :git => 'git://github.com/tent/json-pointer-ruby.git', :branch => 'master'
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are
5
+ met:
6
+
7
+ * Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above
10
+ copyright notice, this list of conditions and the following disclaimer
11
+ in the documentation and/or other materials provided with the
12
+ distribution.
13
+ * Neither the name of Apollic Software, LLC nor the names of its
14
+ contributors may be used to endorse or promote products derived from
15
+ this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # TentCanonicalJson [![Build Status](https://travis-ci.org/tent/tent-canonical-json-ruby.png)](https://travis-ci.org/tent/tent-canonical-json-ruby)
2
+
3
+ Derive Tent canonical json from post.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'tent-canonical-json'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install tent-canonical-json
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ post = Hash.new # replace with hash representation of post json
23
+ TentCanonicalJson.encode(post) # => canonical json string
24
+ ```
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = 'spec/**/*_spec.rb'
7
+ end
8
+ task :default => :spec
@@ -0,0 +1,154 @@
1
+ require "tent-canonical-json/version"
2
+ require "json-pointer"
3
+ require "yajl"
4
+
5
+ class TentCanonicalJson
6
+ REMOVE = %w[ /permissions /received_at /version/received_at /version/id ].freeze
7
+ REMOVE_MATCHING = {
8
+ "/mentions/*/public" => false
9
+ }.freeze
10
+ MOVE = {
11
+ "/original_entity" => "/entity",
12
+ "/mentions/*/original_entity" => "/mentions/{index}/entity",
13
+ "/version/parents/*/original_entity" => "/version/parents/{index}/entity"
14
+ }.freeze
15
+ REMOVE_EMPTY = %w[ /app /attachments /mentions /content /licenses /version/parents /version/message /version ].freeze
16
+ REMOVE_MATCHING_PATHS = {
17
+ "/version/parents/*/post" => "/id",
18
+ "/version/parents/*/entity" => "/entity",
19
+ "/mentions/*/post" => "/id",
20
+ "/mentions/*/entity" => "/entity"
21
+ }.freeze
22
+
23
+ def self.encode(post)
24
+ new(post).encode
25
+ end
26
+
27
+ def initialize(post)
28
+ @post = stringify_keys(post.dup)
29
+ end
30
+
31
+ def encode
32
+ cleanup
33
+ sorted_encode
34
+ end
35
+
36
+ private
37
+
38
+ def cleanup
39
+ REMOVE.each do |path|
40
+ pointer = JsonPointer.new(@post, path)
41
+ pointer.delete
42
+ end
43
+
44
+ MOVE.each_pair do |path, target_path|
45
+ pointer = JsonPointer.new(@post, path)
46
+ next unless pointer.exists?
47
+
48
+ if path =~ %r{/\*/}
49
+ pointer.value.each_with_index do |value, index|
50
+ next unless value
51
+ target_pointer = JsonPointer.new(@post, target_path.sub('{index}', index.to_s))
52
+ target_pointer.value = value
53
+ end
54
+ pointer.delete
55
+ else
56
+ target_pointer = JsonPointer.new(@post, target_path)
57
+ target_pointer.value = pointer.value if pointer.value
58
+ pointer.delete
59
+ end
60
+ end
61
+
62
+ REMOVE_MATCHING.each_pair do |path, value|
63
+ pointer = JsonPointer.new(@post, path)
64
+ next unless pointer.exists?
65
+
66
+ pointer.value.each_with_index do |pointer_value, index|
67
+ next unless pointer_value == value
68
+ delete_path = path.split('/')
69
+ delete_path.pop
70
+ delete_path = delete_path.join('/').sub('*', index.to_s)
71
+ delete_pointer = JsonPointer.new(@post, delete_path)
72
+ delete_pointer.delete
73
+ end
74
+ end
75
+
76
+ REMOVE_EMPTY.each do |path|
77
+ pointer = JsonPointer.new(@post, path)
78
+ pointer.delete if obj_empty?(pointer.value)
79
+ end
80
+
81
+ REMOVE_MATCHING_PATHS.each_pair do |path, other_path|
82
+ pointer = JsonPointer.new(@post, path)
83
+ next unless pointer.exists?
84
+ other_pointer = JsonPointer.new(@post, other_path)
85
+ next unless other_pointer.exists?
86
+
87
+ pointer.value.each_with_index do |value, index|
88
+ delete_pointer = JsonPointer.new(@post, path.sub('*', index.to_s))
89
+ delete_pointer.delete if value == other_pointer.value
90
+ end
91
+ end
92
+ end
93
+
94
+ def sorted_encode(hash=@post)
95
+ sorted_keys = hash.keys.sort
96
+ encoded_values = hash.inject({}) do |memo, (k,v)|
97
+ memo[k] = case v
98
+ when Hash
99
+ sorted_encode(v)
100
+ when Array
101
+ json = v.map { |i|
102
+ case i
103
+ when Hash
104
+ sorted_encode(i)
105
+ else
106
+ Yajl::Encoder.encode(i)
107
+ end
108
+ }.join(",")
109
+ "[#{json}]"
110
+ else
111
+ Yajl::Encoder.encode(v)
112
+ end
113
+ memo
114
+ end
115
+ json = sorted_keys.map do |key|
116
+ "#{key.to_s.inspect}:#{encoded_values[key]}"
117
+ end.join(",")
118
+ "{#{json}}"
119
+ end
120
+
121
+ def stringify_keys(hash)
122
+ hash.inject(Hash.new) do |new_hash, (key, val)|
123
+ new_hash[key.to_s] = case val
124
+ when Hash
125
+ stringify_keys(val)
126
+ when Array
127
+ val.map { |v|
128
+ case v
129
+ when Hash
130
+ stringify_keys(v)
131
+ else
132
+ v
133
+ end
134
+ }
135
+ else
136
+ val
137
+ end
138
+ new_hash
139
+ end
140
+ end
141
+
142
+ def obj_empty?(obj)
143
+ case obj
144
+ when Hash
145
+ obj.keys.empty?
146
+ when Array
147
+ obj.empty?
148
+ when String
149
+ obj == ""
150
+ when NilClass
151
+ true
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,3 @@
1
+ class TentCanonicalJson
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'bundler/setup'
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with :rspec do |c|
8
+ c.syntax = :expect
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ shared_examples "a canonical json encode method" do
2
+ it "encodes post" do
3
+ expect(described_class.encode(post)).to eq(encoded_post)
4
+ end
5
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+ require 'tent-canonical-json'
3
+ require 'support/canonical_json_encode_method_examples'
4
+
5
+ describe TentCanonicalJson do
6
+ describe ".encode" do
7
+ let(:time) { (Time.now.to_f * 1000).to_i }
8
+
9
+ context "with no removable members" do
10
+ let(:post) do
11
+ {
12
+ :id => "d8a3884345df0d6854d4bec893a4649e",
13
+ :entity => "https://entity.example.org",
14
+ :type => "https://example.com/types/foo/v0#",
15
+ :published_at => time,
16
+ :version => {
17
+ :parents => [
18
+ {
19
+ :version => "6438326334656235323631636239633861613938353565646436376431626431",
20
+ :entity => "https://other.example.org",
21
+ :post => "f4fa728bc8daf2a322e47ee4fc865a49",
22
+ },
23
+ {
24
+ :version => "6637666262613665303633366638393065353666626266333238336535323463",
25
+ }
26
+ ],
27
+ :published_at => time,
28
+ :message => "A short description of changes made.",
29
+ },
30
+ :mentions => [
31
+ {
32
+ :post => "23a5b25940d6f3a75748f1edb5628e80",
33
+ :entity => "https://foo.example.com",
34
+ },
35
+ {
36
+ :version => "3963663737376166653332336561393031306331333933356262396438316264",
37
+ :entity => "https://bar.example.org",
38
+ :post => "2c2ca13f2af0b5cb557867ec86e75451",
39
+ }
40
+ ],
41
+ :licenses => [
42
+ {
43
+ :url => "https://somelicense.example.org",
44
+ }
45
+ ],
46
+ :content => {
47
+ :zombies => {
48
+ :kills => 1200,
49
+ :escapes => 32,
50
+ :kills_without_wepon => 100,
51
+ },
52
+ :demons => {
53
+ :kills => 13,
54
+ :kills_without_wepon => 2,
55
+ :escapes => 43,
56
+ },
57
+ :vamps => {
58
+ :kills => 43,
59
+ :kills_without_wepon => 14,
60
+ :escapes => 200,
61
+ :tricked_into_sunlight => 13,
62
+ },
63
+ },
64
+ }
65
+ end
66
+
67
+ let(:encoded_post) do
68
+ %({"content":{"demons":{"escapes":43,"kills":13,"kills_without_wepon":2},"vamps":{"escapes":200,"kills":43,"kills_without_wepon":14,"tricked_into_sunlight":13},"zombies":{"escapes":32,"kills":1200,"kills_without_wepon":100}},"entity":"https://entity.example.org","id":"d8a3884345df0d6854d4bec893a4649e","licenses":[{"url":"https://somelicense.example.org"}],"mentions":[{"entity":"https://foo.example.com","post":"23a5b25940d6f3a75748f1edb5628e80"},{"entity":"https://bar.example.org","post":"2c2ca13f2af0b5cb557867ec86e75451","version":"3963663737376166653332336561393031306331333933356262396438316264"}],"published_at":#{time},"type":"https://example.com/types/foo/v0#","version":{"message":"A short description of changes made.","parents":[{"entity":"https://other.example.org","post":"f4fa728bc8daf2a322e47ee4fc865a49","version":"6438326334656235323631636239633861613938353565646436376431626431"},{"version":"6637666262613665303633366638393065353666626266333238336535323463"}],"published_at":#{time}}})
69
+ end
70
+
71
+ it_behaves_like "a canonical json encode method"
72
+ end
73
+
74
+ context "with removable members" do
75
+ let(:post) do
76
+ {
77
+ :id => "d8a3884345df0d6854d4bec893a4649e",
78
+ :entity => "https://entity.example.org",
79
+ :type => "https://example.com/types/foo/v0#",
80
+ :published_at => time,
81
+ :received_at => time,
82
+ :version => {
83
+ :id => "3963663737376166653332336561393031306331333933356262396438316264",
84
+ :received_at => time,
85
+ :published_at => time,
86
+ :message => "Hello World!",
87
+ },
88
+ :permissions => {
89
+ :public => true
90
+ },
91
+ }
92
+ end
93
+
94
+ let(:encoded_post) do
95
+ %({"entity":"https://entity.example.org","id":"d8a3884345df0d6854d4bec893a4649e","published_at":#{time},"type":"https://example.com/types/foo/v0#","version":{"message":"Hello World!","published_at":#{time}}})
96
+ end
97
+
98
+ it_behaves_like "a canonical json encode method"
99
+ end
100
+
101
+ context "with removable empty members" do
102
+ let(:post) do
103
+ {
104
+ :id => "d8a3884345df0d6854d4bec893a4649e",
105
+ :entity => "https://entity.example.org",
106
+ :type => "https://example.com/types/foo/v0#",
107
+ :published_at => time,
108
+ :version => {
109
+ :published_at => time,
110
+ :parents => [],
111
+ :message => "",
112
+ },
113
+ :content => {},
114
+ :mentions => [],
115
+ :licenses => [],
116
+ :app => {},
117
+ :attachments => [],
118
+ }
119
+ end
120
+
121
+ let(:encoded_post) do
122
+ %({"entity":"https://entity.example.org","id":"d8a3884345df0d6854d4bec893a4649e","published_at":#{time},"type":"https://example.com/types/foo/v0#","version":{"published_at":#{time}}})
123
+ end
124
+
125
+ it_behaves_like "a canonical json encode method"
126
+ end
127
+
128
+ context "with redundant members" do
129
+ let(:post) do
130
+ {
131
+ :id => "d8a3884345df0d6854d4bec893a4649e",
132
+ :entity => "https://entity.example.org",
133
+ :type => "https://example.com/types/foo/v0#",
134
+ :published_at => time,
135
+ :version => {
136
+ :published_at => time,
137
+ :message => "Hello World!",
138
+ :parents => [
139
+ {
140
+ :version => "6330626530393234306663623531643735623232616166663462663434323163",
141
+ :entity => "https://entity.example.org",
142
+ :post => "d8a3884345df0d6854d4bec893a4649e",
143
+ },
144
+ {
145
+ :version => "3933383165396136376161333631373531636561393031373863303934616436",
146
+ :entity => "https://entity.example.org",
147
+ }
148
+ ],
149
+ },
150
+ :mentions => [
151
+ {
152
+ :post => "d8a3884345df0d6854d4bec893a4649e",
153
+ :entity => "https://entity.example.org",
154
+ :version => "6136643936666130356662323433613133303832613231313935656264313236",
155
+ }
156
+ ],
157
+ }
158
+ end
159
+
160
+ let(:encoded_post) do
161
+ %({"entity":"https://entity.example.org","id":"d8a3884345df0d6854d4bec893a4649e","mentions":[{"version":"6136643936666130356662323433613133303832613231313935656264313236"}],"published_at":#{time},"type":"https://example.com/types/foo/v0#","version":{"message":"Hello World!","parents":[{"version":"6330626530393234306663623531643735623232616166663462663434323163"},{"version":"3933383165396136376161333631373531636561393031373863303934616436"}],"published_at":#{time}}})
162
+ end
163
+
164
+ it_behaves_like "a canonical json encode method"
165
+ end
166
+
167
+ context "with conditional removable members" do
168
+ let(:post) do
169
+ {
170
+ :id => "d8a3884345df0d6854d4bec893a4649e",
171
+ :entity => "https://entity.example.org",
172
+ :type => "https://example.com/types/foo/v0#",
173
+ :published_at => time,
174
+ :mentions => [
175
+ {
176
+ :public => false,
177
+ :entity => "https://foobar.example.com"
178
+ }
179
+ ]
180
+ }
181
+ end
182
+
183
+ let(:encoded_post) do
184
+ %({"entity":"https://entity.example.org","id":"d8a3884345df0d6854d4bec893a4649e","published_at":#{time},"type":"https://example.com/types/foo/v0#"})
185
+ end
186
+
187
+ it_behaves_like "a canonical json encode method"
188
+ end
189
+
190
+ context "with movable members" do
191
+ let(:post) do
192
+ {
193
+ :id => "d8a3884345df0d6854d4bec893a4649e",
194
+ :entity => "https://entity.example.org",
195
+ :original_entity => "https://baz.example.org",
196
+ :type => "https://example.com/types/foo/v0#",
197
+ :published_at => time,
198
+ :version => {
199
+ :parents => [
200
+ {
201
+ :version => "6438326334656235323631636239633861613938353565646436376431626431",
202
+ :entity => "https://other.example.org",
203
+ :original_entity => "https://thefirstother.example.org"
204
+ }
205
+ ],
206
+ :published_at => time,
207
+ :message => "A short description of changes made.",
208
+ },
209
+ :mentions => [
210
+ {
211
+ :entity => "https://foobar.example.com",
212
+ :original_entity => "https://bar.example.com"
213
+ }
214
+ ]
215
+ }
216
+ end
217
+
218
+ let(:encoded_post) do
219
+ %({"entity":"https://baz.example.org","id":"d8a3884345df0d6854d4bec893a4649e","mentions":[{"entity":"https://bar.example.com"}],"published_at":#{time},"type":"https://example.com/types/foo/v0#","version":{"message":"A short description of changes made.","parents":[{"entity":"https://thefirstother.example.org","version":"6438326334656235323631636239633861613938353565646436376431626431"}],"published_at":#{time}}})
220
+ end
221
+
222
+ it_behaves_like "a canonical json encode method"
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tent-canonical-json/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "tent-canonical-json"
8
+ gem.version = TentCanonicalJson::VERSION
9
+ gem.authors = ["Jesse Stuart"]
10
+ gem.email = ["jesse@jessestuart.ca"]
11
+ gem.description = %q{Derive Tent canonical json from post}
12
+ gem.summary = %q{Derive Tent canonical json from post}
13
+ gem.homepage = "https://github.com/tent/tent-canonical-json-ruby"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency "json-pointer"
21
+ gem.add_runtime_dependency "yajl-ruby"
22
+
23
+ gem.add_development_dependency 'rspec', '~> 2.11'
24
+ gem.add_development_dependency 'rake'
25
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tent-canonical-json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jesse Stuart
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json-pointer
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: yajl-ruby
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.11'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.11'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Derive Tent canonical json from post
79
+ email:
80
+ - jesse@jessestuart.ca
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE
88
+ - README.md
89
+ - Rakefile
90
+ - lib/tent-canonical-json.rb
91
+ - lib/tent-canonical-json/version.rb
92
+ - spec/spec_helper.rb
93
+ - spec/support/canonical_json_encode_method_examples.rb
94
+ - spec/tent-canonical-json_spec.rb
95
+ - tent-canonical-json.gemspec
96
+ homepage: https://github.com/tent/tent-canonical-json-ruby
97
+ licenses: []
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 1.8.23
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Derive Tent canonical json from post
120
+ test_files:
121
+ - spec/spec_helper.rb
122
+ - spec/support/canonical_json_encode_method_examples.rb
123
+ - spec/tent-canonical-json_spec.rb