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,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