sigterm_extensions 0.0.4

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +17 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/bin/ctxirb +156 -0
  7. data/lib/git.rb +166 -0
  8. data/lib/git/LICENSE +21 -0
  9. data/lib/git/author.rb +14 -0
  10. data/lib/git/base.rb +551 -0
  11. data/lib/git/base/factory.rb +75 -0
  12. data/lib/git/branch.rb +126 -0
  13. data/lib/git/branches.rb +71 -0
  14. data/lib/git/config.rb +22 -0
  15. data/lib/git/diff.rb +159 -0
  16. data/lib/git/index.rb +5 -0
  17. data/lib/git/lib.rb +1041 -0
  18. data/lib/git/log.rb +128 -0
  19. data/lib/git/object.rb +312 -0
  20. data/lib/git/path.rb +31 -0
  21. data/lib/git/remote.rb +36 -0
  22. data/lib/git/repository.rb +6 -0
  23. data/lib/git/stash.rb +27 -0
  24. data/lib/git/stashes.rb +55 -0
  25. data/lib/git/status.rb +199 -0
  26. data/lib/git/version.rb +5 -0
  27. data/lib/git/working_directory.rb +4 -0
  28. data/lib/sigterm_extensions.rb +75 -0
  29. data/lib/sigterm_extensions/all.rb +12 -0
  30. data/lib/sigterm_extensions/backtrace_cleaner.rb +129 -0
  31. data/lib/sigterm_extensions/callbacks.rb +847 -0
  32. data/lib/sigterm_extensions/concern.rb +169 -0
  33. data/lib/sigterm_extensions/configurable.rb +38 -0
  34. data/lib/sigterm_extensions/core_ext.rb +4 -0
  35. data/lib/sigterm_extensions/core_ext/array.rb +3 -0
  36. data/lib/sigterm_extensions/core_ext/array/extract.rb +19 -0
  37. data/lib/sigterm_extensions/core_ext/array/extract_options.rb +29 -0
  38. data/lib/sigterm_extensions/core_ext/class.rb +3 -0
  39. data/lib/sigterm_extensions/core_ext/class/attribute.rb +139 -0
  40. data/lib/sigterm_extensions/core_ext/class/attribute_accessors.rb +4 -0
  41. data/lib/sigterm_extensions/core_ext/class/subclasses.rb +52 -0
  42. data/lib/sigterm_extensions/core_ext/custom.rb +12 -0
  43. data/lib/sigterm_extensions/core_ext/digest.rb +3 -0
  44. data/lib/sigterm_extensions/core_ext/digest/uuid.rb +51 -0
  45. data/lib/sigterm_extensions/core_ext/enumerable.rb +232 -0
  46. data/lib/sigterm_extensions/core_ext/file.rb +3 -0
  47. data/lib/sigterm_extensions/core_ext/file/atomic.rb +68 -0
  48. data/lib/sigterm_extensions/core_ext/hash.rb +3 -0
  49. data/lib/sigterm_extensions/core_ext/hash/deep_merge.rb +41 -0
  50. data/lib/sigterm_extensions/core_ext/hash/deep_transform_values.rb +44 -0
  51. data/lib/sigterm_extensions/core_ext/hash/except.rb +22 -0
  52. data/lib/sigterm_extensions/core_ext/hash/keys.rb +141 -0
  53. data/lib/sigterm_extensions/core_ext/hash/reverse_merge.rb +23 -0
  54. data/lib/sigterm_extensions/core_ext/hash/slice.rb +24 -0
  55. data/lib/sigterm_extensions/core_ext/kernel.rb +3 -0
  56. data/lib/sigterm_extensions/core_ext/kernel/concern.rb +12 -0
  57. data/lib/sigterm_extensions/core_ext/kernel/reporting.rb +43 -0
  58. data/lib/sigterm_extensions/core_ext/kernel/singleton_class.rb +6 -0
  59. data/lib/sigterm_extensions/core_ext/load_error.rb +7 -0
  60. data/lib/sigterm_extensions/core_ext/module.rb +3 -0
  61. data/lib/sigterm_extensions/core_ext/module/aliasing.rb +29 -0
  62. data/lib/sigterm_extensions/core_ext/module/anonymous.rb +28 -0
  63. data/lib/sigterm_extensions/core_ext/module/attr_internal.rb +36 -0
  64. data/lib/sigterm_extensions/core_ext/module/attribute_accessors.rb +208 -0
  65. data/lib/sigterm_extensions/core_ext/module/attribute_accessors_per_thread.rb +146 -0
  66. data/lib/sigterm_extensions/core_ext/module/concerning.rb +132 -0
  67. data/lib/sigterm_extensions/core_ext/module/delegation.rb +319 -0
  68. data/lib/sigterm_extensions/core_ext/module/redefine_method.rb +38 -0
  69. data/lib/sigterm_extensions/core_ext/module/remove_method.rb +15 -0
  70. data/lib/sigterm_extensions/core_ext/name_error.rb +36 -0
  71. data/lib/sigterm_extensions/core_ext/object.rb +3 -0
  72. data/lib/sigterm_extensions/core_ext/object/blank.rb +153 -0
  73. data/lib/sigterm_extensions/core_ext/object/colors.rb +39 -0
  74. data/lib/sigterm_extensions/core_ext/object/duplicable.rb +47 -0
  75. data/lib/sigterm_extensions/core_ext/object/inclusion.rb +27 -0
  76. data/lib/sigterm_extensions/core_ext/object/instance_variables.rb +28 -0
  77. data/lib/sigterm_extensions/core_ext/object/methods.rb +61 -0
  78. data/lib/sigterm_extensions/core_ext/object/with_options.rb +80 -0
  79. data/lib/sigterm_extensions/core_ext/range.rb +3 -0
  80. data/lib/sigterm_extensions/core_ext/range/compare_range.rb +74 -0
  81. data/lib/sigterm_extensions/core_ext/range/conversions.rb +39 -0
  82. data/lib/sigterm_extensions/core_ext/range/overlaps.rb +8 -0
  83. data/lib/sigterm_extensions/core_ext/securerandom.rb +43 -0
  84. data/lib/sigterm_extensions/core_ext/string.rb +3 -0
  85. data/lib/sigterm_extensions/core_ext/string/access.rb +93 -0
  86. data/lib/sigterm_extensions/core_ext/string/filters.rb +143 -0
  87. data/lib/sigterm_extensions/core_ext/string/starts_ends_with.rb +4 -0
  88. data/lib/sigterm_extensions/core_ext/string/strip.rb +25 -0
  89. data/lib/sigterm_extensions/core_ext/tryable.rb +132 -0
  90. data/lib/sigterm_extensions/descendants_tracker.rb +108 -0
  91. data/lib/sigterm_extensions/gem_methods.rb +47 -0
  92. data/lib/sigterm_extensions/hash_binding.rb +16 -0
  93. data/lib/sigterm_extensions/inflector.rb +339 -0
  94. data/lib/sigterm_extensions/inflector/acronyms.rb +42 -0
  95. data/lib/sigterm_extensions/inflector/inflections.rb +249 -0
  96. data/lib/sigterm_extensions/inflector/inflections/defaults.rb +117 -0
  97. data/lib/sigterm_extensions/inflector/rules.rb +37 -0
  98. data/lib/sigterm_extensions/inflector/version.rb +8 -0
  99. data/lib/sigterm_extensions/interactive_editor.rb +120 -0
  100. data/lib/sigterm_extensions/lazy.rb +34 -0
  101. data/lib/sigterm_extensions/lazy_load_hooks.rb +79 -0
  102. data/lib/sigterm_extensions/option_merger.rb +32 -0
  103. data/lib/sigterm_extensions/ordered_hash.rb +48 -0
  104. data/lib/sigterm_extensions/ordered_options.rb +83 -0
  105. data/lib/sigterm_extensions/paths.rb +235 -0
  106. data/lib/sigterm_extensions/per_thread_registry.rb +58 -0
  107. data/lib/sigterm_extensions/proxy_object.rb +14 -0
  108. data/lib/sigterm_extensions/staging/boot.rb +31 -0
  109. data/lib/sigterm_extensions/staging/boot/bundler_patch.rb +24 -0
  110. data/lib/sigterm_extensions/staging/boot/command.rb +26 -0
  111. data/lib/sigterm_extensions/staging/boot/gemfile_next_auto_sync.rb +79 -0
  112. data/lib/sigterm_extensions/version.rb +4 -0
  113. data/lib/sigterm_extensions/wrappable.rb +16 -0
  114. data/sigterm_extensions.gemspec +42 -0
  115. data/templates/dotpryrc.rb.erb +124 -0
  116. metadata +315 -0
