lhj-tools 0.1.2 → 0.1.6

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.
data/lib/lhj/config.rb CHANGED
@@ -1,354 +1,14 @@
1
-
2
1
  module Lhj
3
- # Stores the global configuration of CocoaPods.
4
- #
2
+ # config
5
3
  class Config
6
- # The default settings for the configuration.
7
- #
8
- # Users can specify custom settings in `~/.cocoapods/config.yaml`.
9
- # An example of the contents of this file might look like:
10
- #
11
- # ---
12
- # skip_repo_update: true
13
- # new_version_message: false
14
- #
15
- DEFAULTS = {
16
- :verbose => false,
17
- :silent => false,
18
- :skip_download_cache => !ENV['COCOAPODS_SKIP_CACHE'].nil?,
19
-
20
- :new_version_message => ENV['COCOAPODS_SKIP_UPDATE_MESSAGE'].nil?,
21
-
22
- :cache_root => Pathname.new(Dir.home) + 'Library/Caches/CocoaPods',
23
- }
24
-
25
- # Applies the given changes to the config for the duration of the given
26
- # block.
27
- #
28
- # @param [Hash<#to_sym,Object>] changes
29
- # the changes to merge temporarily with the current config
30
- #
31
- # @yield [] is called while the changes are applied
32
- #
33
- def with_changes(changes)
34
- old = {}
35
- changes.keys.each do |key|
36
- key = key.to_sym
37
- old[key] = send(key) if respond_to?(key)
38
- end
39
- configure_with(changes)
40
- yield if block_given?
41
- ensure
42
- configure_with(old)
43
- end
44
-
45
- public
46
-
47
- #-------------------------------------------------------------------------#
48
-
49
- # @!group UI
50
-
51
- # @return [Boolean] Whether CocoaPods should provide detailed output about the
52
- # performed actions.
53
- #
54
- attr_accessor :verbose
55
- alias_method :verbose?, :verbose
56
-
57
- # @return [Boolean] Whether CocoaPods should produce not output.
58
- #
59
- attr_accessor :silent
60
- alias_method :silent?, :silent
61
-
62
- # @return [Boolean] Whether CocoaPods is allowed to run as root.
63
- #
64
- attr_accessor :allow_root
65
- alias_method :allow_root?, :allow_root
66
-
67
- # @return [Boolean] Whether a message should be printed when a new version of
68
- # CocoaPods is available.
69
- #
70
- attr_accessor :new_version_message
71
- alias_method :new_version_message?, :new_version_message
72
-
73
- #-------------------------------------------------------------------------#
74
-
75
- # @!group Installation
76
-
77
- # @return [Boolean] Whether the installer should skip the download cache.
78
- #
79
- attr_accessor :skip_download_cache
80
- alias_method :skip_download_cache?, :skip_download_cache
81
-
82
- public
83
-
84
- #-------------------------------------------------------------------------#
85
-
86
- # @!group Cache
87
-
88
- # @return [Pathname] The directory where CocoaPods should cache remote data
89
- # and other expensive to compute information.
90
- #
91
- attr_accessor :cache_root
92
-
93
- def cache_root
94
- @cache_root.mkpath unless @cache_root.exist?
95
- @cache_root
96
- end
97
-
98
- public
99
-
100
- #-------------------------------------------------------------------------#
101
-
102
- # @!group Initialization
103
-
104
- def initialize(use_user_settings = true)
105
- configure_with(DEFAULTS)
106
-
107
- unless ENV['CP_HOME_DIR'].nil?
108
- @cache_root = home_dir + 'cache'
109
- end
110
-
111
- if use_user_settings && user_settings_file.exist?
112
- require 'yaml'
113
- user_settings = YAML.load_file(user_settings_file)
114
- configure_with(user_settings)
115
- end
116
-
117
- unless ENV['CP_CACHE_DIR'].nil?
118
- @cache_root = Pathname.new(ENV['CP_CACHE_DIR']).expand_path
119
- end
120
- end
121
-
122
- def verbose
123
- @verbose && !silent
124
- end
125
-
126
- public
127
-
128
- #-------------------------------------------------------------------------#
129
-
130
- # @!group Paths
131
-
132
- # @return [Pathname] the directory where repos, templates and configuration
133
- # files are stored.
134
- #
135
4
  def home_dir
