qb 0.3.7 → 0.3.8

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.
@@ -16,6 +16,8 @@ require 'nrser'
16
16
  # Package
17
17
  # -----------------------------------------------------------------------
18
18
 
19
+ require_relative './repo/git'
20
+
19
21
 
20
22
  # Refinements
21
23
  # =======================================================================
@@ -36,8 +38,9 @@ module QB; end
36
38
  # Definitions
37
39
  # =======================================================================
38
40
 
39
-
40
- # @todo document QB::Path class.
41
+ # An extension of {Pathname} that implements the facts we want about paths
42
+ # as {NRSER::Meta::Props}.
43
+ #
41
44
  class QB::Path < Pathname
42
45
 
43
46
  # Mixins
@@ -46,29 +49,137 @@ class QB::Path < Pathname
46
49
  include NRSER::Meta::Props
47
50
 
48
51
 
49
- # Constants
52
+ # Props
50
53
  # ======================================================================
51
54
 
55
+ # Principle path properties
56
+ # ---------------------------------------------------------------------
57
+ #
58
+ # Values stored as variables on the instance.
59
+ #
52
60
 
53
- # Class Methods
54
- # ======================================================================
61
+ # The current working directory *relevant* to the path - basically, when
62
+ # and where the instance was created, which may be on a totally different
63
+ # system if the instance was loaded from data.
64
+ #
65
+ # TODO Not totally flushed out yet, could imagine a lot of problems and
66
+ # weirdness, but it seems like we need something like this to make
67
+ # instances transportable. Issues with relative paths come to mind...
68
+ #
69
+ prop :cwd,
70
+ type: t.pathname,
71
+ to_data: :to_s
55
72
 
73
+ # The raw path argument used to instantiate the path object.
74
+ #
75
+ # NOTE Because we reduce {Pathname} instances to {String} when converting
76
+ # to data, there is some lossy-ness. I guess it's similar to how
77
+ # symbols and strings all become strings when run through {JSON}
78
+ # dump and load.
79
+ #
80
+ prop :raw,
81
+ type: t.path,
82
+ to_data: :to_s
56
83
 
57
- # Props
58
- # ======================================================================
59
84
 
60
- prop :raw, type: t.
61
- prop :expanded, type: t.pathname, source: :expand_path
62
- prop :exists, type: t.bool, source: :exists?
63
- prop :is_expanded, type: t.bool, source: :expanded?
85
+ # Derived path properties
86
+ # ---------------------------------------------------------------------
87
+ #
88
+ # On-demand values computed via method calls.
89
+ #
90
+
91
+ prop :expanded,
92
+ type: t.path,
93
+ source: ->() { expand_path.to_s }
94
+
95
+ prop :exists,
96
+ type: t.bool,
97
+ source: :exist?
98
+
99
+ prop :is_expanded,
100
+ type: t.bool,
101
+ source: :expanded?
102
+
103
+ prop :is_absolute,
104
+ type: t.bool,
105
+ source: :absolute?
106
+
107
+ prop :is_relative,
108
+ type: t.bool,
109
+ source: :relative?
110
+
111
+ prop :is_dir,
112
+ type: t.bool,
113
+ source: :directory?
114
+
115
+ prop :is_file,
116
+ type: t.bool,
117
+ source: :file?
118
+
119
+ prop :is_cwd,
120
+ type: t.bool,
121
+ source: :cwd?
122
+
123
+ prop :relative,
124
+ type: t.maybe( t.path ),
125
+ source: :relative,
126
+ to_data: :to_s
127
+
128
+ prop :realpath,
129
+ type: t.maybe( t.path ),
130
+ source: :try_realpath,
131
+ to_data: :to_s
132
+
133
+ prop :is_realpath,
134
+ type: t.bool,
135
+ source: :realpath?
136
+
137
+
138
+ # Composed properties
139
+ # ---------------------------------------------------------------------
140
+ #
141
+ # On-demand values that point to other {NRSER::Meta::Props} instances.
142
+ #
143
+
144
+ prop :git,
145
+ type: QB::Repo::Git,
146
+ source: :git
147
+
64
148
 