@@ -0,0 +1,120 @@
1
+ # vim: set syntax=ruby :
2
+ # Giles Bowkett, Greg Brown, and several audience members from Giles' Ruby East presentation.
3
+ # http://gilesbowkett.blogspot.com/2007/10/use-vi-or-any-text-editor-from-within.html
4
+
5
+ require 'irb'
6
+ require 'fileutils'
7
+ require 'tempfile'
8
+ require 'shellwords'
9
+ require 'yaml'
10
+
11
+ class InteractiveEditor
12
+ VERSION = '0.0.11'
13
+ EDITORS = Hash.new { |h,k| h[k] = InteractiveEditor.new(k) }
14
+
15
+ attr_accessor :editor
16
+
17
+ def initialize(editor)
18
+ @editor = editor.to_s
19
+ end
20
+
21
+ def edit(object, file=nil)
22
+ object = object == TOPLEVEL_BINDING.eval('self') ? nil : object
23
+
24
+ current_file = if file
25
+ FileUtils.touch(file) unless File.exist?(file)
26
+ File.new(file)
27
+ else
28
+ if @file && File.exist?(@file.path) && !object
29
+ @file
30
+ else
31
+ Tempfile.new( object ? ["yobj_tempfile", ".yml"] : ["irb_tempfile", ".rb"] )
32
+ end
33
+ end
34
+
35
+ if object
36
+ File.open( current_file.path, 'w' ) { |f| f << object.to_yaml }
37
+ else
38
+ @file = current_file
39
+ mtime = File.stat(@file.path).mtime
40
+ end
41
+
42
+ args = Shellwords.shellwords(@editor) #parse @editor as arguments could be complex
43
+ args << current_file.path
44
+ current_file.close rescue nil
45
+ Exec.system(*args)
46
+
47
+ if object
48
+ File.exists?(current_file.path) ? YAML.load_file(current_file.path) : object
49
+ elsif mtime < File.stat(@file.path).mtime
50
+ execute
51
+ end
52
+ end
53
+
54
+ def execute
55
+ eval(IO.read(@file.path), TOPLEVEL_BINDING)
56
+ end
57
+
58
+ def self.edit(editor, self_, file=nil)
59
+ find_editor[editor].edit(self_, file)
60
+ end
61
+
62
+ def self.find_editor
63
+ #maybe serialise last file to disk, for recovery
64
+ if defined?(Pry) and IRB == Pry
65
+ IRB.config.interactive_editors ||= EDITORS
66
+ else
67
+ IRB.conf[:interactive_editors] ||= EDITORS
68
+ end
69
+ end
70
+
71
+ module Exec
72
+ module Java
73
+ def system(file, *args)
74
+ require 'spoon'
75
+ Process.waitpid(Spoon.spawnp(file, *args))
76
+ rescue Errno::ECHILD => e
77
+ raise "error exec'ing #{file}: #{e}"
78
+ end
79
+ end
80
+
81
+ module MRI
82
+ def system(file, *args)
83
+ Kernel::system(file, *args) #or raise "error exec'ing #{file}: #{$?}"
84
+ end
85
+ end
86
+
87
+ extend RUBY_PLATFORM =~ /java/ ? Java : MRI
88
+ end
89
+
90
+ module Editors
91
+ {
92
+ :vi => nil,
93
+ :vim => nil,
94
+ :nvim => nil,
95
+ :emacs => nil,
96
+ :nano => nil,
97
+ :mate => 'mate -w',
98
+ :subl => 'subl -wn',
99
+ :mvim => 'mvim -g -f' + case ENV['TERM_PROGRAM']
100
+ when 'iTerm.app'; ' -c "au VimLeave * !open -a iTerm"'
101
+ when 'Apple_Terminal'; ' -c "au VimLeave * !open -a Terminal"'
102
+ else '' #don't do tricky things if we don't know the Term
103
+ end
104
+ }.each do |k,v|
105
+ define_method(k) do |*args|
106
+ InteractiveEditor.edit(v || k, self, *args)
107
+ end
108
+ end
109
+
110
+ def ed(*args)
111
+ if ENV['EDITOR'].to_s.size > 0
112
+ InteractiveEditor.edit(ENV['EDITOR'], self, *args)
113
+ else
114
+ raise "You need to set the EDITOR environment variable first"
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ include InteractiveEditor::Editors
@@ -0,0 +1,34 @@
1
+ module SigtermExtensions
2
+ # A class that can be wrapped around an expensive method call so it's only
3
+ # executed when actually needed.
4
+ #
5
+ # Usage:
6
+ #
7
+ # object = SigtermExtensions::Lazy.new { some_expensive_work_here }
8
+ #
9
+ # object['foo']
10
+ # object.bar
11
+ class Lazy < BasicObject
12
+ def initialize(&block)
13
+ @block = block
14
+ end
15
+
16
+ def method_missing(name, *args, &block)
17
+ __evaluate__
18
+
19
+ @result.__send__(name, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
20
+ end
21
+
22
+ def respond_to_missing?(name, include_private = false)
23
+ __evaluate__
24
+
25
+ @result.respond_to?(name, include_private) || super
26
+ end
27
+
28
+ private
29
+
30
+ def __evaluate__
31
+ @result = @block.call unless defined?(@result)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ module SigtermExtensions
2
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
3
+ # making the app boot faster. Because of this feature now there is no need to
4
+ # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
5
+ # configuration. Instead a hook is registered that applies configuration once
6
+ # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
7
+ # used as example but this feature can be applied elsewhere too.
8
+ #
9
+ # Here is an example where +on_load+ method is called to register a hook.
10
+ #
11
+ # initializer 'active_record.initialize_timezone' do
12
+ # ActiveSupport.on_load(:active_record) do
13
+ # self.time_zone_aware_attributes = true
14
+ # self.default_timezone = :utc
15
+ # end
16
+ # end
17
+ #
18
+ # When the entirety of +ActiveRecord::Base+ has been
19
+ # evaluated then +run_load_hooks+ is invoked. The very last line of
20
+ # +ActiveRecord::Base+ is:
21
+ #
22
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
23
+ module LazyLoadHooks
24
+ def self.extended(base) # :nodoc:
25
+ base.class_eval do
26
+ @load_hooks = Hash.new { |h, k| h[k] = [] }
27
+ @loaded = Hash.new { |h, k| h[k] = [] }
28
+ @run_once = Hash.new { |h, k| h[k] = [] }
29
+ end
30
+ end
31
+
32
+ # Declares a block that will be executed when a Rails component is fully
33
+ # loaded.
34
+ #
35
+ # Options:
36
+ #
37
+ # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
38
+ # * <tt>:run_once</tt> - Given +block+ will run only once.
39
+ def on_load(name, options = {}, &block)
40
+ @loaded[name].each do |base|
41
+ execute_hook(name, base, options, block)
42
+ end
43
+
44
+ @load_hooks[name] << [block, options]
45
+ end
46
+
47
+ def run_load_hooks(name, base = Object)
48
+ @loaded[name] << base
49
+ @load_hooks[name].each do |hook, options|
50
+ execute_hook(name, base, options, hook)
51
+ end
52
+ end
53
+
54
+ private
55
+ def with_execution_control(name, block, once)
56
+ unless @run_once[name].include?(block)
57
+ @run_once[name] << block if once
58
+
59
+ yield
60
+ end
61
+ end
62
+
63
+ def execute_hook(name, base, options, block)
64
+ with_execution_control(name, block, options[:run_once]) do
65
+ if options[:yield]
66
+ block.call(base)
67
+ else
68
+ if base.is_a?(Module)
69
+ base.class_eval(&block)
70
+ else
71
+ base.instance_eval(&block)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ extend LazyLoadHooks
79
+ end
@@ -0,0 +1,32 @@
1
+ require "sigterm_extensions/core_ext/hash/deep_merge"
2
+
3
+ module SigtermExtensions
4
+ class OptionMerger #:nodoc:
5
+ instance_methods.each do |method|
6
+ undef_method(method) unless method.match?(/^(__|instance_eval|class|object_id)/)
7
+ end
8
+
9
+ def initialize(context, options)
10
+ @context, @options = context, options
11
+ end
12
+
13
+ private
14
+ def method_missing(method, *arguments, &block)
15
+ options = nil
16
+ if arguments.first.is_a?(Proc)
17
+ proc = arguments.pop
18
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
19
+ elsif arguments.last.respond_to?(:to_hash)
20
+ options = @options.deep_merge(arguments.pop)
21
+ else
22
+ options = @options
23
+ end
24
+
25
+ if options
26
+ @context.__send__(method, *arguments, **options, &block)
27
+ else
28
+ @context.__send__(method, *arguments, &block)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ require "yaml"
2
+
3
+ YAML.add_builtin_type("omap") do |type, val|
4
+ SigtermExtensions::OrderedHash[val.map { |v| v.to_a.first }]
5
+ end
6
+
7
+ module SigtermExtensions
8
+ # DEPRECATED: <tt>SigtermExtensions::OrderedHash</tt> implements a hash that preserves
9
+ # insertion order.
10
+ #
11
+ # oh = SigtermExtensions::OrderedHash.new
12
+ # oh[:a] = 1
13
+ # oh[:b] = 2
14
+ # oh.keys # => [:a, :b], this order is guaranteed
15
+ #
16
+ # Also, maps the +omap+ feature for YAML files
17
+ # (See https://yaml.org/type/omap.html) to support ordered items
18
+ # when loading from yaml.
19
+ #
20
+ # <tt>SigtermExtensions::OrderedHash</tt> is namespaced to prevent conflicts
21
+ # with other implementations.
22
+ class OrderedHash < ::Hash
23
+ def to_yaml_type
24
+ "!tag:yaml.org,2002:omap"
25
+ end
26
+
27
+ def encode_with(coder)
28
+ coder.represent_seq "!omap", map { |k, v| { k => v } }
29
+ end
30
+
31
+ def select(*args, &block)
32
+ dup.tap { |hash| hash.select!(*args, &block) }
33
+ end
34
+
35
+ def reject(*args, &block)
36
+ dup.tap { |hash| hash.reject!(*args, &block) }
37
+ end
38
+
39
+ def nested_under_indifferent_access
40
+ self
41
+ end
42
+
43
+ # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
44
+ def extractable_options?
45
+ true
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,83 @@
1
+ require "sigterm_extensions/core_ext/object/blank"
2
+
3
+ module SigtermExtensions
4
+ # Usually key value pairs are handled something like this:
5
+ #
6
+ # h = {}
7
+ # h[:boy] = 'John'
8
+ # h[:girl] = 'Mary'
9
+ # h[:boy] # => 'John'
10
+ # h[:girl] # => 'Mary'
11
+ # h[:dog] # => nil
12
+ #
13
+ # Using +OrderedOptions+, the above code could be reduced to:
14
+ #
15
+ # h = SigtermExtensions::OrderedOptions.new
16
+ # h.boy = 'John'
17
+ # h.girl = 'Mary'
18
+ # h.boy # => 'John'
19
+ # h.girl # => 'Mary'
20
+ # h.dog # => nil
21
+ #
22
+ # To raise an exception when the value is blank, append a
23
+ # bang to the key name, like:
24
+ #
25
+ # h.dog! # => raises KeyError: :dog is blank
26
+ #
27
+ class OrderedOptions < Hash
28
+ alias_method :_get, :[] # preserve the original #[] method
29
+ protected :_get # make it protected
30
+
31
+ def []=(key, value)
32
+ super(key.to_sym, value)
33
+ end
34
+
35
+ def [](key)
36
+ super(key.to_sym)
37
+ end
38
+
39
+ def method_missing(name, *args)
40
+ name_string = +name.to_s
41
+ if name_string.chomp!("=")
42
+ self[name_string] = args.first
43
+ else
44
+ bangs = name_string.chomp!("!")
45
+
46
+ if bangs
47
+ self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
48
+ else
49
+ self[name_string]
50
+ end
51
+ end
52
+ end
53
+
54
+ def respond_to_missing?(name, include_private)
55
+ true
56
+ end
57
+ end
58
+
59
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
60
+ # hash inherited from another hash.
61
+ #
62
+ # Use this if you already have some hash and you want to create a new one based on it.
63
+ #
64
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
65
+ # h.girl # => 'Mary'
66
+ # h.boy # => 'John'
67
+ class InheritableOptions < OrderedOptions
68
+ def initialize(parent = nil)
69
+ if parent.kind_of?(OrderedOptions)
70
+ # use the faster _get when dealing with OrderedOptions
71
+ super() { |h, k| parent._get(k) }
72
+ elsif parent
73
+ super() { |h, k| parent[k] }
74
+ else
75
+ super()
76
+ end
77
+ end
78
+
79
+ def inheritable_copy
80
+ self.class.new(self)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,235 @@
1
+ module SigtermExtensions
2
+ module Paths
3
+ # This object is an extended hash that behaves as root of the <tt>SigtermExtensions::Paths</tt> system.
4
+ # It allows you to collect information about how you want to structure your application
5
+ # paths through a Hash-like API. It requires you to give a physical path on initialization.
6
+ #
7
+ # root = Root.new "/SigtermExtensions"
8
+ # root.add "app/controllers", eager_load: true
9
+ #
10
+ # The above command creates a new root object and adds "app/controllers" as a path.
11
+ # This means we can get a <tt>SigtermExtensions::Paths::Path</tt> object back like below:
12
+ #
13
+ # path = root["app/controllers"]
14
+ # path.eager_load? # => true
15
+ # path.is_a?(SigtermExtensions::Paths::Path) # => true
16
+ #
17
+ # The +Path+ object is simply an enumerable and allows you to easily add extra paths:
18
+ #
19
+ # path.is_a?(Enumerable) # => true
20
+ # path.to_ary.inspect # => ["app/controllers"]
21
+ #
22
+ # path << "lib/controllers"
23
+ # path.to_ary.inspect # => ["app/controllers", "lib/controllers"]
24
+ #
25
+ # Notice that when you add a path using +add+, the path object created already
26
+ # contains the path with the same path value given to +add+. In some situations,
27
+ # you may not want this behavior, so you can give <tt>:with</tt> as option.
28
+ #
29
+ # root.add "config/routes", with: "config/routes.rb"
30
+ # root["config/routes"].inspect # => ["config/routes.rb"]
31
+ #
32
+ # The +add+ method accepts the following options as arguments:
33
+ # eager_load, autoload, autoload_once, and glob.
34
+ #
35
+ # Finally, the +Path+ object also provides a few helpers:
36
+ #
37
+ # root = Root.new "/SigtermExtensions"
38
+ # root.add "app/controllers"
39
+ #
40
+ # root["app/controllers"].expanded # => ["/SigtermExtensions/app/controllers"]
41
+ # root["app/controllers"].existent # => ["/SigtermExtensions/app/controllers"]
42
+ #
43
+ # Check the <tt>SigtermExtensions::Paths::Path</tt> documentation for more information.
44
+ class Root
45
+ attr_accessor :path
46
+
47
+ def initialize(path)
48
+ @path = path
49
+ @root = {}
50
+ end
51
+
52
+ def []=(path, value)
53
+ glob = self[path] ? self[path].glob : nil
54
+ add(path, with: value, glob: glob)
55
+ end
56
+
57
+ def add(path, options = {})
58
+ with = Array(options.fetch(:with, path))
59
+ @root[path] = Path.new(self, path, with, options)
60
+ end
61
+
62
+ def [](path)
63
+ @root[path]
64
+ end
65
+
66
+ def values
67
+ @root.values
68
+ end
69
+
70
+ def keys
71
+ @root.keys
72
+ end
73
+
74
+ def values_at(*list)
75
+ @root.values_at(*list)
76
+ end
77
+
78
+ def all_paths
79
+ values.tap(&:uniq!)
80
+ end
81
+
82
+ def autoload_once
83
+ filter_by(&:autoload_once?)
84
+ end
85
+
86
+ def eager_load
87
+ filter_by(&:eager_load?)
88
+ end
89
+
90
+ def autoload_paths
91
+ filter_by(&:autoload?)
92
+ end
93
+
94
+ def load_paths
95
+ filter_by(&:load_path?)
96
+ end
97
+
98
+ private
99
+ def filter_by(&block)
100
+ all_paths.find_all(&block).flat_map { |path|
101
+ paths = path.existent
102
+ paths - path.children.flat_map { |p| yield(p) ? [] : p.existent }
103
+ }.uniq
104
+ end
105
+ end
106
+
107
+ def new_root(r)
108
+ Root.new(r)
109
+ end
110
+
111
+ class Path
112
+ include Enumerable
113
+
114
+ attr_accessor :glob
115
+
116
+ def initialize(root, current, paths, options = {})
117
+ @paths = paths
118
+ @current = current
119
+ @root = root
120
+ @glob = options[:glob]
121
+ @exclude = options[:exclude]
122
+
123
+ options[:autoload_once] ? autoload_once! : skip_autoload_once!
124
+ options[:eager_load] ? eager_load! : skip_eager_load!
125
+ options[:autoload] ? autoload! : skip_autoload!
126
+ options[:load_path] ? load_path! : skip_load_path!
127
+ end
128
+
129
+ def absolute_current # :nodoc:
130
+ File.expand_path(@current, @root.path)
131
+ end
132
+
133
+ def children
134
+ keys = @root.keys.find_all { |k|
135
+ k.start_with?(@current) && k != @current
136
+ }
137
+ @root.values_at(*keys.sort)
138
+ end
139
+
140
+ def first
141
+ expanded.first
142
+ end
143
+
144
+ def last
145
+ expanded.last
146
+ end
147
+
148
+ %w(autoload_once eager_load autoload load_path).each do |m|
149
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
150
+ def #{m}! # def eager_load!
151
+ @#{m} = true # @eager_load = true
152
+ end # end
153
+ #
154
+ def skip_#{m}! # def skip_eager_load!
155
+ @#{m} = false # @eager_load = false
156
+ end # end
157
+ #
158
+ def #{m}? # def eager_load?
159
+ @#{m} # @eager_load
160
+ end # end
161
+ RUBY
162
+ end
163
+
164
+ def each(&block)
165
+ @paths.each(&block)
166
+ end
167
+
168
+ def <<(path)
169
+ @paths << path
170
+ end
171
+ alias :push :<<
172
+
173
+ def concat(paths)
174
+ @paths.concat paths
175
+ end
176
+
177
+ def unshift(*paths)
178
+ @paths.unshift(*paths)
179
+ end
180
+
181
+ def to_ary
182
+ @paths
183
+ end
184
+
185
+ def extensions # :nodoc:
186
+ $1.split(",") if @glob =~ /\{([\S]+)\}/
187
+ end
188
+
189
+ # Expands all paths against the root and return all unique values.
190
+ def expanded
191
+ raise "You need to set a path root" unless @root.path
192
+ result = []
193
+
194
+ each do |path|
195
+ path = File.expand_path(path, @root.path)
196
+
197
+ if @glob && File.directory?(path)
198
+ result.concat files_in(path)
199
+ else
200
+ result << path
201
+ end
202
+ end
203
+
204
+ result.uniq!
205
+ result
206
+ end
207
+
208
+ # Returns all expanded paths but only if they exist in the filesystem.
209
+ def existent
210
+ expanded.select do |f|
211
+ does_exist = File.exist?(f)
212
+
213
+ if !does_exist && File.symlink?(f)
214
+ raise "File #{f.inspect} is a symlink that does not point to a valid file"
215
+ end
216
+ does_exist
217
+ end
218
+ end
219
+
220
+ def existent_directories
221
+ expanded.select { |d| File.directory?(d) }
222
+ end
223
+
224
+ alias to_a expanded
225
+
226
+ private
227
+ def files_in(path)
228
+ files = Dir.glob(@glob, base: path)
229
+ files -= @exclude if @exclude
230
+ files.map! { |file| File.join(path, file) }
231
+ files.sort
232
+ end
233
+ end
234
+ end
235
+ end