dry-files 0.2.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE +1 -1
- data/dry-files.gemspec +1 -3
- data/lib/dry/files/file_system.rb +1 -11
- data/lib/dry/files/memory_file_system/node.rb +19 -3
- data/lib/dry/files/memory_file_system.rb +9 -3
- data/lib/dry/files/version.rb +1 -1
- data/lib/dry/files.rb +182 -11
- metadata +3 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9b554d01289fcefe3eb4159a0d832a55544de168d9df3d68db65d017526debd
|
4
|
+
data.tar.gz: cfe09c1716f6046421111bd848a81c9a0785b195f4a612c348c2054cdf6aa805
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e6142b107d2deafc6bb1b98412fee75be75e05934e940fd8227abd8079cff9b4078d064a5e8ae90339b95a9852a189bab199d3ec5f4de5e6001e13b22be7cde
|
7
|
+
data.tar.gz: 53abdcbd822a4dbc78dac59308454fe46738e21448d3faeca7eae8e80d6a0725b06f7d614c4e736bba0107719c45229765a16ea84b529aad3a4445d882859500
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 1.0.0 2022-11-04
|
4
|
+
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
|
8
|
+
- Bumped version to 1.0.0 (@solnic)
|
9
|
+
|
10
|
+
[Compare v0.3.0...v1.0.0](https://github.com/dry-rb/dry-files/compare/v0.3.0...v1.0.0)
|
11
|
+
|
12
|
+
## 0.3.0 2022-09-19
|
13
|
+
|
14
|
+
|
15
|
+
### Added
|
16
|
+
|
17
|
+
- Support for `inject_line_at_class_bottom` (via #13) (@jodosha)
|
18
|
+
|
19
|
+
|
20
|
+
[Compare v0.2.0...v0.3.0](https://github.com/dry-rb/dry-files/compare/v0.2.0...v0.3.0)
|
21
|
+
|
3
22
|
## 0.2.0 2022-07-10
|
4
23
|
|
5
24
|
|
data/LICENSE
CHANGED
data/dry-files.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
25
|
-
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-files/blob/
|
25
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-files/blob/main/CHANGELOG.md"
|
26
26
|
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-files"
|
27
27
|
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-files/issues"
|
28
28
|
|
@@ -30,7 +30,5 @@ Gem::Specification.new do |spec|
|
|
30
30
|
|
31
31
|
# to update dependencies edit project.yml
|
32
32
|
|
33
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
34
33
|
spec.add_development_dependency "rspec", "~> 3.10"
|
35
|
-
spec.add_development_dependency "rubocop", "~> 1.12"
|
36
34
|
end
|
@@ -46,7 +46,7 @@ module Dry
|
|
46
46
|
# @raise [Dry::Files::IOError] in case of I/O error
|
47
47
|
#
|
48
48
|
# @since 0.1.0
|
49
|
-
def open(path, mode
|
49
|
+
def open(path, mode, *args, &blk)
|
50
50
|
touch(path)
|
51
51
|
|
52
52
|
with_error_handling do
|
@@ -345,16 +345,6 @@ module Dry
|
|
345
345
|
|
346
346
|
private
|
347
347
|
|
348
|
-
# @since 0.1.0
|
349
|
-
# @api private
|
350
|
-
OPEN_MODE = ::File::RDWR
|
351
|
-
private_constant :OPEN_MODE
|
352
|
-
|
353
|
-
# @since 0.1.0
|
354
|
-
# @api private
|
355
|
-
WRITE_MODE = (::File::CREAT | ::File::WRONLY | ::File::TRUNC).freeze
|
356
|
-
private_constant :WRITE_MODE
|
357
|
-
|
358
348
|
# Catch `SystemCallError` and re-raise a `Dry::Files::IOError`.
|
359
349
|
#
|
360
350
|
# `SystemCallError` is parent for all the `Errno::*` Ruby exceptions.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "English"
|
4
3
|
require "stringio"
|
5
4
|
|
6
5
|
module Dry
|
@@ -215,8 +214,17 @@ module Dry
|
|
215
214
|
#
|
216
215
|
# @since 0.1.0
|
217
216
|
# @api private
|
218
|
-
def write(
|
219
|
-
|
217
|
+
def write(content)
|
218
|
+
content = case content
|
219
|
+
when String
|
220
|
+
content
|
221
|
+
when Array
|
222
|
+
array_to_string(content)
|
223
|
+
when NilClass
|
224
|
+
EMPTY_CONTENT
|
225
|
+
end
|
226
|
+
|
227
|
+
@content = StringIO.new(content)
|
220
228
|
@mode = DEFAULT_FILE_MODE
|
221
229
|
end
|
222
230
|
|
@@ -240,6 +248,14 @@ module Dry
|
|
240
248
|
def executable?
|
241
249
|
(mode & MODE_USER_EXECUTE).positive?
|
242
250
|
end
|
251
|
+
|
252
|
+
# @since 0.3.0
|
253
|
+
# @api private
|
254
|
+
def array_to_string(content)
|
255
|
+
content.map do |line|
|
256
|
+
line.sub(NEW_LINE_MATCHER, EMPTY_CONTENT)
|
257
|
+
end.join(NEW_LINE) + NEW_LINE
|
258
|
+
end
|
243
259
|
end
|
244
260
|
end
|
245
261
|
end
|
@@ -11,7 +11,7 @@ module Dry
|
|
11
11
|
class MemoryFileSystem
|
12
12
|
# @since 0.1.0
|
13
13
|
# @api private
|
14
|
-
EMPTY_CONTENT =
|
14
|
+
EMPTY_CONTENT = ""
|
15
15
|
private_constant :EMPTY_CONTENT
|
16
16
|
|
17
17
|
require_relative "./memory_file_system/node"
|
@@ -33,12 +33,18 @@ module Dry
|
|
33
33
|
#
|
34
34
|
# @param path [String] the target file
|
35
35
|
# @yieldparam [Dry::Files::MemoryFileSystem::Node]
|
36
|
+
# @return [Dry::Files::MemoryFileSystem::Node]
|
36
37
|
#
|
37
38
|
# @since 0.1.0
|
38
39
|
# @api private
|
39
|
-
def open(path,
|
40
|
+
def open(path, *)
|
40
41
|
file = touch(path)
|
41
|
-
|
42
|
+
|
43
|
+
if block_given?
|
44
|
+
yield file
|
45
|
+
else
|
46
|
+
file
|
47
|
+
end
|
42
48
|
end
|
43
49
|
|
44
50
|
# Read file contents
|
data/lib/dry/files/version.rb
CHANGED
data/lib/dry/files.rb
CHANGED
@@ -14,6 +14,14 @@ module Dry
|
|
14
14
|
require_relative "files/error"
|
15
15
|
require_relative "files/adapter"
|
16
16
|
|
17
|
+
# @since 0.3.0
|
18
|
+
# @api public
|
19
|
+
OPEN_MODE = ::File::RDWR
|
20
|
+
|
21
|
+
# @since 0.3.0
|
22
|
+
# @api public
|
23
|
+
WRITE_MODE = (::File::CREAT | ::File::WRONLY | ::File::TRUNC).freeze
|
24
|
+
|
17
25
|
# Creates a new instance
|
18
26
|
#
|
19
27
|
# Memory file system is experimental
|
@@ -111,6 +119,22 @@ module Dry
|
|
111
119
|
adapter.pwd
|
112
120
|
end
|
113
121
|
|
122
|
+
# Opens (or creates) a new file for both read/write operations
|
123
|
+
#
|
124
|
+
# @param path [String] the target file
|
125
|
+
# @param mode [String,Integer] Ruby file open mode
|
126
|
+
# @param args [Array<Object>] ::File.open args
|
127
|
+
# @param blk [Proc] the block to yield
|
128
|
+
#
|
129
|
+
# @yieldparam [File,Dry::Files::MemoryFileSystem::Node] the opened file
|
130
|
+
#
|
131
|
+
# @return [File,Dry::Files::MemoryFileSystem::Node] the opened file
|
132
|
+
#
|
133
|
+
# @raise [Dry::Files::IOError] in case of I/O error
|
134
|
+
def open(path, mode = OPEN_MODE, *args, &blk)
|
135
|
+
adapter.open(path, mode, *args, &blk)
|
136
|
+
end
|
137
|
+
|
114
138
|
# Temporary changes the current working directory of the process to the
|
115
139
|
# given path and yield the given block.
|
116
140
|
#
|
@@ -657,14 +681,92 @@ module Dry
|
|
657
681
|
# # end
|
658
682
|
# # end
|
659
683
|
def inject_line_at_block_bottom(path, target, *contents)
|
660
|
-
content
|
661
|
-
starting
|
662
|
-
line
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
684
|
+
content = adapter.readlines(path)
|
685
|
+
starting = index(content, path, target)
|
686
|
+
line = content[starting]
|
687
|
+
delimiter = if line.match?(INLINE_OPEN_BLOCK_MATCHER)
|
688
|
+
INLINE_BLOCK_DELIMITER
|
689
|
+
else
|
690
|
+
BLOCK_DELIMITER
|
691
|
+
end
|
692
|
+
target = content[starting..]
|
693
|
+
ending = closing_block_index(target, starting, path, line, delimiter)
|
694
|
+
offset = SPACE * (content[ending][SPACE_MATCHER].bytesize + INDENTATION)
|
695
|
+
|
696
|
+
contents = Array(contents).flatten
|
697
|
+
contents = _offset_block_lines(contents, offset)
|
698
|
+
|
699
|
+
content.insert(ending, contents)
|
700
|
+
write(path, content)
|
701
|
+
end
|
702
|
+
|
703
|
+
# Inject `contents` in `path` at the bottom of the Ruby class that matches `target`.
|
704
|
+
# The given `contents` will appear at the BOTTOM of the Ruby class.
|
705
|
+
#
|
706
|
+
# @param path [String,Pathname] the path to file
|
707
|
+
# @param target [String,Regexp] the target matcher for Ruby class
|
708
|
+
# @param contents [String,Array<String>] the contents to inject
|
709
|
+
#
|
710
|
+
# @raise [Dry::Files::IOError] in case of I/O error
|
711
|
+
# @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
|
712
|
+
#
|
713
|
+
# @since 0.4.0
|
714
|
+
# @api public
|
715
|
+
#
|
716
|
+
# @example Inject a single line
|
717
|
+
# require "dry/files"
|
718
|
+
#
|
719
|
+
# files = Dry::Files.new
|
720
|
+
# path = "config/application.rb"
|
721
|
+
#
|
722
|
+
# File.read(path)
|
723
|
+
# # # frozen_string_literal: true
|
724
|
+
# #
|
725
|
+
# # class Application
|
726
|
+
# # end
|
727
|
+
#
|
728
|
+
# # inject a single line
|
729
|
+
# files.inject_line_at_class_bottom(path, /Application/, %(attr_accessor :name))
|
730
|
+
#
|
731
|
+
# File.read(path)
|
732
|
+
# # # frozen_string_literal: true
|
733
|
+
# #
|
734
|
+
# # class Application
|
735
|
+
# # attr_accessor :name
|
736
|
+
# # end
|
737
|
+
#
|
738
|
+
# @example Inject multiple lines
|
739
|
+
# require "dry/files"
|
740
|
+
#
|
741
|
+
# files = Dry::Files.new
|
742
|
+
# path = "math.rb"
|
743
|
+
#
|
744
|
+
# File.read(path)
|
745
|
+
# # # frozen_string_literal: true
|
746
|
+
# #
|
747
|
+
# # class Math
|
748
|
+
# # end
|
749
|
+
#
|
750
|
+
# # inject multiple lines
|
751
|
+
# files.inject_line_at_class_bottom(path,
|
752
|
+
# /Math/,
|
753
|
+
# ["def sum(a, b)", " a + b", "end"])
|
754
|
+
#
|
755
|
+
# File.read(path)
|
756
|
+
# # # frozen_string_literal: true
|
757
|
+
# #
|
758
|
+
# # class Math
|
759
|
+
# # def sum(a, b)
|
760
|
+
# # a + b
|
761
|
+
# # end
|
762
|
+
# # end
|
763
|
+
def inject_line_at_class_bottom(path, target, *contents)
|
764
|
+
content = adapter.readlines(path)
|
765
|
+
starting = index(content, path, target)
|
766
|
+
line = content[starting]
|
767
|
+
target = content[starting..]
|
768
|
+
ending = closing_class_index(target, starting, path, line, BLOCK_DELIMITER)
|
769
|
+
offset = SPACE * (content[ending][SPACE_MATCHER].bytesize + INDENTATION)
|
668
770
|
|
669
771
|
contents = Array(contents).flatten
|
670
772
|
contents = _offset_block_lines(contents, offset)
|
@@ -736,11 +838,38 @@ module Dry
|
|
736
838
|
|
737
839
|
private
|
738
840
|
|
841
|
+
# @since 0.3.0
|
842
|
+
# @api private
|
843
|
+
class Delimiter
|
844
|
+
# @since 0.3.0
|
845
|
+
# @api private
|
846
|
+
attr_reader :opening, :closing
|
847
|
+
|
848
|
+
# @since 0.3.0
|
849
|
+
# @api private
|
850
|
+
def initialize(name, opening, closing)
|
851
|
+
@name = name
|
852
|
+
@opening = opening
|
853
|
+
@closing = closing
|
854
|
+
freeze
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
739
858
|
# @since 0.1.0
|
740
859
|
# @api private
|
741
860
|
NEW_LINE = $/ # rubocop:disable Style/SpecialGlobalVars
|
742
861
|
private_constant :NEW_LINE
|
743
862
|
|
863
|
+
# @since 0.3.0
|
864
|
+
# @api private
|
865
|
+
NEW_LINE_MATCHER = /#{NEW_LINE}\z/.freeze
|
866
|
+
private_constant :NEW_LINE_MATCHER
|
867
|
+
|
868
|
+
# @since 0.3.0
|
869
|
+
# @api private
|
870
|
+
EMPTY_LINE = /\A\z/.freeze
|
871
|
+
private_constant :EMPTY_LINE
|
872
|
+
|
744
873
|
# @since 0.1.0
|
745
874
|
# @api private
|
746
875
|
CONTENT_OFFSET = 1
|
@@ -761,21 +890,42 @@ module Dry
|
|
761
890
|
SPACE_MATCHER = /\A[[:space:]]*/.freeze
|
762
891
|
private_constant :SPACE_MATCHER
|
763
892
|
|
764
|
-
# @since 0.
|
893
|
+
# @since 0.3.0
|
765
894
|
# @api private
|
766
|
-
|
767
|
-
private_constant :
|
895
|
+
INLINE_OPEN_BLOCK = "{"
|
896
|
+
private_constant :INLINE_OPEN_BLOCK
|
768
897
|
|
769
898
|
# @since 0.1.0
|
770
899
|
# @api private
|
771
900
|
INLINE_CLOSE_BLOCK = "}"
|
772
901
|
private_constant :INLINE_CLOSE_BLOCK
|
773
902
|
|
903
|
+
# @since 0.3.0
|
904
|
+
# @api private
|
905
|
+
OPEN_BLOCK = "do"
|
906
|
+
private_constant :OPEN_BLOCK
|
907
|
+
|
774
908
|
# @since 0.1.0
|
775
909
|
# @api private
|
776
910
|
CLOSE_BLOCK = "end"
|
777
911
|
private_constant :CLOSE_BLOCK
|
778
912
|
|
913
|
+
# @since 0.1.0
|
914
|
+
# @api private
|
915
|
+
INLINE_OPEN_BLOCK_MATCHER = INLINE_CLOSE_BLOCK
|
916
|
+
private_constant :INLINE_OPEN_BLOCK_MATCHER
|
917
|
+
|
918
|
+
# @since 0.3.0
|
919
|
+
# @api private
|
920
|
+
INLINE_BLOCK_DELIMITER = Delimiter.new("InlineBlockDelimiter",
|
921
|
+
INLINE_OPEN_BLOCK, INLINE_CLOSE_BLOCK)
|
922
|
+
private_constant :INLINE_BLOCK_DELIMITER
|
923
|
+
|
924
|
+
# @since 0.3.0
|
925
|
+
# @api private
|
926
|
+
BLOCK_DELIMITER = Delimiter.new("BlockDelimiter", OPEN_BLOCK, CLOSE_BLOCK)
|
927
|
+
private_constant :BLOCK_DELIMITER
|
928
|
+
|
779
929
|
# @since 0.1.0
|
780
930
|
# @api private
|
781
931
|
attr_reader :adapter
|
@@ -812,6 +962,25 @@ module Dry
|
|
812
962
|
raise MissingTargetError.new(target, path)
|
813
963
|
end
|
814
964
|
|
965
|
+
# @since 0.3.0
|
966
|
+
# @api private
|
967
|
+
def closing_block_index(content, starting, path, target, delimiter, count_offset = 0) # rubocop:disable Metrics/ParameterLists
|
968
|
+
blocks_count = content.count { |line| line.match?(delimiter.opening) } + count_offset
|
969
|
+
matching_line = content.find do |line|
|
970
|
+
blocks_count -= 1 if line.match?(delimiter.closing)
|
971
|
+
line if blocks_count.zero?
|
972
|
+
end
|
973
|
+
|
974
|
+
(content.index(matching_line) or
|
975
|
+
raise MissingTargetError.new(target, path)) + starting
|
976
|
+
end
|
977
|
+
|
978
|
+
# @since 0.4.0
|
979
|
+
# @api private
|
980
|
+
def closing_class_index(content, starting, path, target, delimiter)
|
981
|
+
closing_block_index(content, starting, path, target, delimiter, 1)
|
982
|
+
end
|
983
|
+
|
815
984
|
# @since 0.1.0
|
816
985
|
# @api private
|
817
986
|
def _inject_line_before(path, target, contents, finder)
|
@@ -839,6 +1008,8 @@ module Dry
|
|
839
1008
|
if line.match?(NEW_LINE)
|
840
1009
|
line = line.split(NEW_LINE)
|
841
1010
|
_offset_block_lines(line, offset)
|
1011
|
+
elsif line.match?(EMPTY_LINE)
|
1012
|
+
line + NEW_LINE
|
842
1013
|
else
|
843
1014
|
offset + line + NEW_LINE
|
844
1015
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-files
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rake
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '13.0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '13.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rspec
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,20 +24,6 @@ dependencies:
|
|
38
24
|
- - "~>"
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: '3.10'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rubocop
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.12'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.12'
|
55
27
|
description: file utilities
|
56
28
|
email:
|
57
29
|
- me@lucaguidi.com
|
@@ -77,7 +49,7 @@ licenses:
|
|
77
49
|
- MIT
|
78
50
|
metadata:
|
79
51
|
allowed_push_host: https://rubygems.org
|
80
|
-
changelog_uri: https://github.com/dry-rb/dry-files/blob/
|
52
|
+
changelog_uri: https://github.com/dry-rb/dry-files/blob/main/CHANGELOG.md
|
81
53
|
source_code_uri: https://github.com/dry-rb/dry-files
|
82
54
|
bug_tracker_uri: https://github.com/dry-rb/dry-files/issues
|
83
55
|
post_install_message:
|