dry-files 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d22584a76b85c83b8d1d50e0b805335347f205f7ac7da1ea31dd538a5b459fd
4
- data.tar.gz: 4054a4f0bf0daa95cc3e23937d5acb9726606cf7974159146cd21787a186549b
3
+ metadata.gz: 62369326685ba2aa2719ff1ffd3bda010ac51f80e6f5094fde071c6ea440d407
4
+ data.tar.gz: 5f629c648d5d31f4d46bf7a32b80efc6a053e79990117978f9ec3e26a2a4490f
5
5
  SHA512:
6
- metadata.gz: d7881004a31c1106fb0a42b11015169fe1deac588aa1609ab3dd13c28c945f83cea7b722fb43250de665d862ec27f8f76071f88eac3b1567b2ea95a201cb8a4d
7
- data.tar.gz: 3d2c8d0f875b00e889d0020a5023443b686529548bf1b28ca5007085bd88d4581affc6ad1a2d5319f861dfc768cef8e486f45959204478f05670121a239db124
6
+ metadata.gz: 4729286d5f15c84b98d8349104a2bd4b89106c6f9c100af996687c2968a7f9cb890fb2cca90821031ab5074172fb36d9fa0ffc15840caa31d4cbc60e4af71d2b
7
+ data.tar.gz: cbbda5c073e563b48145035ca9f1ed3ac7deceea3c02b78060225e8710d9bbd6cfee7727bb67fd254791689fdcb763608fc8cfaf1e4e31bcd168e96a9a5ad666
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
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
+
13
+ ## 0.2.0 2022-07-10
14
+
15
+
16
+ ### Added
17
+
18
+ - Dry::Files#append to create intermediate directory and touch destination file (via #8) (@jodosha)
19
+
20
+
21
+ [Compare v0.1.0...v0.2.0](https://github.com/dry-rb/dry-files/compare/v0.1.0...v0.2.0)
22
+
3
23
  ## 0.1.0 2021-05-04
4
24
 
5
25
  Initial release
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ <!--- this file is synced from dry-rb/template-gem project -->
1
2
  [gem]: https://rubygems.org/gems/dry-files
2
3
  [actions]: https://github.com/dry-rb/dry-files/actions
3
4
  [codacy]: https://www.codacy.com/gh/dry-rb/dry-files
@@ -10,19 +11,19 @@
10
11
  [![CI Status](https://github.com/dry-rb/dry-files/workflows/ci/badge.svg)][actions]
11
12
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/71200ee8d70b412c9e21c20b8b3b3688)][codacy]
12
13
  [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/71200ee8d70b412c9e21c20b8b3b3688)][codacy]
