xing-backend-specdoc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a4fb54b420e096821934f1179eb5c9f274f70deb
4
+ data.tar.gz: b0624e2586339eb47c2b6e18fc1165cb422dec75
5
+ SHA512:
6
+ metadata.gz: c70d8ad1026fa80d5d8545dfc76054ba26b530687e6455ee9f098d001c92d5148df66c7fd2f9755585362002a26167349dc7a73d85612e9c4af862e19e87bd0d
7
+ data.tar.gz: 98efef1c24410242f382363ec918a5b583db04a50aa17aa6584d978cf867848b7272e47938d0d16f3ab066757055c50154da7bee95b3f041f407556e05bce69f
@@ -0,0 +1,63 @@
1
+ require 'xing/specdoc/patterner'
2
+ require 'xing/specdoc/document'
3
+
4
+ module Xing
5
+ module SpecDoc
6
+ class ApiDoccer
7
+ def self.patterner
8
+ @patterner ||= Patterner.new(Rails.application.routes)
9
+ end
10
+
11
+ def initialize(example, request, response, patterner=nil)
12
+ @example = example
13
+ @request = request
14
+ @response = response
15
+ @patterner = patterner || ApiDoccer.patterner
16
+ end
17
+
18
+ attr_reader :example, :request, :response, :patterner
19
+
20
+ def output_path
21
+ @output_path ||= File::join( SpecDoc.response_target_dir, filename )
22
+ end
23
+
24
+ def base_filename
25
+ @base_filename ||=
26
+ begin
27
+ pattern = patterner.build(request).sub(%r{^/},"").gsub(%{/:},":").gsub(%r{/},"-")
28
+ File::join(example.metadata[:doc_path],
29
+ "#{request.method}+#{response.status}@#{pattern}")
30
+ end
31
+ end
32
+
33
+ def filename
34
+ base_filename + ".json"
35
+ end
36
+
37
+ def existing_body
38
+ JSON.parse(File.read(output_path))
39
+ end
40
+
41
+ def example_passed?
42
+ example.exception.nil?
43
+ end
44
+
45
+ def document
46
+ @document ||= Document.new(output_path, response.body)
47
+ end
48
+
49
+ def significant_change?
50
+ document.different_from?(existing_body)
51
+ rescue Errno::ENOENT
52
+ true
53
+ end
54
+
55
+ def store
56
+ if example_passed?
57
+ Xing::SpecDoc.store(self)
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,92 @@
1
+ require 'fileutils'
2
+ require 'xing/specdoc/document'
3
+ require 'xing/specdoc/winnower'
4
+
5
+ module Xing
6
+ module SpecDoc
7
+ class DocFamily
8
+ def initialize(name)
9
+ @name = name
10
+ @docs = []
11
+ @index = 1
12
+ @out_stream = $stdout
13
+ end
14
+
15
+ attr_reader :name, :docs
16
+ attr_accessor :out_stream
17
+
18
+ def add(doc)
19
+ @docs << doc
20
+ end
21
+
22
+ def old_doc_glob
23
+ File::join(Xing::SpecDoc.response_target_dir, name) + "*"
24
+ end
25
+
26
+ def old_doc_paths
27
+ @old_doc_paths ||= dir_glob(old_doc_glob).find_all do |path|
28
+ re = /#{Regexp.escape(name)}-\d+\.json$/
29
+ path =~ re
30
+ end
31
+ end
32
+
33
+ def old_docs
34
+ old_doc_paths.map do |path|
35
+ Document.new(path, File.read(path))
36
+ end
37
+ end
38
+
39
+ def record
40
+ ensure_target_dir
41
+
42
+ winnower = Winnower.new(docs, old_docs)
43
+
44
+ winnower.obsolete_paths.each do |path|
45
+ kill(path)
46
+ end
47
+
48
+ winnower.kept_new.each do |doc|
49
+ write(doc)
50
+ end
51
+ end
52
+
53
+ def next_output_path
54
+ loop do
55
+ candidate = File::join(Xing::SpecDoc.response_target_dir, "#{name}-#{@index}.json")
56
+ if exist?(candidate)
57
+ @index += 1
58
+ else
59
+ return candidate
60
+ end
61
+ end
62
+ end
63
+
64
+ def ensure_target_dir
65
+ FileUtils.mkdir_p(File.dirname(File::join(Xing::SpecDoc.response_target_dir, name)))
66
+ end
67
+
68
+ def kill(path)
69
+ @out_stream.puts "Removing outdated JSON example at #{path}"
70
+ FileUtils::rm_f(path)
71
+ end
72
+
73
+ def write(doc)
74
+ path = next_output_path
75
+ @out_stream.puts "Writing new JSON example to #{path}"
76
+ File.write(path, doc.pretty_body)
77
+ end
78
+
79
+ def dir_glob(pattern)
80
+ Dir.glob(pattern)
81
+ end
82
+
83
+ def exist?(path)
84
+ File.exist?(path)
85
+ end
86
+
87
+ def read(path)
88
+ File.read(path)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,73 @@
1
+ require 'hashdiff'
2
+
3
+ module Xing
4
+ module SpecDoc
5
+ class Document
6
+ def initialize(path, contents)
7
+ @path = path
8
+ @contents = contents
9
+ end
10
+ attr_reader :path, :contents
11
+
12
+ def parsed_body
13
+ @parsed_body ||= JSON.parse(contents)
14
+ end
15
+
16
+ def pretty_body
17
+ @pretty_body ||= JSON.pretty_generate(parsed_body)+"\n"
18
+ end
19
+
20
+ def difference_from(other)
21
+ diff(parsed_body, other)
22
+ end
23
+
24
+ def diff(one, other)
25
+ diff = HashDiff.diff(one, other)
26
+
27
+
28
+ by_change = diff.group_by {|change| change[0]}
29
+
30
+ adds = by_change.delete("+") || []
31
+ subs = by_change.delete("-") || []
32
+ mods = by_change.values.inject do |all, a_kind|
33
+ all + a_kind
34
+ end || []
35
+
36
+ arr_adds, other_adds = adds.partition do |add|
37
+ add[1] =~ /\[\d+\]$/
38
+ end
39
+
40
+ mods += (other_adds || [])
41
+
42
+ arr_adds.each do |add|
43
+ sub = subs.find{|sub| sub[1] == add[1]}
44
+ if sub.nil?
45
+ mods << add
46
+ else
47
+ subs.delete(sub)
48
+ new_diff = diff(add[2], sub[2])
49
+ mods += new_diff.map do |change|
50
+ [change[0], [add[1], change[1]].join(".")] + change[2..-1]
51
+ end
52
+ end
53
+ end
54
+
55
+ mods += subs
56
+
57
+ mods
58
+ end
59
+
60
+ def significant_diff_item?(change)
61
+ return true if change[0] != "~"
62
+ return true if change[2].class != change[3].class
63
+ return false
64
+ end
65
+
66
+ def different_from?(body)
67
+ difference_from(body).any? do |change|
68
+ significant_diff_item?(change)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,40 @@
1
+ require 'xing/specdoc/api-doccer'
2
+ require 'xing/specdoc/doc-family'
3
+
4
+ module Xing
5
+ module SpecDoc
6
+ class << self
7
+ def target_dir
8
+ RSpec.configuration.specdoc_target_directory
9
+ end
10
+
11
+ def response_target_dir
12
+ File.join(target_dir, "responses")
13
+ end
14
+
15
+ def request_target_dir
16
+ File.join(target_dir, "requests")
17
+ end
18
+
19
+ def doc_hash
20
+ @doc_hash ||= Hash.new{|h,k| h[k]= DocFamily.new(k)}
21
+ end
22
+
23
+ def store(doc)
24
+ doc_hash[doc.base_filename].add(doc)
25
+ end
26
+
27
+ def record_docs
28
+ doc_hash.each_pair do |_, docs|
29
+ docs.record
30
+ end
31
+ end
32
+
33
+ def included(group)
34
+ group.after(:each) do |example|
35
+ ApiDoccer.new(example, request, response).store
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Xing
2
+ module SpecDoc
3
+ class Patterner
4
+ def initialize(rails_routes, route_set=nil)
5
+ @rails_routes = rails_routes
6
+ @route_set = route_set
7
+ end
8
+ attr_reader :rails_routes
9
+
10
+ def route_map
11
+ @route_map ||=
12
+ begin
13
+ ad_routes_array = rails_routes.routes
14
+ rack_routes_array = rails_routes.set.instance_eval{ @routes }
15
+ Hash[ rack_routes_array.zip(ad_routes_array) ]
16
+ end
17
+ end
18
+
19
+ def route_set
20
+ @route_set ||= Rails.application.routes.router
21
+ end
22
+
23
+ def build(req)
24
+ route_set.recognize(req) do |route, matches, params|
25
+ rails_route = route_map[route]
26
+
27
+ path_spec = :unrecognized
28
+ segment_keys = {}
29
+
30
+ if route_map.has_key?(route)
31
+ rails_route = route_map[route]
32
+ path_spec = rails_route.path.spec.to_s
33
+ segment_keys = rails_route.segment_keys
34
+ return path_spec.sub(/\(\.:format\)/,"")
35
+ end
36
+ end
37
+ return nil
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,66 @@
1
+
2
+ module Xing
3
+ module SpecDoc
4
+ class Winnower
5
+ def initialize(incoming_docs, existing_docs)
6
+ @incoming_docs = incoming_docs
7
+ @existing_docs = existing_docs
8
+
9
+ reset
10
+ end
11
+
12
+ def kept_new
13
+ if @keepable.nil?
14
+ resolve
15
+ end
16
+ @keepable
17
+ end
18
+
19
+ def obsolete_paths
20
+ if @keepable.nil?
21
+ resolve
22
+ end
23
+ @obsolete_paths
24
+ end
25
+
26
+ def reset
27
+ @obsolete_paths = []
28
+ @keepable = nil
29
+ end
30
+
31
+ def resolve
32
+ new_docs = significant_docs
33
+
34
+ @existing_docs.each do |doc|
35
+ new_docs = resolve_against_existing(doc, new_docs)
36
+ end
37
+
38
+ @keepable = new_docs
39
+ end
40
+
41
+ def significant_docs
42
+ @incoming_docs.reduce([]) do |list, doc|
43
+ if list.any?{|kept| not kept.different_from?(doc.parsed_body)}
44
+ list
45
+ else
46
+ list + [doc]
47
+ end
48
+ end
49
+ end
50
+
51
+ def resolve_against_existing(doc, list)
52
+ old_doc = JSON.parse(doc.contents)
53
+
54
+ keep, trash = list.partition do |doc|
55
+ doc.different_from?(old_doc)
56
+ end
57
+
58
+ if trash.length == 0
59
+ @obsolete_paths << doc.path
60
+ end
61
+
62
+ return keep
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ require 'xing/specdoc/module'
2
+
3
+ RSpec.configure do |config|
4
+ config.add_setting :specdoc_target_directory, :default => "../spec_api"
5
+
6
+ config.include(Xing::SpecDoc, :doc_path => proc{|value| !!value})
7
+
8
+ config.after(:suite) do
9
+ Xing::SpecDoc.record_docs
10
+ end
11
+ end
@@ -0,0 +1,125 @@
1
+ require 'xing/specdoc/document'
2
+
3
+ describe Xing::SpecDoc::Document do
4
+ let :path do
5
+ "whatever_path"
6
+ end
7
+
8
+ subject :doccer do
9
+ Xing::SpecDoc::Document.new(path, response_json)
10
+ end
11
+
12
+ let :our_hash do
13
+ {
14
+ "number" => 127,
15
+ "string" => "a string",
16
+ "string_list" => [
17
+ "a", "b", "c"
18
+ ],
19
+ "number_list" => [
20
+ 1, 2, 3
21
+ ],
22
+ "object" => {
23
+ "number" => 127,
24
+ "string" => "a string",
25
+ "string_list" => [
26
+ "a", "b", "c"
27
+ ],
28
+ "number_list" => [
29
+ 1, 2, 3
30
+ ]
31
+ }
32
+ }
33
+ end
34
+
35
+ let :other_hash do
36
+ our_hash.dup
37
+ end
38
+
39
+ let :response_json do
40
+ our_hash.to_json
41
+ end
42
+
43
+ context "should not register difference" do
44
+ let :is_diff do
45
+ false
46
+ end
47
+
48
+ it "from an identical JSON" do
49
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
50
+ end
51
+
52
+ it "from changed number values" do
53
+ other_hash["number"] = 321
54
+
55
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
56
+ end
57
+
58
+ it "from changed string values" do
59
+ other_hash["string"] = "another string"
60
+
61
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
62
+ end
63
+
64
+ it "from changed list values" do
65
+ other_hash["string_list"] = [ "z","y","x" ]
66
+ other_hash["number_list"] = [ 4,5,6 ]
67
+
68
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
69
+ end
70
+ end
71
+
72
+ context "should register differences" do
73
+ let :is_diff do
74
+ true
75
+ end
76
+
77
+ it "when string changes to number" do
78
+ other_hash["string"] = 1234
79
+
80
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
81
+ end
82
+
83
+ it "when number changes to string" do
84
+ other_hash["number"] = "tuesday"
85
+
86
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
87
+ end
88
+
89
+ it "when a field is removed" do
90
+ other_hash.delete("string")
91
+
92
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
93
+ end
94
+
95
+ it "when a field is added" do
96
+ other_hash["new_string"] = "hello"
97
+
98
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
99
+ end
100
+
101
+ it "when a list shortens" do
102
+ other_hash["string_list"] = our_hash["string_list"][0..-2]
103
+
104
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
105
+ end
106
+
107
+ it "when a list lengthens" do
108
+ other_hash["number_list"] = our_hash["number_list"] + [5]
109
+
110
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
111
+ end
112
+
113
+ it "when a member of list changes from number to string" do
114
+ other_hash["number_list"] = our_hash["number_list"][0..-2] + ["string"]
115
+
116
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
117
+ end
118
+
119
+ it "when a member of list changes from string to number" do
120
+ other_hash["string_list"] = our_hash["string_list"][0..-2] + [37]
121
+
122
+ expect(doccer.different_from?(other_hash)).to eq(is_diff)
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,112 @@
1
+ require 'xing/specdoc/document'
2
+ require 'xing/specdoc/winnower'
3
+
4
+ describe Xing::SpecDoc::Winnower do
5
+ subject :winnower do
6
+ Xing::SpecDoc::Winnower.new(new_docs, old_docs)
7
+ end
8
+
9
+ let :first_content do
10
+ { "a" => "a" }.to_json
11
+ end
12
+
13
+ let :second_content do
14
+ { "something" => "else" }.to_json
15
+ end
16
+
17
+ let :third_content do
18
+ { "something_completely" => "else" }.to_json
19
+ end
20
+
21
+ let :new_docs do
22
+ new_contents.each_with_index.map do |content, index|
23
+ Xing::SpecDoc::Document.new("new_path_#{index}", content)
24
+ end
25
+ end
26
+
27
+ let :old_docs do
28
+ old_contents.each_with_index.map do |content, index|
29
+ Xing::SpecDoc::Document.new("old_path_#{index}", content)
30
+ end
31
+ end
32
+
33
+ let :old_paths do
34
+ old_docs.map(&:path)
35
+ end
36
+
37
+ let :new_contents do
38
+ []
39
+ end
40
+
41
+ let :old_contents do
42
+ []
43
+ end
44
+
45
+ context "should reduce extraneous new docs" do
46
+ let :new_contents do
47
+ [ first_content, first_content, first_content ]
48
+ end
49
+
50
+ it "should strip the extras" do
51
+ expect(winnower.kept_new.length).to eq(1)
52
+ end
53
+
54
+ it "should not invent obsoletes" do
55
+ expect(winnower.obsolete_paths.length).to eq(0)
56
+ end
57
+ end
58
+
59
+ context "should prefer existing docs" do
60
+ let :new_contents do
61
+ [ first_content ]
62
+ end
63
+
64
+ let :old_contents do
65
+ [ first_content ]
66
+ end
67
+
68
+ it "should strip the duplicate new docs" do
69
+ expect(winnower.kept_new.length).to eq(0)
70
+ end
71
+
72
+ it "should not consider incumbents obsolete" do
73
+ expect(winnower.obsolete_paths.length).to eq(0)
74
+ end
75
+ end
76
+
77
+ context "should consider old docs without support obsolete" do
78
+ let :new_contents do
79
+ [ first_content ]
80
+ end
81
+
82
+ let :old_contents do
83
+ [ second_content ]
84
+ end
85
+
86
+ it "should strip the duplicate new docs" do
87
+ expect(winnower.kept_new.length).to eq(1)
88
+ end
89
+
90
+ it "should not consider incumbents obsolete" do
91
+ expect(winnower.obsolete_paths.length).to eq(1)
92
+ end
93
+ end
94
+
95
+ context "whole set up" do
96
+ let :new_contents do
97
+ [ first_content, second_content ]
98
+ end
99
+
100
+ let :old_contents do
101
+ [ second_content, third_content ]
102
+ end
103
+
104
+ it "should strip the duplicate new docs" do
105
+ expect(winnower.kept_new.length).to eq(1)
106
+ end
107
+
108
+ it "should not consider incumbents obsolete" do
109
+ expect(winnower.obsolete_paths.length).to eq(1)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,164 @@
1
+ #require 'ftools'
2
+ require 'fileutils'
3
+
4
+ module FileSandbox
5
+ def self.included(spec)
6
+ return unless spec.respond_to? :before
7
+
8
+ spec.before do
9
+ setup_sandbox
10
+ end
11
+
12
+ spec.after do
13
+ teardown_sandbox
14
+ end
15
+ end
16
+
17
+ class HaveContents
18
+ def initialize(contents)
19
+ @contents = contents
20
+ end
21
+
22
+ def matches?(target)
23
+ case @contents
24
+ when Regexp
25
+ @contents =~ target.contents
26
+ when String
27
+ @contents == target.contents
28
+ end
29
+ end
30
+ end
31
+
32
+ def have_contents(expected)
33
+ HaveContents.new(expected)
34
+ end
35
+
36
+ attr_reader :sandbox
37
+
38
+ def in_sandbox(&block)
39
+ raise "I expected to create a sandbox as you passed in a block to me" if !block_given?
40
+
41
+ setup_sandbox
42
+ original_error = nil
43
+
44
+ begin
45
+ yield @sandbox
46
+ rescue => e
47
+ original_error = e
48
+ raise
49
+ ensure
50
+ begin
51
+ teardown_sandbox
52
+ rescue
53
+ if original_error
54
+ STDERR.puts "ALERT: a test raised an error and failed to release some lock(s) in the sandbox directory"
55
+ raise(original_error)
56
+ else
57
+ raise
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def setup_sandbox(path = '__sandbox')
64
+ unless @sandbox
65
+ @sandbox = Sandbox.new(path)
66
+ @__old_path_for_sandbox = Dir.pwd
67
+ Dir.chdir(@sandbox.root)
68
+ end
69
+ end
70
+
71
+ def teardown_sandbox
72
+ if @sandbox
73
+ Dir.chdir(@__old_path_for_sandbox)
74
+ @sandbox.clean_up
75
+ @sandbox = nil
76
+ end
77
+ end
78
+
79
+ class Sandbox
80
+ attr_reader :root
81
+
82
+ def initialize(path = '__sandbox')
83
+ @root = File.expand_path(path)
84
+ clean_up
85
+ FileUtils.mkdir_p @root
86
+ end
87
+
88
+ def [](name)
89
+ SandboxFile.new(File.join(@root, name), name)
90
+ end
91
+
92
+ # usage new :file=>'my file.rb', :with_contents=>'some stuff'
93
+ def new(options)
94
+ if options.has_key? :directory
95
+ dir = self[options.delete(:directory)]
96
+ FileUtils.mkdir_p dir.path
97
+ else
98
+ file = self[options.delete(:file)]
99
+ if (binary_content = options.delete(:with_binary_content) || options.delete(:with_binary_contents))
100
+ file.binary_content = binary_content
101
+ else
102
+ file.content = (options.delete(:with_content) || options.delete(:with_contents) || '')
103
+ end
104
+ end
105
+
106
+ raise "unexpected keys '#{options.keys.join(', ')}'" unless options.empty?
107
+
108
+ dir || file
109
+ end
110
+
111
+ def remove(options)
112
+ name = File.join(@root, options[:file])
113
+ FileUtils.remove_file name
114
+ end
115
+
116
+ def clean_up
117
+ FileUtils.rm_rf @root
118
+ if File.exists? @root
119
+ raise "Could not remove directory #{@root.inspect}, something is probably still holding a lock on it"
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ class SandboxFile
126
+ attr_reader :path
127
+
128
+ def initialize(path, sandbox_path)
129
+ @path = path
130
+ @sandbox_path = sandbox_path
131
+ end
132
+
133
+ def inspect
134
+ "SandboxFile: #@sandbox_path"
135
+ end
136
+
137
+ def exist?
138
+ File.exist? path
139
+ end
140
+
141
+ def content
142
+ File.read path
143
+ end
144
+
145
+ def content=(content)
146
+ FileUtils.mkdir_p File.dirname(@path)
147
+ File.open(@path, "w") {|f| f << content}
148
+ end
149
+
150
+ def binary_content=(content)
151
+ FileUtils.mkdir_p File.dirname(@path)
152
+ File.open(@path, "wb") {|f| f << content}
153
+ end
154
+
155
+ def create
156
+ self.content = ''
157
+ end
158
+
159
+ alias exists? exist?
160
+ alias contents content
161
+ alias contents= content=
162
+ alias binary_contents= binary_content=
163
+ end
164
+ end
@@ -0,0 +1,17 @@
1
+ puts Dir::pwd
2
+ require 'test/unit'
3
+ begin
4
+ require 'spec'
5
+ rescue LoadError
6
+ false
7
+ end
8
+
9
+ class RSpecTest < Test::Unit::TestCase
10
+ def test_that_rspec_is_available
11
+ assert_nothing_raised("\n\n * RSpec isn't available - please run: gem install rspec *\n\n"){ ::Spec }
12
+ end
13
+
14
+ def test_that_specs_pass
15
+ assert(system(*%w{spec -f e -p **/*.rb spec}),"\n\n * Specs failed *\n\n")
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'rspec/core/formatters/base_formatter'
3
+ require 'file-sandbox'
4
+ require 'cadre/rspec3'
5
+
6
+ RSpec.configure do |config|
7
+ config.run_all_when_everything_filtered = true
8
+ config.add_formatter(Cadre::RSpec3::NotifyOnCompleteFormatter)
9
+ config.add_formatter(Cadre::RSpec3::QuickfixFormatter)
10
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xing-backend-specdoc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Judson Lester
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashdiff
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ description: ''
28
+ email:
29
+ - nyarly@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/xing/specdoc.rb
35
+ - lib/xing/specdoc/api-doccer.rb
36
+ - lib/xing/specdoc/doc-family.rb
37
+ - lib/xing/specdoc/document.rb
38
+ - lib/xing/specdoc/module.rb
39
+ - lib/xing/specdoc/patterner.rb
40
+ - lib/xing/specdoc/winnower.rb
41
+ - spec/document_spec.rb
42
+ - spec/winnower_spec.rb
43
+ - spec_help/file-sandbox.rb
44
+ - spec_help/gem_test_suite.rb
45
+ - spec_help/spec_helper.rb
46
+ homepage: http://nyarly.github.com/xing-backend-specdoc
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options:
52
+ - "--inline-source"
53
+ - "--main"
54
+ - doc/README
55
+ - "--title"
56
+ - xing-backend-specdoc-0.0.1 Documentation
57
+ require_paths:
58
+ - lib/
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project: xing-backend-specdoc
71
+ rubygems_version: 2.4.8
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: ''
75
+ test_files:
76
+ - spec_help/gem_test_suite.rb