rom-files 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +112 -0
  6. data/.rubocop_todo.yml +7 -0
  7. data/.ruby-version +1 -0
  8. data/.simplecov +6 -0
  9. data/.travis.yml +24 -0
  10. data/.yardopts +6 -0
  11. data/CHANGELOG.md +10 -0
  12. data/Gemfile +25 -0
  13. data/Guardfile +48 -0
  14. data/LICENSE.md +21 -0
  15. data/README.md +44 -0
  16. data/Rakefile +37 -0
  17. data/Roadmap.taskpaper +44 -0
  18. data/bin/_guard-core +21 -0
  19. data/bin/console +7 -0
  20. data/bin/guard +21 -0
  21. data/bin/rake +21 -0
  22. data/bin/rspec +21 -0
  23. data/bin/yard +21 -0
  24. data/bin/yardoc +21 -0
  25. data/bin/yri +21 -0
  26. data/lib/dry/types/pathname.rb +25 -0
  27. data/lib/pathname/extensions.rb +32 -0
  28. data/lib/pathname/extensions/constants.rb +12 -0
  29. data/lib/pathname/extensions/explode.rb +27 -0
  30. data/lib/pathname/extensions/ext.rb +28 -0
  31. data/lib/pathname/extensions/partial.rb +30 -0
  32. data/lib/pathname/extensions/pathmap.rb +128 -0
  33. data/lib/rom-files.rb +3 -0
  34. data/lib/rom/files.rb +28 -0
  35. data/lib/rom/files/associations.rb +6 -0
  36. data/lib/rom/files/associations/many_to_many.rb +12 -0
  37. data/lib/rom/files/associations/many_to_one.rb +12 -0
  38. data/lib/rom/files/associations/one_to_many.rb +12 -0
  39. data/lib/rom/files/associations/one_to_one.rb +12 -0
  40. data/lib/rom/files/attribute.rb +21 -0
  41. data/lib/rom/files/commands/create.rb +18 -0
  42. data/lib/rom/files/commands/delete.rb +17 -0
  43. data/lib/rom/files/commands/update.rb +17 -0
  44. data/lib/rom/files/connection.rb +120 -0
  45. data/lib/rom/files/constants.rb +16 -0
  46. data/lib/rom/files/dataset.rb +89 -0
  47. data/lib/rom/files/dataset/filtering.rb +96 -0
  48. data/lib/rom/files/dataset/mime_type.rb +44 -0
  49. data/lib/rom/files/dataset/paths.rb +79 -0
  50. data/lib/rom/files/dataset/sorting.rb +27 -0
  51. data/lib/rom/files/extensions.rb +30 -0
  52. data/lib/rom/files/extensions/gem.rb +29 -0
  53. data/lib/rom/files/extensions/gem/relations/documentations.rb +18 -0
  54. data/lib/rom/files/extensions/gem/relations/executables.rb +20 -0
  55. data/lib/rom/files/extensions/gem/relations/implementations.rb +29 -0
  56. data/lib/rom/files/extensions/gem/relations/specifications.rb +29 -0
  57. data/lib/rom/files/extensions/markdown/attributes_inferrer.rb +20 -0
  58. data/lib/rom/files/extensions/markdown/types.rb +18 -0
  59. data/lib/rom/files/extensions/markup/attributes_inferrer.rb +32 -0
  60. data/lib/rom/files/extensions/ruby/attributes_inferrer.rb +21 -0
  61. data/lib/rom/files/extensions/ruby/types.rb +45 -0
  62. data/lib/rom/files/extensions/text/attributes_inferrer.rb +20 -0
  63. data/lib/rom/files/gateway.rb +63 -0
  64. data/lib/rom/files/plugins/configuration/gem.rb +30 -0
  65. data/lib/rom/files/plugins/relation/instrumentation.rb +28 -0
  66. data/lib/rom/files/plugins/schema/contents.rb +72 -0
  67. data/lib/rom/files/plugins/schema/mime.rb +59 -0
  68. data/lib/rom/files/plugins/schema/shebang.rb +79 -0
  69. data/lib/rom/files/plugins/schema/stat.rb +108 -0
  70. data/lib/rom/files/relation.rb +143 -0
  71. data/lib/rom/files/schema.rb +77 -0
  72. data/lib/rom/files/schema/attributes_inferrer.rb +79 -0
  73. data/lib/rom/files/schema/inferrer.rb +36 -0
  74. data/lib/rom/files/types.rb +37 -0
  75. data/lib/rom/files/version.rb +7 -0
  76. data/rom-files.gemspec +48 -0
  77. data/spec/integration/rom/files/gateway_spec.rb +28 -0
  78. data/spec/integration/rom/files/gem_spec.rb +98 -0
  79. data/spec/integration/rom/files/relations_spec.rb +63 -0
  80. data/spec/integration/rom/files/schema/inferrer_spec.rb +77 -0
  81. data/spec/lib/dry/types/pathname_spec.rb +50 -0
  82. data/spec/lib/pathname/extensions/explode_spec.rb +25 -0
  83. data/spec/lib/pathname/extensions/ext_spec.rb +33 -0
  84. data/spec/lib/pathname/extensions/partial_spec.rb +17 -0
  85. data/spec/lib/pathname/extensions/pathmap_spec.rb +147 -0
  86. data/spec/lib/rom/files/attribute_spec.rb +43 -0
  87. data/spec/lib/rom/files/connection_spec.rb +76 -0
  88. data/spec/lib/rom/files/dataset/inside_spec.rb +22 -0
  89. data/spec/lib/rom/files/dataset/mime_type_spec.rb +23 -0
  90. data/spec/lib/rom/files/dataset/recursive_question_mark_spec.rb +42 -0
  91. data/spec/lib/rom/files/dataset/recursive_spec.rb +29 -0
  92. data/spec/lib/rom/files/dataset/reject_append_spec.rb +68 -0
  93. data/spec/lib/rom/files/dataset/reject_spec.rb +26 -0
  94. data/spec/lib/rom/files/dataset/select_append_spec.rb +69 -0
  95. data/spec/lib/rom/files/dataset/select_spec.rb +38 -0
  96. data/spec/lib/rom/files/dataset/sort_spec.rb +22 -0
  97. data/spec/lib/rom/files/dataset_spec.rb +52 -0
  98. data/spec/lib/rom/files/extensions/text/attributes_inferrer_spec.rb +54 -0
  99. data/spec/lib/rom/files/gateway_spec.rb +39 -0
  100. data/spec/lib/rom/files/plugins/schema/contents_spec.rb +66 -0
  101. data/spec/lib/rom/files/plugins/schema/mime_spec.rb +66 -0
  102. data/spec/lib/rom/files/plugins/schema/stat_spec.rb +109 -0
  103. data/spec/lib/rom/files/relation/pluck_spec.rb +20 -0
  104. data/spec/lib/rom/files/relation/reject_spec.rb +22 -0
  105. data/spec/lib/rom/files/relation/select_spec.rb +35 -0
  106. data/spec/lib/rom/files/relation/sort_spec.rb +21 -0
  107. data/spec/lib/rom/files/relation/to_a_spec.rb +39 -0
  108. data/spec/lib/rom/files/relation_spec.rb +10 -0
  109. data/spec/lib/rom/files/schema/attributes_inferrer_spec.rb +45 -0
  110. data/spec/lib/rom/files/schema/inferrer_spec.rb +29 -0
  111. data/spec/lib/rom/files/schema_spec.rb +109 -0
  112. data/spec/lib/rom/files/types/mime_type_spec.rb +9 -0
  113. data/spec/lib/rom/files/types/path_spec.rb +10 -0
  114. data/spec/lib/rom/files/types_spec.rb +6 -0
  115. data/spec/shared/rom/files/files_setup.rb +19 -0
  116. data/spec/shared/rom/files/filesystem_setup.rb +10 -0
  117. data/spec/shared/rom/files/gateway_setup.rb +17 -0
  118. data/spec/shared/rom/files/media_dataset.rb +10 -0
  119. data/spec/shared/rom/files/media_files.rb +17 -0
  120. data/spec/shared/rom/files/media_relation.rb +30 -0
  121. data/spec/spec_helper.rb +10 -0
  122. metadata +421 -0
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/relation'
4
+ require 'shared/rom/files/media_relation'
5
+
6
+ RSpec.describe ROM::Files::Relation, '#select' do
7
+ include_context 'media relation'
8
+
9
+ subject(:names) { relation.pluck(:basename) }
10
+ let(:relation) { container.relations[:media] }
11
+
12
+ context '(simple_pattern)' do
13
+ let(:relation) { super().select('*.txt') }
14
+
15
+ it { is_expected.to eql [P('some_file.txt')] }
16
+ end
17
+
18
+ context '(multiple, patterns)' do
19
+ let(:relation) { super().select('*.txt', '*.md') }
20
+
21
+ it { is_expected.to eql [P('some_file.txt'), P('some_markdown.md')] }
22
+ end
23
+
24
+ context '(glob_pattern)' do
25
+ let(:relation) { super().select('*.{txt,md}') }
26
+
27
+ it { is_expected.to eql [P('some_file.txt'), P('some_markdown.md')] }
28
+ end
29
+
30
+ context '(ordered_glob_pattern)' do
31
+ let(:relation) { super().select('*.{md,txt}') }
32
+
33
+ it { is_expected.to eql [P('some_markdown.md'), P('some_file.txt')] }
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/media_relation'
4
+
5
+ RSpec.describe ROM::Files::Relation, '#sort' do
6
+ include_context 'media relation'
7
+
8
+ subject(:names) { relation.pluck(:basename) }
9
+
10
+ context 'default (by path)' do
11
+ let(:relation) { super().sort }
12
+
13
+ it { is_expected.to eql([P('some_file.txt'), P('some_image.png'), P('some_markdown.md')]) }
14
+ end
15
+
16
+ context 'by extname' do
17
+ let(:relation) { super().sort(:extname) }
18
+
19
+ it { is_expected.to eql([P('some_markdown.md'), P('some_image.png'), P('some_file.txt')]) }
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/media_relation'
4
+
5
+ RSpec.describe ROM::Files::Relation, '#to_a' do
6
+ include_context 'media relation'
7
+
8
+ let(:names) { relation.pluck(:basename) }
9
+ let(:paths) { relation.to_a.map { |file| file[:__FILE__] } }
10
+
11
+ its(:to_a) { is_expected.to eq data }
12
+ its(:schema) { is_expected.to be_a ROM::Files::Schema }
13
+
14
+ it 'lists file paths' do
15
+ expect(paths).to eql([
16
+ P('media/some_image.png'),
17
+ P('media/some_file.txt'),
18
+ P('media/some_markdown.md')
19
+ ])
20
+ end
21
+
22
+ context 'names' do
23
+ subject { names }
24
+
25
+ it { is_expected.to eql([P('some_image.png'), P('some_file.txt'), P('some_markdown.md')]) }
26
+
27
+ context 'with custom view using select' do
28
+ let(:relation) { super().text_files }
29
+
30
+ it { is_expected.to eql [P('some_file.txt'), P('some_markdown.md')] }
31
+ end
32
+
33
+ context 'with custom view using reject' do
34
+ let(:relation) { super().binary_files }
35
+
36
+ it { is_expected.to eql [P('some_image.png')] }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/relation'
4
+ require 'shared/rom/files/media_relation'
5
+
6
+ RSpec.describe ROM::Files::Relation do
7
+ include_context 'media relation'
8
+
9
+ its(:count) { is_expected.to eq 3 }
10
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/schema/attributes_inferrer'
4
+ require 'shared/rom/files/media_relation'
5
+
6
+ RSpec.describe ROM::Files::Schema::AttributesInferrer do
7
+ include_context 'media files'
8
+
9
+ subject(:inferrer) { described_class.new(attr_class: attr_class) }
10
+ let(:attr_class) { ROM::Files::Attribute }
11
+
12
+ its(:attr_class) { is_expected.to eq attr_class }
13
+
14
+ describe '#call' do
15
+ subject(:result) { inferrer.(schema, gateway) }
16
+ let(:relation_name) { ROM::Relation::Name.new(:media) }
17
+ let(:schema) { ROM::Files::Schema.new(relation_name) }
18
+
19
+ it { is_expected.to be_a Array }
20
+ its(:size) { is_expected.to eq 2 }
21
+
22
+ describe 'inferred' do
23
+ subject(:inferred) { result.first }
24
+
25
+ it { is_expected.to be_a Array }
26
+ its(:size) { is_expected.to eq 1 }
27
+
28
+ describe '__FILE__' do
29
+ subject(:attribute) { inferred.first }
30
+
31
+ it { is_expected.to be_a attr_class }
32
+ its(:name) { is_expected.to eq ROM::Files::ID }
33
+ its(:primitive) { is_expected.to eq Pathname }
34
+ its(:primary_key?) { is_expected.to be true }
35
+ end
36
+ end
37
+
38
+ describe 'missing' do
39
+ subject(:missing) { result.last }
40
+
41
+ it { is_expected.to be_a Array }
42
+ its(:size) { is_expected.to eq 0 }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/schema/inferrer'
4
+ require 'shared/rom/files/media_files'
5
+
6
+ RSpec.describe ROM::Files::Schema::Inferrer do
7
+ include_context 'media files'
8
+
9
+ subject(:inferrer) { described_class.new }
10
+ let(:relation_name) { ROM::Relation::Name.new(:media) }
11
+ let(:schema) { ROM::Files::Schema.new(relation_name) }
12
+
13
+ its(:attr_class) { is_expected.to eq ROM::Files::Attribute }
14
+
15
+ describe '#call' do
16
+ subject(:result) { inferrer.(schema, gateway) }
17
+
18
+ it { is_expected.to be_a Hash }
19
+ its([:attributes]) { is_expected.to be_a Array }
20
+
21
+ describe '[:attributes]' do
22
+ subject(:attributes) { result[:attributes] }
23
+
24
+ it { is_expected.to be_a Array }
25
+ its(:size) { is_expected.to eq 1 }
26
+ its(:first) { is_expected.to be_a ROM::Files::Attribute }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/schema'
4
+ require 'shared/rom/files/media_relation'
5
+
6
+ RSpec.describe ROM::Files::Schema do
7
+ subject(:schema) { schema_proc.() }
8
+ let(:identify) { schema.method(:identify) }
9
+ let(:contents_for) { schema.method(:contents_for) }
10
+
11
+ before { schema.finalize_attributes!.finalize! }
12
+
13
+ context 'with simple primary key' do
14
+ let(:schema_proc) do
15
+ Class.new(ROM::Relation[:files]).schema do
16
+ attribute :path, ROM::Files::Types::Path
17
+ end
18
+ end
19
+
20
+ its(:primary_key) { is_expected.to eql [schema[:path]] }
21
+
22
+ describe '#identify' do
23
+ subject { identify }
24
+
25
+ its([path: Pathname('a')]) { is_expected.to eq Pathname('a') }
26
+ its([path: Pathname('a'), other: 'b']) { is_expected.to eq Pathname('a') }
27
+ its([other: 'b']) { is_expected.to eq nil }
28
+ end
29
+ end
30
+
31
+ context 'with complex primary key' do
32
+ let(:schema_proc) do
33
+ Class.new(ROM::Relation[:files]).schema do
34
+ attribute :dir, ROM::Files::Types::Path
35
+ attribute :path, ROM::Files::Types::Path
36
+ end
37
+ end
38
+
39
+ its(:primary_key) { is_expected.to eql [schema[:dir], schema[:path]] }
40
+
41
+ describe '#identify' do
42
+ subject { identify }
43
+
44
+ its([dir: Pathname('lib/'), path: Pathname('file.rb')]) { is_expected.to eq Pathname('lib/file.rb') }
45
+ its([dir: Pathname('lib/')]) { is_expected.to eq Pathname('lib/') }
46
+ its([path: Pathname('file.rb')]) { is_expected.to eq Pathname('file.rb') }
47
+ its([other: 'b']) { is_expected.to eq nil }
48
+ end
49
+ end
50
+
51
+ context 'without primary key defined' do
52
+ let(:schema_proc) do
53
+ Class.new(ROM::Relation[:files]).schema do
54
+ attribute :__FILE__, ROM::Files::Types::Pathname
55
+ end
56
+ end
57
+
58
+ its(:primary_key) { is_expected.to eql [] }
59
+
60
+ describe '#identify' do
61
+ subject { identify }
62
+
63
+ its([__FILE__: Pathname('a')]) { is_expected.to eq Pathname('a') }
64
+ its([__FILE__: Pathname('a'), other: 'b']) { is_expected.to eq Pathname('a') }
65
+ its([other: 'b']) { is_expected.to eq nil }
66
+ end
67
+ end
68
+
69
+ context 'with simple contents attribute' do
70
+ let(:schema_proc) do
71
+ Class.new(ROM::Relation[:files]).schema do
72
+ attribute :path, ROM::Files::Types::Path
73
+ attribute :contents, ROM::Files::Types::String.meta(DATA: true)
74
+ end
75
+ end
76
+
77
+ its(:primary_key) { is_expected.to eql [schema[:path]] }
78
+
79
+ describe '#contents_for' do
80
+ subject { contents_for }
81
+
82
+ its([contents: 'Contents']) { is_expected.to eq 'Contents' }
83
+ its([contents: '']) { is_expected.to eq '' }
84
+ its([contents: nil]) { is_expected.to eq nil }
85
+ its([other: 'b']) { is_expected.to eq nil }
86
+ end
87
+ end
88
+
89
+ context 'with complex simple contents attribute' do
90
+ let(:schema_proc) do
91
+ Class.new(ROM::Relation[:files]).schema do
92
+ attribute :path, ROM::Files::Types::Path
93
+ attribute :header, ROM::Files::Types::String.meta(DATA: true)
94
+ attribute :footer, ROM::Files::Types::String.meta(DATA: true)
95
+ end
96
+ end
97
+
98
+ its(:primary_key) { is_expected.to eql [schema[:path]] }
99
+
100
+ describe '#contents_for' do
101
+ subject { contents_for }
102
+
103
+ its([header: "Header\n", footer: "Footer\n"]) { is_expected.to eq "Header\nFooter\n" }
104
+ its([header: "Header\n"]) { is_expected.to eq "Header\n" }
105
+ its([footer: "Footer\n"]) { is_expected.to eq "Footer\n" }
106
+ its([contents: '']) { is_expected.to eq nil }
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/types'
4
+
5
+ RSpec.describe ROM::Files::Types::MimeType do
6
+ subject(:type) { described_class }
7
+
8
+ its(['text/plain']) { is_expected.to be_a MIME::Type }
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/types'
4
+
5
+ RSpec.describe ROM::Files::Types::Path do
6
+ subject(:type) { described_class }
7
+
8
+ its([Pathname.pwd]) { is_expected.to be_a Pathname }
9
+ its(:meta) { is_expected.to eq(primary_key: true) }
10
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/files/types'
4
+
5
+ RSpec.describe ROM::Files::Types do
6
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/gateway_setup'
4
+
5
+ RSpec.shared_context 'files setup' do
6
+ include_context 'gateway setup'
7
+
8
+ let(:path) { uri.join(dir) }
9
+ let(:data) do
10
+ tree.map do |file, _contents|
11
+ { __FILE__: path.join(file).relative_path_from(uri) }
12
+ end
13
+ end
14
+
15
+ before :each do
16
+ path.mkpath
17
+ tree.each { |file, contents| path.join(file).write(contents) }
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'filesystem setup' do
4
+ let(:uri) { SPEC_ROOT.join('../tmp/test') }
5
+
6
+ before do
7
+ uri.rmtree if uri.directory?
8
+ uri.mkpath
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/filesystem_setup'
4
+
5
+ RSpec.shared_context 'gateway setup' do
6
+ include_context 'filesystem setup'
7
+
8
+ let(:configuration) { ROM::Configuration.new(:files, uri, extensions: %i[text markdown]) }
9
+ let(:container) { ROM.container(configuration) }
10
+ let(:gateway) { container.gateways[:default] }
11
+
12
+ # @param [Pathname, #to_s] path
13
+ # @return [Pathname]
14
+ def P(path) # rubocop:disable Naming/MethodName
15
+ Pathname(path)
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/media_files'
4
+
5
+ RSpec.shared_context 'media dataset' do
6
+ include_context 'media files'
7
+
8
+ subject(:dataset) { ROM::Files::Dataset.new(connection: connection) }
9
+ let(:connection) { ROM::Files::Connection.new(path) }
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/files_setup'
4
+
5
+ RSpec.shared_context 'media files' do
6
+ include_context 'files setup'
7
+
8
+ let(:dir) { 'media' }
9
+ let(:path) { uri.join(dir) }
10
+ let(:tree) do
11
+ {
12
+ 'some_image.png' => '',
13
+ 'some_file.txt' => 'some content',
14
+ 'some_markdown.md' => 'some **markdown** file'
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shared/rom/files/media_files'
4
+
5
+ RSpec.shared_context 'media relation' do
6
+ include_context 'media files'
7
+
8
+ subject(:relation) { container.relations.media }
9
+
10
+ before :each do
11
+ configuration.relation :media do
12
+ schema :media, infer: true
13
+
14
+ # @return [ROM::Files::Relation]
15
+ def text_files
16
+ select('*.{txt,md}')
17
+ end
18
+
19
+ # @return [ROM::Files::Relation]
20
+ def binary_files
21
+ reject('*.txt', '*.md')
22
+ end
23
+ end
24
+
25
+ configuration.relation :text do
26
+ schema 'text/plain', infer: true do
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+ Bundler.setup
5
+ require 'rspec/its'
6
+ require 'simplecov'
7
+
8
+ require 'rom-files'
9
+
10
+ SPEC_ROOT = Pathname(__dir__)