136
- @home_dir ||= Pathname.new(ENV['CP_HOME_DIR'] || '~/.lhj').expand_path
5
+ @home_dir ||= Pathname.new('~/.lhj').expand_path
137
6
  end
138
7
 
139
- # @return [Pathname] the directory where the CocoaPods sources are stored.
140
- #
141
- def repos_dir
142
- @repos_dir ||= Pathname.new(ENV['CP_REPOS_DIR'] || (home_dir + 'repos')).expand_path
143
- end
144
-
145
- attr_writer :repos_dir
146
-
147
- # @return [Source::Manager] the source manager for the spec repos in `repos_dir`
148
- #
149
- def sources_manager
150
- return @sources_manager if @sources_manager && @sources_manager.repos_dir == repos_dir
151
- @sources_manager = Source::Manager.new(repos_dir)
152
- end
153
-
154
- # @return [Pathname] the directory where the CocoaPods templates are stored.
155
- #
156
- def templates_dir
157
- @templates_dir ||= Pathname.new(ENV['CP_TEMPLATES_DIR'] || (home_dir + 'templates')).expand_path
158
- end
159
-
160
- # @return [Pathname] the root of the CocoaPods installation where the
161
- # Podfile is located.
162
- #
163
- def installation_root
164
- @installation_root ||= begin
165
- current_dir = Pathname.new(Dir.pwd.unicode_normalize(:nfkc))
166
- current_path = current_dir
167
- until current_path.root?
168
- if podfile_path_in_dir(current_path)
169
- installation_root = current_path
170
- unless current_path == current_dir
171
- UI.puts("[in #{current_path}]")
172
- end
173
- break
174
- else
175
- current_path = current_path.parent
176
- end
177
- end
178
- installation_root || current_dir
179
- end
180
- end
181
-
182
- attr_writer :installation_root
183
- alias_method :project_root, :installation_root
184
-
185
- # @return [Pathname] The root of the sandbox.
186
- #
187
- def sandbox_root
188
- @sandbox_root ||= installation_root + 'Pods'
189
- end
190
-
191
- attr_writer :sandbox_root
192
- alias_method :project_pods_root, :sandbox_root
193
-
194
- # @return [Sandbox] The sandbox of the current project.
195
- #
196
- def sandbox
197
- @sandbox ||= Sandbox.new(sandbox_root)
198
- end
199
-
200
- # @return [Podfile] The Podfile to use for the current execution.
201
- # @return [Nil] If no Podfile is available.
202
- #
203
- def podfile
204
- @podfile ||= Podfile.from_file(podfile_path) if podfile_path
205
- end
206
- attr_writer :podfile
207
-
208
- # @return [Lockfile] The Lockfile to use for the current execution.
209
- # @return [Nil] If no Lockfile is available.
210
- #
211
- def lockfile
212
- @lockfile ||= Lockfile.from_file(lockfile_path) if lockfile_path
213
- end
214
-
215
- # Returns the path of the Podfile.
216
- #
217
- # @note The Podfile can be named either `CocoaPods.podfile.yaml`,
218
- # `CocoaPods.podfile` or `Podfile`. The first two are preferred as
219
- # they allow to specify an OS X UTI.
220
- #
221
- # @return [Pathname]
222
- # @return [Nil]
223
- #
224
- def podfile_path
225
- @podfile_path ||= podfile_path_in_dir(installation_root)
226
- end
227
-
228
- # Returns the path of the Lockfile.
229
- #
230
- # @note The Lockfile is named `Podfile.lock`.
231
- #
232
- def lockfile_path
233
- @lockfile_path ||= installation_root + 'Podfile.lock'
234
- end
235
-
236
- # Returns the path of the default Podfile pods.
237
- #
238
- # @note The file is expected to be named Podfile.default
239
- #
240
- # @return [Pathname]
241
- #
242
- def default_podfile_path
243
- @default_podfile_path ||= templates_dir + 'Podfile.default'
244
- end
245
-
246
- # Returns the path of the default Podfile test pods.
247
- #
248
- # @note The file is expected to be named Podfile.test
249
- #
250
- # @return [Pathname]
251
- #
252
- def default_test_podfile_path
253
- @default_test_podfile_path ||= templates_dir + 'Podfile.test'
254
- end
255
-
256
- # @return [Pathname] The file to use to cache the search data.
257
- #
258
- def search_index_file
259
- cache_root + 'search_index.json'
260
- end
261
-
262
- private
263
-
264
- #-------------------------------------------------------------------------#
265
-
266
- # @!group Private helpers
267
-
268
- # @return [Pathname] The path of the file which contains the user settings.
269
- #
270
- def user_settings_file
271
- home_dir + 'config.yaml'
272
- end
273
-
274
- # Sets the values of the attributes with the given hash.
275
- #
276
- # @param [Hash{String,Symbol => Object}] values_by_key
277
- # The values of the attributes grouped by key.
278
- #
279
- # @return [void]
280
- #
281
- def configure_with(values_by_key)
282
- return unless values_by_key
283
- values_by_key.each do |key, value|
284
- if key.to_sym == :cache_root
285
- value = Pathname.new(value).expand_path
286
- end
287
- instance_variable_set("@#{key}", value)
288
- end
289
- end
290
-
291
- # @return [Array<String>] The filenames that the Podfile can have ordered
292
- # by priority.
293
- #
294
- PODFILE_NAMES = [
295
- 'CocoaPods.podfile.yaml',
296
- 'CocoaPods.podfile',
297
- 'Podfile',
298
- 'Podfile.rb',
299
- ].freeze
300
-
301
- public
302
-
303
- # Returns the path of the Podfile in the given dir if any exists.
304
- #
305
- # @param [Pathname] dir
306
- # The directory where to look for the Podfile.
307
- #
308
- # @return [Pathname] The path of the Podfile.
309
- # @return [Nil] If not Podfile was found in the given dir
310
- #
311
- def podfile_path_in_dir(dir)
312
- PODFILE_NAMES.each do |filename|
313
- candidate = dir + filename
314
- if candidate.file?
315
- return candidate
316
- end
317
- end
318
- nil
319
- end
320
-
321
- # Excludes the given dir from Time Machine backups.
322
- #
323
- # @param [Pathname] dir
324
- # The directory to exclude from Time Machine backups.
325
- #
326
- # @return [void]
327
- #
328
- def exclude_from_backup(dir)
329
- return if Gem.win_platform?
330
- system('tmutil', 'addexclusion', dir.to_s, %i(out err) => File::NULL)
331
- end
332
-
333
- public
334
-
335
- #-------------------------------------------------------------------------#
336
-
337
- # @!group Singleton
338
-
339
- # @return [Config] the current config instance creating one if needed.
340
- #
341
8
  def self.instance
