sigterm_extensions 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +6 -0
- data/LICENSE.md +0 -0
- data/README.md +0 -0
- data/bin/ctxirb +156 -0
- data/lib/git.rb +166 -0
- data/lib/git/LICENSE +21 -0
- data/lib/git/author.rb +14 -0
- data/lib/git/base.rb +551 -0
- data/lib/git/base/factory.rb +75 -0
- data/lib/git/branch.rb +126 -0
- data/lib/git/branches.rb +71 -0
- data/lib/git/config.rb +22 -0
- data/lib/git/diff.rb +159 -0
- data/lib/git/index.rb +5 -0
- data/lib/git/lib.rb +1041 -0
- data/lib/git/log.rb +128 -0
- data/lib/git/object.rb +312 -0
- data/lib/git/path.rb +31 -0
- data/lib/git/remote.rb +36 -0
- data/lib/git/repository.rb +6 -0
- data/lib/git/stash.rb +27 -0
- data/lib/git/stashes.rb +55 -0
- data/lib/git/status.rb +199 -0
- data/lib/git/version.rb +5 -0
- data/lib/git/working_directory.rb +4 -0
- data/lib/sigterm_extensions.rb +75 -0
- data/lib/sigterm_extensions/all.rb +12 -0
- data/lib/sigterm_extensions/backtrace_cleaner.rb +129 -0
- data/lib/sigterm_extensions/callbacks.rb +847 -0
- data/lib/sigterm_extensions/concern.rb +169 -0
- data/lib/sigterm_extensions/configurable.rb +38 -0
- data/lib/sigterm_extensions/core_ext.rb +4 -0
- data/lib/sigterm_extensions/core_ext/array.rb +3 -0
- data/lib/sigterm_extensions/core_ext/array/extract.rb +19 -0
- data/lib/sigterm_extensions/core_ext/array/extract_options.rb +29 -0
- data/lib/sigterm_extensions/core_ext/class.rb +3 -0
- data/lib/sigterm_extensions/core_ext/class/attribute.rb +139 -0
- data/lib/sigterm_extensions/core_ext/class/attribute_accessors.rb +4 -0
- data/lib/sigterm_extensions/core_ext/class/subclasses.rb +52 -0
- data/lib/sigterm_extensions/core_ext/custom.rb +12 -0
- data/lib/sigterm_extensions/core_ext/digest.rb +3 -0
- data/lib/sigterm_extensions/core_ext/digest/uuid.rb +51 -0
- data/lib/sigterm_extensions/core_ext/enumerable.rb +232 -0
- data/lib/sigterm_extensions/core_ext/file.rb +3 -0
- data/lib/sigterm_extensions/core_ext/file/atomic.rb +68 -0
- data/lib/sigterm_extensions/core_ext/hash.rb +3 -0
- data/lib/sigterm_extensions/core_ext/hash/deep_merge.rb +41 -0
- data/lib/sigterm_extensions/core_ext/hash/deep_transform_values.rb +44 -0
- data/lib/sigterm_extensions/core_ext/hash/except.rb +22 -0
- data/lib/sigterm_extensions/core_ext/hash/keys.rb +141 -0
- data/lib/sigterm_extensions/core_ext/hash/reverse_merge.rb +23 -0
- data/lib/sigterm_extensions/core_ext/hash/slice.rb +24 -0
- data/lib/sigterm_extensions/core_ext/kernel.rb +3 -0
- data/lib/sigterm_extensions/core_ext/kernel/concern.rb +12 -0
- data/lib/sigterm_extensions/core_ext/kernel/reporting.rb +43 -0
- data/lib/sigterm_extensions/core_ext/kernel/singleton_class.rb +6 -0
- data/lib/sigterm_extensions/core_ext/load_error.rb +7 -0
- data/lib/sigterm_extensions/core_ext/module.rb +3 -0
- data/lib/sigterm_extensions/core_ext/module/aliasing.rb +29 -0
- data/lib/sigterm_extensions/core_ext/module/anonymous.rb +28 -0
- data/lib/sigterm_extensions/core_ext/module/attr_internal.rb +36 -0
- data/lib/sigterm_extensions/core_ext/module/attribute_accessors.rb +208 -0
- data/lib/sigterm_extensions/core_ext/module/attribute_accessors_per_thread.rb +146 -0
- data/lib/sigterm_extensions/core_ext/module/concerning.rb +132 -0
- data/lib/sigterm_extensions/core_ext/module/delegation.rb +319 -0
- data/lib/sigterm_extensions/core_ext/module/redefine_method.rb +38 -0
- data/lib/sigterm_extensions/core_ext/module/remove_method.rb +15 -0
- data/lib/sigterm_extensions/core_ext/name_error.rb +36 -0
- data/lib/sigterm_extensions/core_ext/object.rb +3 -0
- data/lib/sigterm_extensions/core_ext/object/blank.rb +153 -0
- data/lib/sigterm_extensions/core_ext/object/colors.rb +39 -0
- data/lib/sigterm_extensions/core_ext/object/duplicable.rb +47 -0
- data/lib/sigterm_extensions/core_ext/object/inclusion.rb +27 -0
- data/lib/sigterm_extensions/core_ext/object/instance_variables.rb +28 -0
- data/lib/sigterm_extensions/core_ext/object/methods.rb +61 -0
- data/lib/sigterm_extensions/core_ext/object/with_options.rb +80 -0
- data/lib/sigterm_extensions/core_ext/range.rb +3 -0
- data/lib/sigterm_extensions/core_ext/range/compare_range.rb +74 -0
- data/lib/sigterm_extensions/core_ext/range/conversions.rb +39 -0
- data/lib/sigterm_extensions/core_ext/range/overlaps.rb +8 -0
- data/lib/sigterm_extensions/core_ext/securerandom.rb +43 -0
- data/lib/sigterm_extensions/core_ext/string.rb +3 -0
- data/lib/sigterm_extensions/core_ext/string/access.rb +93 -0
- data/lib/sigterm_extensions/core_ext/string/filters.rb +143 -0
- data/lib/sigterm_extensions/core_ext/string/starts_ends_with.rb +4 -0
- data/lib/sigterm_extensions/core_ext/string/strip.rb +25 -0
- data/lib/sigterm_extensions/core_ext/tryable.rb +132 -0
- data/lib/sigterm_extensions/descendants_tracker.rb +108 -0
- data/lib/sigterm_extensions/gem_methods.rb +47 -0
- data/lib/sigterm_extensions/hash_binding.rb +16 -0
- data/lib/sigterm_extensions/inflector.rb +339 -0
- data/lib/sigterm_extensions/inflector/acronyms.rb +42 -0
- data/lib/sigterm_extensions/inflector/inflections.rb +249 -0
- data/lib/sigterm_extensions/inflector/inflections/defaults.rb +117 -0
- data/lib/sigterm_extensions/inflector/rules.rb +37 -0
- data/lib/sigterm_extensions/inflector/version.rb +8 -0
- data/lib/sigterm_extensions/interactive_editor.rb +120 -0
- data/lib/sigterm_extensions/lazy.rb +34 -0
- data/lib/sigterm_extensions/lazy_load_hooks.rb +79 -0
- data/lib/sigterm_extensions/option_merger.rb +32 -0
- data/lib/sigterm_extensions/ordered_hash.rb +48 -0
- data/lib/sigterm_extensions/ordered_options.rb +83 -0
- data/lib/sigterm_extensions/paths.rb +235 -0
- data/lib/sigterm_extensions/per_thread_registry.rb +58 -0
- data/lib/sigterm_extensions/proxy_object.rb +14 -0
- data/lib/sigterm_extensions/staging/boot.rb +31 -0
- data/lib/sigterm_extensions/staging/boot/bundler_patch.rb +24 -0
- data/lib/sigterm_extensions/staging/boot/command.rb +26 -0
- data/lib/sigterm_extensions/staging/boot/gemfile_next_auto_sync.rb +79 -0
- data/lib/sigterm_extensions/version.rb +4 -0
- data/lib/sigterm_extensions/wrappable.rb +16 -0
- data/sigterm_extensions.gemspec +42 -0
- data/templates/dotpryrc.rb.erb +124 -0
- 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
|