65
149
  # Constructor
66
150
  # ======================================================================
67
151
 
68
- # Instantiate a new `QB::Path`.
69
- def initialize raw:, cwd: Pathname.getwd
70
- @raw = raw
71
- super @raw
152
+ # @overload initialize path
153
+ # Initialize in the same way as you would a {Pathname}. {#cwd} is set to
154
+ # the current directory (via {Pathname#getwd}) and the `path` argument is
155
+ # assigned to {#raw}.
156
+ #
157
+ # @param [String | Pathname] path
158
+ # Target path.
159
+ #
160
+ # @overload initialize **values
161
+ # Initialize by invoking {NRSER::Meta::Props#initialize_props}.
162
+ #
163
+ # The {#raw} value is passed up to {Pathname#initialize}.
164
+ #
165
+ # {#cwd} is accepted in `values`, allowing a re-instantiated object to
166
+ # "make sense" when the process' current directory may no longer be the
167
+ # one that data was constructed against.
168
+ #
169
+ # {#cwd} defaults to the current directory (via {Pathname.getwd}) if not
170
+ # provided.
171
+ #
172
+ # @param **values see {NRSER::Meta::Props#initialize_props}
173
+ #
174
+ def initialize arg
175
+ case arg
176
+ when Hash
177
+ super arg[:raw]
178
+ initialize_props cwd: Pathname.getwd, **arg
179
+ else
180
+ super arg
181
+ initialize_props raw: arg, cwd: Pathname.getwd
182
+ end
72
183
  end # #initialize
73
184
 
74
185
 
@@ -76,13 +187,102 @@ class QB::Path < Pathname
76
187
  # ======================================================================
77
188
 
78
189
  # @return [Boolean]
79
- # `true` if
190
+ # `true` if `self` is equal to {#expand_path}.
191
+ #
80
192
  def expanded?
193
+ self == expand_path
194
+ end
195
+
196
+
197
+ # @return [Boolean]
198
+ # `true` if `self` is equal to {#cwd}.
199
+ #
200
+ def cwd?
201
+ self == cwd
202
+ end
203
+
204
+
205
+ # Relative path from {#cwd} to `self`, if one exists.
206
+ #
207
+ # @return [QB::Path]
208
+ # If a relative path from {#cwd} to `self` exists.
209
+ #
210
+ # @return [nil]
211
+ # If no relative path from {#cwd} to `self` exists. Can't recall exactly
212
+ # how this happens, but I guess it can...
213
+ #
214
+ def relative
215
+ begin
216
+ relative_path_from cwd
217
+ rescue ArgumentError => error
218
+ nil
219
+ end
220
+ end
221
+
222
+
223
+ # Like {Pathname#realpath} but returns {nil} instead of raising if there
224
+ # isn't one.
225
+ #
226
+ # @return [nil]
227
+ # If there is no real path.
228
+ #
229
+ # @return [Pathname]
230
+ # If there is a real path.
231
+ #
232
+ def try_realpath
233
+ begin
234
+ realpath
235
+ rescue SystemCallError => error
236
+ nil
237
+ end
238
+ end
239
+
240
+
241
+ # Is `self` already it's real path?
242
+ #
243
+ # @return [Boolean]
244
+ # `true` if `self` and {#try_realpath} are equal.
245
+ #
246
+ def realpath?
247
+ self == try_realpath
248
+ end # #realpath?
249
+
250
+
251
+ # @return [Pathname]
252
+ # A regular (non-{QB::Path}) {Pathname} version of `self`.
253
+ def path
254
+ Pathname.new self
255
+ end
256
+
257
+
258
+ # Composed Sub-Instances
259
+ # ---------------------------------------------------------------------
260
+
261
+ # {QB::Repo::Git} resource for the Git repo {#path} is in one, or {nil} if
262
+ # it isn't.
263
+ #
264
+ # @return [QB::Repo::Git]
265
+ # If {#path} is in a Git repo.
266
+ #
267
+ # @return [nil]
268
+ # If {#path} is not in a Git repo.
269
+ #
270
+ def git
271
+ unless instance_variable_defined? :@git
272
+ @git = QB::Repo::Git.from_path path
273
+ end
81
274
 