342
9
  @instance ||= new
343
10
  end
344
11
 
345
- # Sets the current config instance. If set to nil the config will be
346
- # recreated when needed.
347
- #
348
- # @param [Config, Nil] the instance.
349
- #
350
- # @return [void]
351
- #
352
12
  class << self
353
13
  attr_writer :instance
354
14
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lhj
4
4
  module Tools
5
- VERSION = "0.1.2"
5
+ VERSION = "0.1.6"
6
6
  end
7
7
  end
data/lib/lhj/tools.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require_relative "tools/version"
4
3
 
5
4
  module Lhj
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lhj
4
+ class Tree
5
+ # Render nodes as files paths explorer
6
+ class DirectoryRenderer
7
+ MARKERS = {
8
+ unicode: {
9
+ branch: '├──',
10
+ leaf: '└──',
11
+ pipe: '│'
12
+ },
13
+ ansi: {
14
+ branch: '|--',
15
+ leaf: '`--',
16
+ pipe: '|'
17
+ }
18
+ }.freeze
19
+
20
+ def initialize(nodes, options = {})
21
+ @nodes = nodes
22
+ @indent = options.fetch(:indent, 4)
23
+ @pipe_mark = MARKERS[:unicode][:pipe] + ' ' * [@indent - 1, 0].max
24
+ @space_mark = ' ' * @indent
25
+ end
26
+
27
+ def render
28
+ @nodes.reduce([]) do |acc, node|
29
+ render_node(acc, node, @pipe_mark, @space_mark)
30
+ end.join('')
31
+ end
32
+
33
+ private
34
+
35
+ def render_node(acc, node, pipe_mark, space_mark)
36
+ acc << node.prefix.gsub(/:pipe/, pipe_mark).gsub(/:space/, space_mark)
37
+ unless node.root?
38
+ acc << MARKERS[:unicode][node.leaf? ? :leaf : :branch]
39
+ acc << ' '
40
+ end
41
+ acc << node.name.to_s + "\n"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ require 'lhj/tree/node'
6
+
7
+ module Lhj
8
+ class Tree
9
+ # Walk and collect nodes from hash data strcture.
10
+ #
11
+ # @api public
12
+ class HashWalker
13
+ attr_reader :nodes
14
+
15
+ attr_reader :files_count
16
+
17
+ attr_reader :dirs_count
18
+
19
+ def initialize(options = {})
20
+ @nodes = []
21
+ @files_count = 0
22
+ @dirs_count = 0
23
+ @level = options.fetch(:level) { -1 }
24
+ @file_limit = options.fetch(:file_limit) { - 1 }
25
+ end
26
+
27
+ def traverse(data)
28
+ walk(data, Pathname.new(''), '', 0, false)
29
+ end
30
+
31
+ def walk(data, parent_path, prefix, level, is_last)
32
+ node = is_last ? LeafNode : Node
33
+
34
+ case data
35
+ when Hash
36
+ return if @level != -1 && level + 1 > @level
37
+
38
+ data.each do |dir, item|
39
+ dir_node = node.new(dir.to_s, parent_path, prefix, level)
40
+ @nodes << dir_node
41
+ @dirs_count += 1 unless dir_node.root?
42
+
43
+ if level > 0
44
+ postfix = ':pipe'
45
+ postfix = ':space' if is_last
46
+ else
47
+ postfix = ''
48
+ end
49
+
50
+ walk(item, parent_path + dir.to_s,
51
+ prefix + postfix, level + 1, false)
52
+ end
53
+ when Array
54
+ return if @file_limit != -1 && data.size > @file_limit
55
+
56
+ last_data_index = data.size - 1
57
+
58
+ data.each_with_index do |item, i|
59
+ last = (last_data_index == i)
60
+
61
+ walk(item, parent_path, prefix, level, last)
62
+ end
63
+ else
64
+ @nodes << node.new(data.to_s, parent_path, prefix, level)
65
+ @files_count += 1
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'pathname'
5
+
6
+ module Lhj
7
+ class Tree
8
+ # A representation of tree node
9
+ #
10
+ # @api private
11
+ class Node
12
+ extend Forwardable
13
+
14
+ # The base name for the directory or file
15
+ attr_reader :name
16
+
17
+ # The parent directory path
18
+ attr_reader :parent
19
+
20
+ # The require path prefix
21
+ attr_reader :prefix
22
+
23
+ # The directory depth
24
+ attr_reader :level
25
+
26
+ # The file stat
27
+ attr_reader :stat
28
+
29
+ # The current path
30
+ attr_reader :path
31
+
32
+ def_delegators :@path, :directory?, :executable?, :file?,
33
+ :symlink?, :socket?, :pipe?
34
+
35
+ def initialize(path, parent, prefix, level)
36
+ if path.is_a? String
37
+ # strip null bytes from the string to avoid throwing errors
38
+ path = path.delete("\0")
39
+ end
40
+
41
+ @path = Pathname.new(path)
42
+ @name = @path.basename
43
+ @parent = Pathname.new(parent)
44
+ @prefix = prefix
45
+ @level = level
46
+ end
47
+
48
+ def full_path
49
+ return parent if name.to_s.empty?
50
+
51
+ parent.join(name)
52
+ end
53
+
54
+ def root?
55
+ parent.to_s.empty?
56
+ end
57
+
58
+ def hidden?
59
+ name.to_s.start_with?('.')
60
+ end
61
+
62
+ def leaf?
63
+ false
64
+ end
65
+
66
+ def to_s
67
+ @name
68
+ end
69
+
70
+ def ==(other)
71
+ other.is_a?(self.class) && other.state_attrs == state_attrs
72
+ end
73
+ alias eql? ==
74
+
75
+ protected
76
+
77
+ def state_attrs
78
+ [@name, @path, @parent, @prefix, @level]
79
+ end
80
+
81
+ ROOT = Node.new('', Pathname.new(''), '', 0).freeze
82
+ end # Node
83
+
84
+ class LeafNode < Node
85
+ def leaf?
86
+ true
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lhj
4
+ class Tree
5
+ # Render nodes as numbered list
6
+ class NumberRenderer
7
+ def initialize(nodes, options = {})
8
+ @indent = options.fetch(:indent, 4)
9
+ @nodes = nodes
10
+ @mark = ' ' * @indent
11
+ end
12
+
13
+ def render
14
+ @nodes.each_with_index.reduce([]) do |acc, (node, i)|
15
+ render_node(acc, node, i, @mark)
16
+ end.join
17
+ end
18
+
19
+ private
20
+
21
+ def render_node(acc, node, i, mark)
22
+ acc << node.prefix.gsub(/:pipe|:space/, mark)
23
+ unless node.root?
24
+ acc << "#{node.level}.#{i} "
25
+ end
26
+ acc << node.name.to_s + "\n"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ require 'lhj/tree/node'
6
+
7
+ module Lhj
8
+ class Tree
9
+ # Walk and collect nodes from directory.
10
+ #
11
+ # @api public
12
+ class PathWalker
13
+ attr_reader :nodes
14
+
15
+ attr_reader :files_count
16
+
17
+ attr_reader :dirs_count
18
+
19
+ # Create a PathWalker
20
+ #
21
+ # @api public
22
+ def initialize(options = {})
23
+ @files_count = 0
24
+ @dirs_count = 0
25
+ @nodes = []
26
+ @filters = []
27
+ @level = options.fetch(:level) { -1 }
28
+ @file_limit = options.fetch(:file_limit) { - 1 }
29
+
30
+ unless options[:show_hidden]
31
+ add_filter(-> (p) { !p.basename.to_s.start_with?('.') })
32
+ end
33
+
34
+ if options[:only_dirs]
35
+ add_filter(-> (p) { p.directory? })
36
+ end
37
+ end
38
+
39
+ def add_filter(filter)
40
+ @filters << filter
41
+ end
42
+
43
+ # Traverse given path recursively
44
+ #
45
+ # @param [String] path
46
+ # the path to traverse
47
+ #
48
+ # @api public
49
+ def traverse(path)
50
+ root_path = Pathname.new(path)
51
+ empty_path = Pathname.new('')
52
+
53
+ unless root_path.directory?
54
+ raise ArgumentError, "#{root_path} is not a directory path"
55
+ end
56
+
57
+ @nodes << Node.new(root_path, empty_path, '', 0)
58
+
59
+ walk(root_path, root_path.children, '', 1)
60
+ end
61
+
62
+ private
63
+
64
+ # Filter entries
65
+ #
66
+ # @api private
67
+ def filter_entries(entries, filters)
68
+ return entries if filters.nil? || filters.empty?
69
+ filter = filters[0]
70
+ filter_entries(entries.select(&filter), filters[1..-1])
71
+ end
72
+
73
+ # Walk paths recursively
74
+ #
75
+ # @api private
76
+ def walk(parent_path, entries, prefix, level)
77
+ if entries.empty? || (@level != -1 && @level < level)
78
+ return
79
+ else
80
+ return if @file_limit != -1 && entries.size > @file_limit
81
+ processed_paths = filter_entries(entries, @filters).sort
82
+ last_path_index = processed_paths.size - 1
83
+
84
+ processed_paths.each_with_index do |path, i|
85
+ sub_path = path.relative_path_from(parent_path)
86
+
87
+ node = last_path_index == i ? LeafNode : Node
88
+
89
+ if path.directory?
90
+ next if @level != -1 && level + 1 > @level
91
+
92
+ @nodes << node.new(sub_path, parent_path, prefix, level)
93
+ @dirs_count += 1
94
+
95
+ postfix = ':pipe'
96
+ postfix = ':space' if i == last_path_index
97
+
98
+ walk(path, path.children, prefix + postfix, level + 1)
99
+ elsif path.file?
100
+ @nodes << node.new(path, parent_path, prefix, level)
101
+ @files_count += 1
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end