dry-files 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- 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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62369326685ba2aa2719ff1ffd3bda010ac51f80e6f5094fde071c6ea440d407
|
4
|
+
data.tar.gz: 5f629c648d5d31f4d46bf7a32b80efc6a053e79990117978f9ec3e26a2a4490f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4729286d5f15c84b98d8349104a2bd4b89106c6f9c100af996687c2968a7f9cb890fb2cca90821031ab5074172fb36d9fa0ffc15840caa31d4cbc60e4af71d2b
|
7
|
+
data.tar.gz: cbbda5c073e563b48145035ca9f1ed3ac7deceea3c02b78060225e8710d9bbd6cfee7727bb67fd254791689fdcb763608fc8cfaf1e4e31bcd168e96a9a5ad666
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 0.3.0 2022-09-19
|
4
|
+
|
5
|
+
|
6
|
+
### Added
|
7
|
+
|
8
|
+
- Support for `inject_line_at_class_bottom` (via #13) (@jodosha)
|
9
|
+
|
10
|
+
|
11
|
+
[Compare v0.2.0...v0.3.0](https://github.com/dry-rb/dry-files/compare/v0.2.0...v0.3.0)
|
12
|
+
|
3
13
|
## 0.2.0 2022-07-10
|
4
14
|
|
5
15
|
|
@@ -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,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-files
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|