275
+ @git
276
+ end
277
+
278
+
279
+ def gem
280
+ unless instance_variable_defined? :@gem
281
+ @gem = QB::Package::Gem.from_root_path path
282
+ end
283
+
284
+ @gem
82
285
  end
83
286
 
84
287
  end # class QB::Path
85
288
 
86
-
87
- # Post-Processing
88
- # =======================================================================
@@ -1,17 +1,43 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
1
9
  require 'cmds'
2
- require 'nrser/refinements/types'
3
10
 
11
+ # Project / Package
12
+ # -----------------------------------------------------------------------
13
+
14
+
15
+ # Refinements
16
+ # =======================================================================
17
+
18
+ require 'nrser/refinements'
19
+ using NRSER
20
+
21
+ require 'nrser/refinements/types'
4
22
  using NRSER::Types
5
23
 
6
- module QB
7
- module Repo
24
+
25
+ # Declarations
26
+ # =======================================================================
27
+
28
+ module QB; end
29
+ module QB::Repo; end
30
+
31
+
32
+ # Definitions
33
+ # =======================================================================
8
34
 
9
35
  # Encapsulate information about a Git repository and expose useful operations as
10
36
  # instance methods.
11
37
  #
12
38
  # The main entry point is {QB::Repo::Git.from_path}, which creates a
13
39
  #
14
- class Git < NRSER::Meta::Props::Base
40
+ class QB::Repo::Git < NRSER::Meta::Props::Base
15
41
  GITHUB_SSH_URL_RE = /^git@github\.com\:(?<owner>.*)\/(?<name>.*)\.git$/
16
42
  GITHUB_HTTPS_URL_RE = /^https:\/\/github\.com\/(?<owner>.*)\/(?<name>.*)\.git$/
17
43
 
@@ -93,19 +119,30 @@ class Git < NRSER::Meta::Props::Base
93
119
  # Class Methods
94
120
  # =====================================================================
95
121
 
96
- # @todo Document from_path method.
122
+ # Instantiate a {QB::Package::Git} resource for whatever Git repo `path`
123
+ # is in, or return `nil` if `path` is not in a Git repo.
97
124
  #
98
- # @param [String, Pathname] input_path
99
- # A path that is in the Git repo.
125
+ # @param [String, Pathname] path
126
+ # A path that may be in the Git repo.
127
+ #
128
+ # @param [Boolean] use_github_api:
129
+ # When `true` will will contact the GitHub API for information to populate
130
+ # the {QB::Repo::Git#github} property for repos that have a GitHub origin
131
+ # URL.
132
+ #
133
+ # Otherwise we will just assume GitHub repos are private since it's the
134
+ # safe guess, resulting in a {QB::Repo::Git#github} value of
135
+ # `{private: true}`.
100
136
  #
101
137
  # @return [QB::Repo::Git]
138
+ # If `path` is in a Git repo.
139
+ #
140
+ # @return [nil]
141
+ # If `path` is not in a Git repo.
102
142
  #
103
143
  # @raise [QB::FSStateError]
104
144
  # - If we can't find any existing directory to look in based on
105
145
  # `input_path`.
106
- #
107
- # - If the directory we do find to look in does not seems to be part of
108
- # a Git repo.
109
146
  #
110
147
  def self.from_path path, use_github_api: false
111
148
  raw_input_path = path
@@ -129,9 +166,7 @@ class Git < NRSER::Meta::Props::Base
129
166
  root_result = Cmds.capture "git rev-parse --show-toplevel"
130
167
 
131
168
  unless root_result.ok?
132
- raise QB::FSStateError,
133
- "Path #{ raw_input_path.inspect } does not appear to be in a " +
134
- "Git repo (looked in #{ closest_dir.inspect })."
169
+ return nil
135
170
  end
136
171
 
137
172
  root = Pathname.new root_result.out.chomp
