rom-files 0.2.0

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.
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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'associations/many_to_many'
4
+ require_relative 'associations/many_to_one'
5
+ require_relative 'associations/one_to_many'
6
+ require_relative 'associations/one_to_one'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/many_to_many'
4
+
5
+ module ROM
6
+ module Files
7
+ module Associations
8
+ class ManyToMany < ROM::Associations::ManyToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/many_to_one'
4
+
5
+ module ROM
6
+ module Files
7
+ module Associations
8
+ class ManyToOne < ROM::Associations::ManyToOne
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/associations/one_to_many'
4
+
5
+ module ROM
6
+ module Files
7
+ module Associations
8
+ class OneToMany < ROM::Associations::OneToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/memory/associations/one_to_many'
4
+
5
+ module ROM
6
+ module Files
7
+ module Associations
8
+ class OneToOne < OneToMany
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'rom/attribute'
5
+ require_relative 'constants'
6
+
7
+ module ROM
8
+ module Files
9
+ class Attribute < ROM::Attribute
10
+ # @param [Pathname] pathname
11
+ # @return [Object]
12
+ def call(pathname)
13
+ return type[pathname.read] if meta[Files::DATA]
14
+ return type[pathname.stat] if meta[:__stat__].is_a?(TrueClass)
15
+ return type[pathname.stat.send(meta[:__stat__])] if meta[:__stat__]
16
+ return type[meta[:__proc__].(pathname)] if meta[:__proc__]
17
+ type[pathname]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/commands/create'
4
+
5
+ module ROM
6
+ module Files
7
+ module Commands
8
+ class Create < ROM::Commands::Create
9
+ adapter :files
10
+
11
+ # @param tuples [Array<Hash>]
12
+ def execute(tuples)
13
+ tuples.each { |tuple| relation << tuple }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/commands/delete'
4
+
5
+ module ROM
6
+ module Files
7
+ module Commands
8
+ class Delete < ROM::Commands::Delete
9
+ adapter :files
10
+
11
+ def execute
12
+ relation.each { |tuple| source.delete(tuple) }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/commands/update'
4
+
5
+ module ROM
6
+ module Files
7
+ module Commands
8
+ class Update < ROM::Commands::Update
9
+ adapter :files
10
+
11
+ def execute(attributes)
12
+ relation.each { |tuple| source.update(tuple, attributes) }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent/hash'
4
+ require 'mime/types/full'
5
+ require_relative 'dataset'
6
+
7
+ module ROM
8
+ module Files
9
+ class Connection
10
+ extend Forwardable
11
+
12
+ # @param path [Pathname, #to_s]
13
+ def initialize(path = Pathname.pwd)
14
+ @path = Pathname(path).expand_path
15
+ @data = Concurrent::Hash.new
16
+ end
17
+
18
+ # @return [Pathname]
19
+ attr_reader :path
20
+
21
+ # Dataset registry
22
+ #
23
+ # @return [Concurrent::Hash]
24
+ #
25
+ # @api private
26
+ attr_reader :data
27
+
28
+ # @!method []
29
+ # @return [Dataset]
30
+ #
31
+ # @api private
32
+ #
33
+ # @!method size
34
+ # Return registered datasets count
35
+ #
36
+ # @return [Integer]
37
+ #
38
+ # @api private
39
+ def_instance_delegators :data, :[], :size
40
+
41
+ # @param name [String, Symbol]
42
+ # @return [Dataset]
43
+ def create_dataset(name)
44
+ mime_type = MIME::Types[name].first
45
+ @data[name] = if mime_type
46
+ build_dataset(mime_type: mime_type)
47
+ else
48
+ build_dataset(inside_paths: [name])
49
+ end
50
+ end
51
+
52
+ def build_dataset(options)
53
+ Dataset.new([], options.merge(connection: self))
54
+ end
55
+
56
+ # @return [Boolean]
57
+ def key?(name)
58
+ MIME::Types[name].any? || path_for(name).exist?
59
+ end
60
+
61
+ # @param patterns [Array<String>]
62
+ # @param path [Pathname]
63
+ # @param exclude_patterns [Array<String>]
64
+ # @param sorting [#to_proc, nil]
65
+ # @param directories [Boolean]
66
+ # @return [Array<Pathname>]
67
+ def search(patterns, path: self.path, exclude_patterns: EMPTY_ARRAY, sorting: nil, directories: false)
68
+ files = patterns.inject([]) do |result, pattern|
69
+ result + Pathname.glob(path_for(pattern, path: path)).map { |found| found.relative_path_from(path) }
70
+ end
71
+ files = files.reject(&:directory?) unless directories
72
+ files = files.reject do |match|
73
+ exclude_patterns.any? do |pattern|
74
+ match.fnmatch(pattern, File::FNM_EXTGLOB)
75
+ end
76
+ end
77
+ files = files.sort_by(&sorting) if sorting
78
+ files
79
+ end
80
+
81
+ # @param id [Pathname, #to_s]
82
+ # @return [String]
83
+ def read(id)
84
+ path_for(id).read
85
+ end
86
+
87
+ # @param id [Pathname, #to_s]
88
+ # @param contents [String, #to_s]
89
+ # @return [String]
90
+ def write(id, contents)
91
+ path_for(id).write(contents.to_s)
92
+ end
93
+
94
+ # @param id [Pathname, #to_s]
95
+ # @return [String]
96
+ def binread(id)
97
+ path_for(id).binread
98
+ end
99
+
100
+ # @param id [Pathname, #to_s]
101
+ # @param contents [String, #to_s]
102
+ # @return [String]
103
+ def binwrite(id, contents)
104
+ path_for(id).binwrite(contents)
105
+ end
106
+
107
+ # @param id [Pathname, #to_s]
108
+ def delete(id)
109
+ path_for(id).delete
110
+ end
111
+
112
+ private
113
+
114
+ # @return [Pathname]
115
+ def path_for(name, path: self.path)
116
+ path.join(name.to_s)
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/core/constants'
4
+
5
+ module ROM
6
+ module Files
7
+ include Dry::Core::Constants
8
+
9
+ ID = :__FILE__
10
+ DATA = :DATA
11
+ HERE = %w[.].freeze
12
+ ALL = %w[*].freeze
13
+ RECURSIVE_PATTERN = '**'
14
+ RECURSIVE_EXPRESSION = /#{Regexp.escape(RECURSIVE_PATTERN)}/
15
+ end
16
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'rom/initializer'
5
+ require 'rom/support/memoizable'
6
+ require 'rom/memory/dataset'
7
+
8
+ require_relative 'connection'
9
+ require_relative 'constants'
10
+ require_relative 'types'
11
+
12
+ require_relative 'dataset/filtering'
13
+ require_relative 'dataset/mime_type'
14
+ require_relative 'dataset/paths'
15
+ require_relative 'dataset/sorting'
16
+
17
+ module ROM
18
+ module Files
19
+ class Dataset < Memory::Dataset
20
+ extend Forwardable
21
+
22
+ include Dry::Equalizer(:mime_type, :inside_paths, :include_patterns, :exclude_patterns, :sorting)
23
+ include Filtering
24
+ include MimeType
25
+ include Paths
26
+ include Sorting
27
+
28
+ # @!method initialize(mime_type: nil, inside_paths: ['.'], include_patterns: ['*'], exclude_patterns: [], sorting: nil)
29
+ # @param inside_paths [Pathname, #to_s]
30
+ # list of directories to search files in
31
+ # @param include_patterns [Array<String, #to_s>]
32
+ # array of patterns to be selected inside `path`, maps to {#include_patterns}
33
+ # @param exclude_patterns [Array<String, #to_s>]
34
+ # array of patterns to be rejected inside `path`, maps to {#exclude_patterns}
35
+ # @param sorting [Symbol, Proc, nil]
36
+ # see {#sorting}
37
+
38
+ # @!attribute [r] connection
39
+ # @return [Connection]
40
+ option :connection, default: proc { Connection.new }
41
+
42
+ # @!attribute [r] path
43
+ # @return [Connection]
44
+ option :path, Types::Coercible::Pathname, default: proc { connection.path }
45
+
46
+ def at(path)
47
+ with(path: Pathname(path))
48
+ end
49
+
50
+ def dig(path)
51
+ with(path: self.path.join(path))
52
+ end
53
+
54
+ def up(level = 1)
55
+ path = self.path
56
+ level.times { path = path.dirname }
57
+ with(path: path)
58
+ end
59
+
60
+ # @!method project(*names)
61
+ # Project a dataset
62
+ #
63
+ # @param names [Array<Symbol>] A list of attribute names
64
+ #
65
+ # @return [Dataset]
66
+ #
67
+ # @api public
68
+
69
+ # @!method join(*args)
70
+ #
71
+ # Join with other datasets
72
+ #
73
+ # @param args [Array<Dataset>] A list of dataset to join with
74
+ #
75
+ # @return [Dataset]
76
+ #
77
+ # @api public
78
+
79
+ # @!method restrict(criteria = nil)
80
+ # Restrict a dataset
81
+ #
82
+ # @param criteria [Hash] A hash with conditions
83
+ #
84
+ # @return [Dataset]
85
+ #
86
+ # @api public
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/memory/dataset'
4
+
5
+ module ROM
6
+ module Files
7
+ class Dataset < Memory::Dataset
8
+ module Filtering
9
+ def self.included(other)
10
+ super(other)
11
+ other.module_eval do
12
+ option :inside_paths, Types::Strict::Array.of(Types::Coercible::Pathname), default: proc { HERE }
13
+ option :include_patterns, Types::Strict::Array.of(Types::Coercible::String), default: proc { ALL }
14
+ option :exclude_patterns, Types::Strict::Array.of(Types::Coercible::String), default: proc { EMPTY_ARRAY }
15
+ option :search_recursive, Types::Bool, default: proc { true }
16
+ end
17
+ end
18
+
19
+ # @!group Reading
20
+
21
+ # @!attribute [r] inside_paths
22
+ # Array of glob patterns to select files inside
23
+ # @return [Array<String>]
24
+
25
+ # @!attribute [r] include_patterns
26
+ # Array of glob patterns to be selected inside {#inside_paths}
27
+ # @return [Array<String>]
28
+
29
+ # @!attribute [r] exclude_patterns
30
+ # Array of filename patterns to be rejected
31
+ # @return [Array<String>]
32
+
33
+ # @!attribute [r] search_recursive
34
+ # Whether to search for files only in specific directory/ies or recursively
35
+ # @return [Boolean]
36
+
37
+ # @return [Dataset]
38
+ def select(*patterns)
39
+ with(include_patterns: patterns.uniq)
40
+ end
41
+
42
+ # @return [Dataset]
43
+ def select_append(*patterns)
44
+ with(include_patterns: (include_patterns + patterns).uniq)
45
+ end
46
+
47
+ # @return [Dataset]
48
+ def inside(*paths)
49
+ with(inside_paths: paths)
50
+ end
51
+
52
+ # @return [Dataset]
53
+ def inside_append(*paths)
54
+ with(inside_paths: (inside_paths + paths).uniq)
55
+ end
56
+
57
+ # @return [Dataset]
58
+ def recursive
59
+ with(search_recursive: true)
60
+ end
61
+
62
+ # @return [Dataset]
63
+ def not_recursive
64
+ with(search_recursive: false)
65
+ end
66
+
67
+ # @return [Array<Pathname>]
68
+ def search_patterns
69
+ inside_paths.inject([]) do |result, path|
70
+ path = path.join('**') if search_recursive
71
+ result + include_patterns.map do |pattern|
72
+ path.join(pattern)
73
+ end
74
+ end
75
+ end
76
+
77
+ # @return [Boolean]
78
+ def recursive?
79
+ search_recursive ||
80
+ inside_paths.all? { |path| path.to_s =~ RECURSIVE_EXPRESSION } ||
81
+ include_patterns.all? { |pattern| pattern =~ RECURSIVE_EXPRESSION }
82
+ end
83
+
84
+ # @return [Dataset]
85
+ def reject(*patterns)
86
+ with(exclude_patterns: patterns.uniq)
87
+ end
88
+
89
+ # @return [Dataset]
90
+ def reject_append(*patterns)
91
+ with(exclude_patterns: (exclude_patterns + patterns).uniq)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end