qb 0.3.7 → 0.3.8

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