@@ -217,11 +252,39 @@ class Git < NRSER::Meta::Props::Base
217
252
  end # .from_path
218
253
 
219
254
 
255
+ # Instantiate a {QB::Package::Git} resource for whatever Git repo `path`
256
+ # is in, raising an error if it's not in one.
257
+ #
258
+ # @param [String, Pathname] path
259
+ # A path that is in the Git repo.
260
+ #
261
+ # @param use_github_api: see #from_path
262
+ #
263
+ # @return [QB::Repo::Git]
264
+ #
265
+ # @raise [QB::FSStateError]
266
+ # - If we can't find any existing directory to look in based on
267
+ # `input_path`.
268
+ #
269
+ # - If the directory we do find to look in does not seems to be part of
270
+ # a Git repo.
271
+ #
272
+ def from_path! path, use_github_api: false
273
+ from_path( path, use_github_api: use_github_api ).tap { |git|
274
+ if git.nil?
275
+ raise QB::FSStateError,
276
+ "Path #{ path.inspect } does not appear to be in a " +
277
+ "Git repo."
278
+ end
279
+ }
280
+ end # #from_path!
281
+
282
+
220
283
  # Props
221
284
  # =====================================================================
222
285
 
223
- prop :raw_input_path, type: t.maybe(t.union(Pathname, t.str)), default: nil
224
- prop :root, type: Pathname
286
+ prop :raw_input_path, type: t.path, default: nil, to_data: :to_s
287
+ prop :root, type: t.pathname, to_data: :to_s
225
288
  prop :user, type: User
226
289
  prop :is_clean, type: t.bool
227
290
  prop :head, type: t.maybe(t.str), default: nil
@@ -231,6 +294,10 @@ class Git < NRSER::Meta::Props::Base
231
294
  prop :name, type: t.maybe(t.str), default: nil
232
295
  prop :github, type: t.maybe(t.hash_), default: nil
233
296
 
297
+
298
+ # Derived Properties
299
+ # ---------------------------------------------------------------------
300
+
234
301
  prop :head_short, type: t.maybe(t.str), source: :head_short
235
302
  prop :full_name, type: t.maybe(t.str), source: :full_name
236
303
  prop :is_github, type: t.bool, source: :github?
@@ -251,11 +318,15 @@ class Git < NRSER::Meta::Props::Base
251
318
  !github.nil?
252
319
  end
253
320
 
321
+
322
+ # Reading Repo State
323
+ # ---------------------------------------------------------------------
324
+
325
+ # @return [Boolean]
326
+ # `false` if the repo has any uncommitted changes or untracked files.
327
+ #
254
328
  def clean?
255
329
  is_clean
256
330
  end
257
331
 
258
- end # class Git
259
-
260
- end # module Repo
261
- end # module QB
332
+ end # class QB::Repo::Git
@@ -411,19 +411,37 @@ class QB::Role
411
411
  path = QB::Util.resolve dest
412
412
 
413
413
  search_dirs = search_path.find_all { |pathname|
414
- path.fnmatch? pathname.join('**')
414
+ path.fnmatch?( pathname / '**' )
415
415
  }
416
416
 
417
417
  case search_dirs.length
418
418
  when 0
419
419
  # It's not in any of the search directories
420
420
  #
421
- # If it has 'roles' as a segment than use what's after that
421
+ # If it has 'roles' as a segment than use what's after the last occurrence
422
+ # of that (unless there isn't anything).
422
423
  #
423
- split = path.to_s.split('/roles/')
424
+ segments = path.to_s.split File::SEPARATOR
425
+
426
+ if index = segments.rindex( 'roles' )
427
+ name_segs = segments[index..-1]
428
+
429
+ unless name_segs.empty?
430
+ return File.join name_segs
431
+ end
432
+ end
433
+
434
+ # Ok, that didn't work... just return the basename I guess...
435
+ File.basename path
436
+
437
+ when 1
438
+
439
+ else
440
+ # Multiple matches?!?!?
441
+
424
442
 
425
443
  end
426
- end
444
+ end # #guess_role_name
427
445
 
428
446
 
429
447
  # Instance Attributes