To avoid relying on the filesystem for modified times.
Unified · Split
| @@ -2,6 +2,7 @@ module Docs · 1 unmodified line | |||
| 2 | class Doc | 2 | class Doc |
| 3 | INDEX_FILENAME = 'index.json' | 3 | INDEX_FILENAME = 'index.json' |
| 4 | DB_FILENAME = 'db.json' | 4 | DB_FILENAME = 'db.json' |
| 5 | META_FILENAME = 'meta.json' | ||
| 5 | 6 | ||
| 6 | class << self | 7 | class << self |
| 7 | include Instrumentable | 8 | include Instrumentable |
| @@ -73,6 +74,10 @@ module Docs · 65 unmodified lines | |||
| 73 | File.join path, DB_FILENAME | 74 | File.join path, DB_FILENAME |
| 74 | end | 75 | end |
| 75 | 76 | ||
| 77 | def meta_path | ||
| 78 | File.join path, META_FILENAME | ||
| 79 | end | ||
| 80 | |||
| 76 | def as_json | 81 | def as_json |
| 77 | json = { name: name, slug: slug, type: type } | 82 | json = { name: name, slug: slug, type: type } |
| 78 | json[:links] = links if links.present? | 83 | json[:links] = links if links.present? |
| @@ -107,6 +112,7 @@ module Docs · 28 unmodified lines | |||
| 107 | if index.present? | 112 | if index.present? |
| 108 | store_index(store, INDEX_FILENAME, index) | 113 | store_index(store, INDEX_FILENAME, index) |
| 109 | store_index(store, DB_FILENAME, pages) | 114 | store_index(store, DB_FILENAME, pages) |
| 115 | store_meta(store) | ||
| 110 | true | 116 | true |
| 111 | else | 117 | else |
| 112 | false | 118 | false |
| @@ -131,8 +137,16 @@ module Docs · 18 unmodified lines | |||
| 131 | instrument "#{filename.remove('.json')}.doc", before: old_json, after: new_json | 137 | instrument "#{filename.remove('.json')}.doc", before: old_json, after: new_json |
| 132 | store.write(filename, new_json) | 138 | store.write(filename, new_json) |
| 133 | end | 139 | end |
| 140 | |||
| 141 | def store_meta(store) | ||
| 142 | json = as_json | ||
| 143 | json[:mtime] = Time.now.to_i | ||
| 144 | json[:db_size] = store.size(DB_FILENAME) | ||
| 145 | store.write(META_FILENAME, json.to_json) | ||
| 146 | end | ||
| 134 | end | 147 | end |
| 135 | 148 | ||
| 149 | |||
| 136 | def initialize | 150 | def initialize |
| 137 | raise NotImplementedError, "#{self.class} is an abstract class and cannot be instantiated." if self.class.abstract | 151 | raise NotImplementedError, "#{self.class} is an abstract class and cannot be instantiated." if self.class.abstract |
| 138 | end | 152 | end |
| @@ -14,32 +14,14 @@ module Docs · 13 unmodified lines | |||
| 14 | end | 14 | end |
| 15 | 15 | ||
| 16 | def as_json | 16 | def as_json |
| 17 | indexed_docs.map do |doc| | 17 | @docs.each_with_object [] do |doc, result| |
| 18 | json = doc.as_json | 18 | next unless @store.exist?(doc.meta_path) |
| 19 | json[:mtime] = doc_mtime(doc) | 19 | result << JSON.parse(@store.read(doc.meta_path)) |
| 20 | json[:db_size] = doc_db_size(doc) | ||
| 21 | json | ||
| 22 | end | 20 | end |
| 23 | end | 21 | end |
| 24 | 22 | ||
| 25 | def to_json | 23 | def to_json |
| 26 | JSON.generate(as_json) | 24 | JSON.generate(as_json) |
| 27 | end | 25 | end |
| 28 | |||
| 29 | private | ||
| 30 | |||
| 31 | def indexed_docs | ||
| 32 | @docs.select do |doc| | ||
| 33 | @store.exist?(doc.index_path) && @store.exist?(doc.db_path) | ||
| 34 | end | ||
| 35 | end | ||
| 36 | |||
| 37 | def doc_mtime(doc) | ||
| 38 | [@store.mtime(doc.index_path).to_i, @store.mtime(doc.db_path).to_i].max | ||
| 39 | end | ||
| 40 | |||
| 41 | def doc_db_size(doc) | ||
| 42 | @store.size(doc.db_path) | ||
| 43 | end | ||
| 44 | end | 26 | end |
| 45 | end | 27 | end |
| @@ -131,6 +131,13 @@ class DocsDocTest < MiniTest::Spec · 130 unmodified lines | |||
| 131 | end | 131 | end |
| 132 | end | 132 | end |
| 133 | 133 | ||
| 134 | describe ".meta_path" do | ||
| 135 | it "returns .path + ::META_FILENAME" do | ||
| 136 | stub(doc).path { 'path' } | ||
| 137 | assert_equal File.join('path', Docs::Doc::META_FILENAME), doc.meta_path | ||
| 138 | end | ||
| 139 | end | ||
| 140 | |||
| 134 | describe ".new" do | 141 | describe ".new" do |
| 135 | it "raises an error when .abstract is true" do | 142 | it "raises an error when .abstract is true" do |
| 136 | doc.abstract = true | 143 | doc.abstract = true |
| @@ -265,12 +272,14 @@ class DocsDocTest < MiniTest::Spec · 128 unmodified lines | |||
| 265 | mock(store).write(page[:store_path], page[:output]) | 272 | mock(store).write(page[:store_path], page[:output]) |
| 266 | mock(store).write('index.json', anything) | 273 | mock(store).write('index.json', anything) |
| 267 | mock(store).write('db.json', anything) | 274 | mock(store).write('db.json', anything) |
| 275 | mock(store).write('meta.json', anything) | ||
| 268 | doc.store_pages(store) | 276 | doc.store_pages(store) |
| 269 | end | 277 | end |
| 270 | 278 | ||
| 271 | it "stores an index that contains all the pages' entries" do | 279 | it "stores an index that contains all the pages' entries" do |
| 272 | stub(store).write(page[:store_path], page[:output]) | 280 | stub(store).write(page[:store_path], page[:output]) |
| 273 | stub(store).write('db.json', anything) | 281 | stub(store).write('db.json', anything) |
| 282 | stub(store).write('meta.json', anything) | ||
| 274 | mock(store).write('index.json', anything) do |path, json| | 283 | mock(store).write('index.json', anything) do |path, json| |
| 275 | json = JSON.parse(json) | 284 | json = JSON.parse(json) |
| 276 | assert_equal pages.length, json['entries'].length | 285 | assert_equal pages.length, json['entries'].length |
| @@ -282,6 +291,7 @@ class DocsDocTest < MiniTest::Spec · 5 unmodified lines | |||
| 282 | it "stores a db that contains all the pages, indexed by path" do | 291 | it "stores a db that contains all the pages, indexed by path" do |
| 283 | stub(store).write(page[:store_path], page[:output]) | 292 | stub(store).write(page[:store_path], page[:output]) |
| 284 | stub(store).write('index.json', anything) | 293 | stub(store).write('index.json', anything) |
| 294 | stub(store).write('meta.json', anything) | ||
| 285 | mock(store).write('db.json', anything) do |path, json| | 295 | mock(store).write('db.json', anything) do |path, json| |
| 286 | json = JSON.parse(json) | 296 | json = JSON.parse(json) |
| 287 | assert_equal page[:output], json[page[:path]] | 297 | assert_equal page[:output], json[page[:path]] |
| @@ -289,6 +299,17 @@ class DocsDocTest < MiniTest::Spec · 1 unmodified line | |||
| 289 | doc.store_pages(store) | 299 | doc.store_pages(store) |
| 290 | end | 300 | end |
| 291 | 301 | ||
| 302 | it "stores a meta file that contains the doc's metadata" do | ||
| 303 | stub(store).write(page[:store_path], page[:output]) | ||
| 304 | stub(store).write('index.json', anything) | ||
| 305 | stub(store).write('db.json', anything) | ||
| 306 | mock(store).write('meta.json', anything) do |path, json| | ||
| 307 | json = JSON.parse(json) | ||
| 308 | assert_equal %w(name slug type mtime db_size).sort, json.keys.sort | ||
| 309 | end | ||
| 310 | doc.store_pages(store) | ||
| 311 | end | ||
| 312 | |||
| 292 | it "replaces the .path directory before storing the files" do | 313 | it "replaces the .path directory before storing the files" do |
| 293 | stub(doc).path { 'path' } | 314 | stub(doc).path { 'path' } |
| 294 | stub(store).write { assert false } | 315 | stub(store).write { assert false } |
| @@ -41,17 +41,12 @@ class ManifestTest < MiniTest::Spec · 40 unmodified lines | |||
| 41 | end | 41 | end |
| 42 | 42 | ||
| 43 | describe "#as_json" do | 43 | describe "#as_json" do |
| 44 | let :index_path do | 44 | let :meta_path do |
| 45 | 'index_path' | 45 | 'meta_path' |
| 46 | end | ||
| 47 | |||
| 48 | let :db_path do | ||
| 49 | 'db_path' | ||
| 50 | end | 46 | end |
| 51 | 47 | ||
| 52 | before do | 48 | before do |
| 53 | stub(doc).index_path { index_path } | 49 | stub(doc).meta_path { meta_path } |
| 54 | stub(doc).db_path { db_path } | ||
| 55 | end | 50 | end |
| 56 | 51 | ||
| 57 | it "returns an array" do | 52 | it "returns an array" do |
| @@ -59,46 +54,22 @@ class ManifestTest < MiniTest::Spec · 1 unmodified line | |||
| 59 | assert_instance_of Array, manifest.as_json | 54 | assert_instance_of Array, manifest.as_json |
| 60 | end | 55 | end |
| 61 | 56 | ||
| 62 | context "when the doc has an index and a db" do | 57 | context "when the doc has a meta file" do |
| 63 | before do | 58 | before do |
| 64 | stub(store).exist?(index_path) { true } | 59 | stub(store).exist?(meta_path) { true } |
| 65 | stub(store).exist?(db_path) { true } | 60 | stub(store).read(meta_path) { '{"name":"Test"}' } |
| 66 | end | 61 | end |
| 67 | 62 | ||
| 68 | it "includes the doc's JSON representation" do | 63 | it "includes the doc's meta representation" do |
| 69 | json = manifest.as_json | 64 | json = manifest.as_json |
| 70 | assert_equal 1, json.length | 65 | assert_equal 1, json.length |
| 71 | assert_empty doc.as_json.keys - json.first.keys | 66 | assert_equal 'Test', json[0]['name'] |
| 72 | end | ||
| 73 | |||
| 74 | it "adds an :mtime attribute with the greatest of the index and db files' mtime" do | ||
| 75 | mtime_index = Time.now - 1 | ||
| 76 | mtime_db = Time.now - 2 | ||
| 77 | stub(store).mtime(index_path) { mtime_index } | ||
| 78 | stub(store).mtime(db_path) { mtime_db } | ||
| 79 | assert_equal mtime_index.to_i, manifest.as_json.first[:mtime] | ||
| 80 | mtime_index, mtime_db = mtime_db, mtime_index | ||
| 81 | assert_equal mtime_db.to_i, manifest.as_json.first[:mtime] | ||
| 82 | end | ||
| 83 | |||
| 84 | it "adds a :db_size attribute" do | ||
| 85 | stub(store).size(db_path) { 42 } | ||
| 86 | assert_equal 42, manifest.as_json.first[:db_size] | ||
| 87 | end | ||
| 88 | end | ||
| 89 | |||
| 90 | context "when the doc doesn't have an index" do | ||
| 91 | it "doesn't include the doc" do | ||
| 92 | stub(store).exist?(db_path) { true } | ||
| 93 | stub(store).exist?(index_path) { false } | ||
| 94 | assert_empty manifest.as_json | ||
| 95 | end | 67 | end |
| 96 | end | 68 | end |
| 97 | 69 | ||
| 98 | context "when the doc doesn't have a db" do | 70 | context "when the doc doesn't have a meta file" do |
| 99 | it "doesn't include the doc" do | 71 | it "doesn't include the doc" do |
| 100 | stub(store).exist?(index_path) { true } | 72 | stub(store).exist?(meta_path) { false } |
| 101 | stub(store).exist?(db_path) { false } | ||
| 102 | assert_empty manifest.as_json | 73 | assert_empty manifest.as_json |
| 103 | end | 74 | end |
| 104 | end | 75 | end |