13
- [![Inline docs](http://inch-ci.org/github/dry-rb/dry-files.svg?branch=master)][inchpages]
14
+ [![Inline docs](http://inch-ci.org/github/dry-rb/dry-files.svg?branch=main)][inchpages]
14
15
 
15
16
  ## Links
16
17
 
17
- * [User documentation](http://dry-rb.org/gems/dry-files)
18
+ * [User documentation](https://dry-rb.org/gems/dry-files)
18
19
  * [API documentation](http://rubydoc.info/gems/dry-files)
19
20
 
20
21
  ## Supported Ruby versions
21
22
 
22
23
  This library officially supports the following Ruby versions:
23
24
 
24
- * MRI >= `2.5`
25
- * jruby >= `9.2`
25
+ * MRI `>= 2.7.0`
26
+ * jruby `>= 9.3` (postponed until 2.7 is supported)
26
27
 
27
28
  ## License
28
29
 
data/dry-files.gemspec CHANGED
@@ -1,33 +1,35 @@
1
1
  # frozen_string_literal: true
2
- # this file is managed by dry-rb/devtools project
3
2
 
4
- lib = File.expand_path('lib', __dir__)
3
+ # this file is synced from dry-rb/template-gem project
4
+
5
+ lib = File.expand_path("lib", __dir__)
5
6
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
- require 'dry/files/version'
7
+ require "dry/files/version"
7
8
 
8
9
  Gem::Specification.new do |spec|
9
- spec.name = 'dry-files'
10
+ spec.name = "dry-files"
10
11
  spec.authors = ["Luca Guidi"]
11
12
  spec.email = ["me@lucaguidi.com"]
12
- spec.license = 'MIT'
13
+ spec.license = "MIT"
13
14
  spec.version = Dry::Files::VERSION.dup
14
15
 
15
16
  spec.summary = "file utilities"
16
17
  spec.description = spec.summary
17
- spec.homepage = 'https://dry-rb.org/gems/dry-files'
18
+ spec.homepage = "https://dry-rb.org/gems/dry-files"
18
19
  spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-files.gemspec", "lib/**/*"]
19
- spec.bindir = 'bin'
20
+ spec.bindir = "bin"
20
21
  spec.executables = []
21
- spec.require_paths = ['lib']
22
+ spec.require_paths = ["lib"]
22
23
 
23
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
- spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-files/blob/master/CHANGELOG.md'
25
- spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-files'
26
- spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-files/issues'
24
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
25
+ spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-files/blob/master/CHANGELOG.md"
26
+ spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-files"
27
+ spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-files/issues"
27
28
 
28
- spec.required_ruby_version = ">= 2.5.0"
29
+ spec.required_ruby_version = ">= 2.7.0"
29
30
 
30
31
  # to update dependencies edit project.yml
32
+
31
33
  spec.add_development_dependency "rake", "~> 13.0"
32
34
  spec.add_development_dependency "rspec", "~> 3.10"
33
35
  spec.add_development_dependency "rubocop", "~> 1.12"
@@ -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 = OPEN_MODE, *args, &blk)
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(*content)
219
- @content = StringIO.new(content.join($RS))
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 = nil
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, *, &blk)
40
+ def open(path, *)
40
41
  file = touch(path)
41
- blk.call(file)
42
+
43
+ if block_given?
44
+ yield file
45
+ else
46
+ file
47
+ end
42
48
  end
43
49
 
44
50
  # Read file contents
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  class Files
5
- VERSION = "0.1.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
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
  #
@@ -307,6 +331,7 @@ module Dry
307
331
  # @api public
308
332
  def append(path, contents)
309
333
  mkdir_p(path)
334
+ touch(path)
310
335
 
311
336
  content = adapter.readlines(path)
312
337
  content << newline unless newline?(content.last)
@@ -656,14 +681,92 @@ module Dry
656
681
  # # end
657
682
  # # end
658
683
  def inject_line_at_block_bottom(path, target, *contents)
659
- content = adapter.readlines(path)
660
- starting = index(content, path, target)
661
- line = content[starting]
662
- size = line[SPACE_MATCHER].bytesize
663
- closing = (SPACE * size) +
664
- (target.match?(INLINE_OPEN_BLOCK_MATCHER) ? INLINE_CLOSE_BLOCK : CLOSE_BLOCK)
665
- ending = starting + index(content[starting..-CONTENT_OFFSET], path, closing)
666
- offset = SPACE * (content[ending][SPACE_MATCHER].bytesize + INDENTATION)
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)
667
770
 
668
771
  contents = Array(contents).flatten
669
772
  contents = _offset_block_lines(contents, offset)
@@ -735,11 +838,38 @@ module Dry
735
838
 
736
839
  private
737
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
+
738
858
  # @since 0.1.0
739
859
  # @api private
740
860
  NEW_LINE = $/ # rubocop:disable Style/SpecialGlobalVars
741
861
  private_constant :NEW_LINE
742
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
+
743
873
  # @since 0.1.0
744
874
  # @api private
745
875
  CONTENT_OFFSET = 1
@@ -760,21 +890,42 @@ module Dry
760
890
  SPACE_MATCHER = /\A[[:space:]]*/.freeze
761
891
  private_constant :SPACE_MATCHER
762
892
 
763
- # @since 0.1.0
893
+ # @since 0.3.0
764
894
  # @api private
765
- INLINE_OPEN_BLOCK_MATCHER = "{"
766
- private_constant :INLINE_OPEN_BLOCK_MATCHER
895
+ INLINE_OPEN_BLOCK = "{"
896
+ private_constant :INLINE_OPEN_BLOCK
767
897
 
768
898
  # @since 0.1.0
769
899
  # @api private
770
900
  INLINE_CLOSE_BLOCK = "}"
771
901
  private_constant :INLINE_CLOSE_BLOCK
772
902
 
903
+ # @since 0.3.0
904
+ # @api private
905
+ OPEN_BLOCK = "do"
906
+ private_constant :OPEN_BLOCK
907
+
773
908
  # @since 0.1.0
774
909
  # @api private
775
910
  CLOSE_BLOCK = "end"
776
911
  private_constant :CLOSE_BLOCK
777
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
+
778
929
  # @since 0.1.0
779
930
  # @api private
780
931
  attr_reader :adapter
@@ -788,7 +939,7 @@ module Dry
788
939
  # @since 0.1.0
789
940
  # @api private
790
941
  def newline?(content)
791
- content.end_with?(NEW_LINE)
942
+ content&.end_with?(NEW_LINE)
792
943
  end
793
944
 
794
945
  # @since 0.1.0
@@ -811,6 +962,25 @@ module Dry
811
962
  raise MissingTargetError.new(target, path)
812
963
  end
813
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
+
814
984
  # @since 0.1.0
815
985
  # @api private
816
986
  def _inject_line_before(path, target, contents, finder)
@@ -838,6 +1008,8 @@ module Dry
838
1008
  if line.match?(NEW_LINE)
839
1009
  line = line.split(NEW_LINE)
840
1010
  _offset_block_lines(line, offset)
1011
+ elsif line.match?(EMPTY_LINE)
1012
+ line + NEW_LINE
841
1013
  else
842
1014
  offset + line + NEW_LINE
843
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.1.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: 2021-05-04 00:00:00.000000000 Z
11
+ date: 2022-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -88,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
88
  requirements:
89
89
  - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: 2.5.0
91
+ version: 2.7.0
92
92
  required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="