lhj-tools 0.1.2 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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