xing-backend-specdoc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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