development 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +0 -0
- data/README.md +154 -0
- data/lib/development.rb +881 -0
- data/lib/development/exception/expression_error.rb +18 -0
- data/lib/development/exception/expression_error/unknown_directory_name.rb +14 -0
- data/lib/development/exception/expression_error/unknown_gem_or_gemset_name.rb +14 -0
- data/lib/development/exception/malformed_expression.rb +16 -0
- data/lib/development/exception/malformed_expression/malformed_enable_disable_expression.rb +15 -0
- data/lib/development/exception/malformed_expression/malformed_gemset_expression.rb +15 -0
- data/lib/development/exception/malformed_expression/malformed_general_directory_expression.rb +15 -0
- data/lib/development/exception/malformed_expression/malformed_location_expression.rb +15 -0
- data/lib/development/exception/malformed_expression/malformed_named_directory_expression.rb +15 -0
- data/lib/development/exception/malformed_expression/malformed_remove_general_directory_expression.rb +15 -0
- data/lib/development/require.rb +35 -0
- data/spec/development_spec.rb +294 -0
- data/spec/mock_gem-subgem/lib/mock_gem/subgem.rb +4 -0
- data/spec/mock_gem/lib/mock_gem.rb +4 -0
- data/spec/other_require_mock/lib/other_require_mock.rb +4 -0
- data/spec/require_mock/lib/require_mock.rb +4 -0
- metadata +90 -0
data/CHANGELOG.md
ADDED
File without changes
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# Development #
|
2
|
+
|
3
|
+
http://rubygems.org/gems/development
|
4
|
+
|
5
|
+
# Summary #
|
6
|
+
|
7
|
+
Manage development contexts, particularly in the context of nested gem dependencies being developed side by side.
|
8
|
+
|
9
|
+
# Description #
|
10
|
+
|
11
|
+
Sometimes problems in one gem under development only appear in the context of use in another gem. In these contexts it can be difficult to discern the particular problem case without diving into the code. When gems are nested in multiple levels of dependency, this can become quite frustrating; gem require paths have to be replaced with relative paths, sometimes in multiple places, and it becomes easy to forget that there are relative paths in code, resulting in production code with broken development paths accidentally left in.
|
12
|
+
|
13
|
+
Development inserts itself in the require process and loads development paths instead of the gem requires used for production code. Which gems load development paths instead of gem requires is determined by a simple configuration file.
|
14
|
+
|
15
|
+
# Install #
|
16
|
+
|
17
|
+
* sudo gem install development
|
18
|
+
|
19
|
+
# Usage #
|
20
|
+
|
21
|
+
Enabling Development requires two things:
|
22
|
+
|
23
|
+
## 1. Require Development ##
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
begin ; require 'development' ; rescue LoadError ; end
|
27
|
+
```
|
28
|
+
|
29
|
+
Or a multi-line version if you prefer:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
begin
|
33
|
+
require 'development'
|
34
|
+
rescue LoadError
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
Doing this rather than simply requiring development ensures that Development can work transparently without requiring even a development dependency.
|
39
|
+
|
40
|
+
## 2. Configure Gems Development Should Intercept ##
|
41
|
+
|
42
|
+
The primary interface to development is the **.development** configuration file, which should be placed in the user's home directory.
|
43
|
+
|
44
|
+
I would make project-specific .development files a requirement (to avoid accidentally enabling it) but I don't think there is a reliable way to find the project directory based on the require. If you know a way, please let me know!
|
45
|
+
|
46
|
+
### Configuration File Example ###
|
47
|
+
|
48
|
+
###
|
49
|
+
# Ruby 'development' configuration file (.development).
|
50
|
+
#
|
51
|
+
# * # denotes comment.
|
52
|
+
# * Indentation is treated as a line continuation.
|
53
|
+
# * Items can be separated by , or simply by whitespace.
|
54
|
+
# * Length of whitespace is irrelevant - contiguous whitespace is "one unit".
|
55
|
+
#
|
56
|
+
|
57
|
+
###
|
58
|
+
# Any lines beginning with plain text (not =, +, -, @, !) will be interpreted as general directory expressions.
|
59
|
+
# It is not necessary to specify any general directories. Multiple directories may be specified, one per line.
|
60
|
+
#
|
61
|
+
# Uncomment the line below to look for directories with gem name in ~/ruby_projects. Any directories specified
|
62
|
+
# in this way will be used in the case that no explicit specification exists for gem name in question.
|
63
|
+
#
|
64
|
+
# ~/ruby_projects
|
65
|
+
|
66
|
+
###
|
67
|
+
# Declare a gemset to group a set of gems and configure them by a single reference.
|
68
|
+
#
|
69
|
+
# Gems are permitted to be in multiple sets, with first match (from top of .development file downward) winning.
|
70
|
+
#
|
71
|
+
# =<gemset_name> gem-name, ...
|
72
|
+
#
|
73
|
+
|
74
|
+
=ruby module-cluster
|
75
|
+
|
76
|
+
=hooked_objects hash-hooked
|
77
|
+
array-hooked
|
78
|
+
array-sorted
|
79
|
+
array-unique
|
80
|
+
array-sorted-unique
|
81
|
+
|
82
|
+
=compositing_objects hash-compositing
|
83
|
+
array-compositing
|
84
|
+
array-sorted-compositing
|
85
|
+
array-unique-compositing
|
86
|
+
array-sorted-unique-compositing
|
87
|
+
|
88
|
+
=ridiculous_power persistence
|
89
|
+
magnets
|
90
|
+
|
91
|
+
###
|
92
|
+
# Declare named locations to associate gems or gemsets with specific locations.
|
93
|
+
#
|
94
|
+
# +directory_name path, ...
|
95
|
+
#
|
96
|
+
# Paths that begin with @ will interpolate the directory name as the starting portion of the path.
|
97
|
+
#
|
98
|
+
|
99
|
+
+code ~/Projects
|
100
|
+
+ridiculous_power @code/rp
|
101
|
+
+ruby @ridiculous_power/ruby
|
102
|
+
|
103
|
+
+hooked_objects @ruby/hooked_objects
|
104
|
+
+compositing_objects @ruby/compositing_objects
|
105
|
+
|
106
|
+
###
|
107
|
+
# Declare lookup locations for specific gems.
|
108
|
+
#
|
109
|
+
# Named paths listed on there own
|
110
|
+
#
|
111
|
+
|
112
|
+
@hooked_objects hooked_objects
|
113
|
+
@compositing_objects compositing_objects
|
114
|
+
|
115
|
+
@ridiculous_power ridiculous_power
|
116
|
+
|
117
|
+
###
|
118
|
+
# !enable and !disable can be used to cause production gems to be used.
|
119
|
+
#
|
120
|
+
# * !enable or !disable on its own will enable or disable all gems and change the default (enabled/disabled),
|
121
|
+
# after which individual gems can be enabled or disabled.
|
122
|
+
#
|
123
|
+
# * !enable or !disable followed by a gem name or a gemset name will enable or disable that gem/gemset.
|
124
|
+
#
|
125
|
+
# Nothing is enabled to start. Call !enable to use .development specifications in all cases; after
|
126
|
+
# !enable individual gems/sets/paths can be enabled/disabled.
|
127
|
+
#
|
128
|
+
|
129
|
+
!enable
|
130
|
+
|
131
|
+
# License #
|
132
|
+
|
133
|
+
(The MIT License)
|
134
|
+
|
135
|
+
Copyright (c) Ridiculous Power, Asher
|
136
|
+
|
137
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
138
|
+
a copy of this software and associated documentation files (the
|
139
|
+
'Software'), to deal in the Software without restriction, including
|
140
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
141
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
142
|
+
permit persons to whom the Software is furnished to do so, subject to
|
143
|
+
the following conditions:
|
144
|
+
|
145
|
+
The above copyright notice and this permission notice shall be
|
146
|
+
included in all copies or substantial portions of the Software.
|
147
|
+
|
148
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
149
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
150
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
151
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
152
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
153
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
154
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/development.rb
ADDED
@@ -0,0 +1,881 @@
|
|
1
|
+
|
2
|
+
require 'array-unique'
|
3
|
+
|
4
|
+
###
|
5
|
+
# Singleton that manages configurations and requires.
|
6
|
+
#
|
7
|
+
module ::Development
|
8
|
+
|
9
|
+
#############
|
10
|
+
# loaded? #
|
11
|
+
#############
|
12
|
+
|
13
|
+
###
|
14
|
+
# Query whether gem was loaded via Development rather than standard require.
|
15
|
+
#
|
16
|
+
# @param gem_name
|
17
|
+
#
|
18
|
+
# Name of gem.
|
19
|
+
#
|
20
|
+
# @return [true,false] Whether gem was loaded via Development.
|
21
|
+
#
|
22
|
+
def loaded?( gem_name )
|
23
|
+
|
24
|
+
return @loaded_gems.include?( gem_name )
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
####################
|
29
|
+
# self.directory #
|
30
|
+
####################
|
31
|
+
|
32
|
+
def self.directory( directory_name )
|
33
|
+
|
34
|
+
return @named_directories[ directory_name.to_sym ]
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
#############################
|
39
|
+
# self.general_load_paths #
|
40
|
+
#############################
|
41
|
+
|
42
|
+
def self.general_load_paths
|
43
|
+
|
44
|
+
return @general_load_paths
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
############################
|
49
|
+
# self.named_directories #
|
50
|
+
############################
|
51
|
+
|
52
|
+
def self.named_directories
|
53
|
+
|
54
|
+
return @named_directories
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
###########################
|
59
|
+
# self.enabled_for_all? #
|
60
|
+
###########################
|
61
|
+
|
62
|
+
def self.enabled_for_all?
|
63
|
+
|
64
|
+
return @enable_for_all
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
#######################
|
69
|
+
# self.enabled_gems #
|
70
|
+
#######################
|
71
|
+
|
72
|
+
def self.enabled_gems
|
73
|
+
|
74
|
+
return @enabled_gems
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
########################
|
79
|
+
# self.disabled_gems #
|
80
|
+
########################
|
81
|
+
|
82
|
+
def self.disabled_gems
|
83
|
+
|
84
|
+
return @disabled_gems
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
##################
|
89
|
+
# self.gemsets #
|
90
|
+
##################
|
91
|
+
|
92
|
+
def self.gemsets
|
93
|
+
|
94
|
+
return @gemsets
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
####################
|
99
|
+
# self.locations #
|
100
|
+
####################
|
101
|
+
|
102
|
+
def self.locations
|
103
|
+
|
104
|
+
return @locations
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
###################
|
109
|
+
# self.location #
|
110
|
+
###################
|
111
|
+
|
112
|
+
def self.location( location_name )
|
113
|
+
|
114
|
+
return @locations[ location_name.to_sym ]
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
######################################################################################################################
|
119
|
+
private ##########################################################################################################
|
120
|
+
######################################################################################################################
|
121
|
+
|
122
|
+
###
|
123
|
+
# @private
|
124
|
+
#
|
125
|
+
# Container to namespace exceptions.
|
126
|
+
#
|
127
|
+
module Exception
|
128
|
+
end
|
129
|
+
|
130
|
+
###
|
131
|
+
# Name of configuration file: .development.
|
132
|
+
#
|
133
|
+
ConfigurationFileName = '.development'
|
134
|
+
|
135
|
+
@enabled_gems = ::Array::Unique.new
|
136
|
+
@disabled_gems = ::Array::Unique.new
|
137
|
+
|
138
|
+
@gemsets = { }
|
139
|
+
@gem_locations = { }
|
140
|
+
|
141
|
+
@general_load_paths = ::Array::Unique.new
|
142
|
+
|
143
|
+
@loaded_gems = ::Array::Unique.new
|
144
|
+
|
145
|
+
@named_directories = { }
|
146
|
+
@locations = { }
|
147
|
+
|
148
|
+
@enable_for_all = false
|
149
|
+
|
150
|
+
################
|
151
|
+
# self.clear #
|
152
|
+
################
|
153
|
+
|
154
|
+
def self.clear
|
155
|
+
|
156
|
+
@enabled_gems.clear
|
157
|
+
@disabled_gems.clear
|
158
|
+
@gemsets.clear
|
159
|
+
@general_load_paths.clear
|
160
|
+
@loaded_gems.clear
|
161
|
+
@named_directories.clear
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
##################################
|
166
|
+
# self.load_configuration_file #
|
167
|
+
##################################
|
168
|
+
|
169
|
+
###
|
170
|
+
# Load configuration file.
|
171
|
+
# Looks first in project directory, second in home directory.
|
172
|
+
# If configuration file is not found in project directory, Development will not load.
|
173
|
+
#
|
174
|
+
def self.load_configuration_file( path )
|
175
|
+
|
176
|
+
# we build up a configuration line and process when we reach its end (the next line)
|
177
|
+
expression_string = ''
|
178
|
+
|
179
|
+
configuration_file_path = ::File.expand_path( path )
|
180
|
+
|
181
|
+
@line_number = 0
|
182
|
+
|
183
|
+
::File.open( configuration_file_path ).each_with_index do |this_line, this_line_number|
|
184
|
+
|
185
|
+
# when parse_configuration_file_line returns false we have a complete expression
|
186
|
+
while parse_configuration_file_line( expression_string, this_line )
|
187
|
+
|
188
|
+
# process expression_string
|
189
|
+
parse_configuration_expression( expression_string )
|
190
|
+
|
191
|
+
# reset expression_string
|
192
|
+
expression_string.clear
|
193
|
+
|
194
|
+
# update line number where expression begins
|
195
|
+
@line_number = this_line_number
|
196
|
+
unless this_line.empty?
|
197
|
+
@line_number += 1
|
198
|
+
end
|
199
|
+
|
200
|
+
# loop will cause start with this_line, which told parse_configuration_file_line
|
201
|
+
# that we were done, and which is therefore not yet processed
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
parse_configuration_expression( expression_string )
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
########################################
|
212
|
+
# self.parse_configuration_file_line #
|
213
|
+
########################################
|
214
|
+
|
215
|
+
###
|
216
|
+
# Parses configuration_file_line to construct expression_string from multiple configuration_file_lines.
|
217
|
+
#
|
218
|
+
# @param expression_string
|
219
|
+
#
|
220
|
+
# Configuration expression that spans one or more lines in configuration file.
|
221
|
+
#
|
222
|
+
# @param configuration_file_line
|
223
|
+
#
|
224
|
+
# Literal line from configuration file (may only be part of an expression).
|
225
|
+
#
|
226
|
+
# @return [true,false] Whether expression is still complete.
|
227
|
+
# True means configuration_file_line was not processed.
|
228
|
+
#
|
229
|
+
def self.parse_configuration_file_line( expression_string, configuration_file_line, continuation = false )
|
230
|
+
|
231
|
+
expression_complete = false
|
232
|
+
|
233
|
+
# if we begin with a comment we can throw away the line
|
234
|
+
if configuration_file_line[ 0 ] == '#'
|
235
|
+
|
236
|
+
# nothing to do - we just ignore the line
|
237
|
+
|
238
|
+
# if we begin with whitespace we have a continuation
|
239
|
+
elsif configuration_file_line[ 0 ] =~ /\s/
|
240
|
+
|
241
|
+
configuration_file_line.strip!
|
242
|
+
|
243
|
+
expression_complete = parse_configuration_file_line( expression_string, configuration_file_line, true )
|
244
|
+
|
245
|
+
elsif continuation
|
246
|
+
|
247
|
+
unless expression_string.empty?
|
248
|
+
unless configuration_file_line.empty?
|
249
|
+
expression_string << ' '
|
250
|
+
end
|
251
|
+
end
|
252
|
+
expression_string << configuration_file_line.strip
|
253
|
+
|
254
|
+
elsif expression_string.empty?
|
255
|
+
|
256
|
+
expression_string.replace( configuration_file_line.strip )
|
257
|
+
|
258
|
+
# otherwise we reached the next line of the configuration file
|
259
|
+
else
|
260
|
+
|
261
|
+
expression_complete = true
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
return expression_complete
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
#########################################
|
270
|
+
# self.parse_configuration_expression #
|
271
|
+
#########################################
|
272
|
+
|
273
|
+
###
|
274
|
+
# Parse single configuration expression built up from one or more actual configuration file lines.
|
275
|
+
#
|
276
|
+
# @param expression_string
|
277
|
+
#
|
278
|
+
# String describing configuration directive.
|
279
|
+
#
|
280
|
+
# @return [Object] self.
|
281
|
+
#
|
282
|
+
def self.parse_configuration_expression( expression_string )
|
283
|
+
|
284
|
+
case expression_string[ 0 ]
|
285
|
+
|
286
|
+
# + - named directory expression
|
287
|
+
when '+'
|
288
|
+
|
289
|
+
# either a directory definition or a general directory directive
|
290
|
+
# directory definitions are multiplart, whereas general directory definitions are one part
|
291
|
+
parse_named_directory_expression( expression_string )
|
292
|
+
|
293
|
+
# - - remove general path
|
294
|
+
when '-'
|
295
|
+
|
296
|
+
parse_remove_general_load_path_expression( expression_string )
|
297
|
+
|
298
|
+
# = - gemset expression
|
299
|
+
when '='
|
300
|
+
|
301
|
+
parse_gemset_expression( expression_string )
|
302
|
+
|
303
|
+
# @ - location expression
|
304
|
+
when '@'
|
305
|
+
|
306
|
+
parse_general_directory_or_location_expression( expression_string )
|
307
|
+
|
308
|
+
# ! - enable/disable expression
|
309
|
+
when '!'
|
310
|
+
|
311
|
+
parse_enable_disable_expression( expression_string )
|
312
|
+
|
313
|
+
# general path expression
|
314
|
+
else
|
315
|
+
|
316
|
+
parse_general_load_path_expression( expression_string )
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
return self
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
#########################################################
|
325
|
+
# self.parse_general_directory_or_location_expression #
|
326
|
+
#########################################################
|
327
|
+
|
328
|
+
###
|
329
|
+
# Parse expression string that has been determined as either a general path or location expression.
|
330
|
+
#
|
331
|
+
# @param expression_string
|
332
|
+
#
|
333
|
+
# String describing general path or location expression.
|
334
|
+
#
|
335
|
+
# @return [Object] Self.
|
336
|
+
#
|
337
|
+
def self.parse_general_directory_or_location_expression( expression_string )
|
338
|
+
|
339
|
+
# if we have multiple parts
|
340
|
+
if whitespace_index = expression_string =~ /\s/
|
341
|
+
|
342
|
+
parse_location_expression( expression_string )
|
343
|
+
|
344
|
+
# if we have one part
|
345
|
+
else
|
346
|
+
|
347
|
+
parse_general_load_path_expression( expression_string )
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
return self
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
###########################################
|
356
|
+
# self.parse_named_directory_expression #
|
357
|
+
###########################################
|
358
|
+
|
359
|
+
###
|
360
|
+
# Parse expression string that has been determined as a named directory expression.
|
361
|
+
#
|
362
|
+
# @param expression_string
|
363
|
+
#
|
364
|
+
# String describing named directory expression.
|
365
|
+
#
|
366
|
+
# @return [Object] Self.
|
367
|
+
#
|
368
|
+
def self.parse_named_directory_expression( expression_string )
|
369
|
+
|
370
|
+
# +directory_name path
|
371
|
+
|
372
|
+
unless whitespace_index = expression_string =~ /\s/
|
373
|
+
raise Exception::MalformedExpression::
|
374
|
+
MalformedNamedDirectoryExpression.new( expression_string, @line_number )
|
375
|
+
end
|
376
|
+
|
377
|
+
directory_name = expression_string.slice( 1, whitespace_index - 1 )
|
378
|
+
slice_length = expression_string.length - whitespace_index
|
379
|
+
path = expression_string.slice( whitespace_index + 1, slice_length ).strip
|
380
|
+
|
381
|
+
case path[0]
|
382
|
+
when '@'
|
383
|
+
path_parts = path.split( '/' )
|
384
|
+
named_path_name = path_parts.shift
|
385
|
+
named_path_name.slice!( 0, 1 )
|
386
|
+
path = ::File.join( directory( named_path_name ), path_parts )
|
387
|
+
end
|
388
|
+
|
389
|
+
@named_directories[ directory_name.to_sym ] = ::File.expand_path( path )
|
390
|
+
|
391
|
+
return self
|
392
|
+
|
393
|
+
end
|
394
|
+
|
395
|
+
##################################
|
396
|
+
# self.parse_gemset_expression #
|
397
|
+
##################################
|
398
|
+
|
399
|
+
###
|
400
|
+
# Parse expression string that has been determined as a gemset expression.
|
401
|
+
#
|
402
|
+
# @param expression_string
|
403
|
+
#
|
404
|
+
# String describing gemset expression.
|
405
|
+
#
|
406
|
+
# @return [Object] Self.
|
407
|
+
#
|
408
|
+
def self.parse_gemset_expression( expression_string )
|
409
|
+
|
410
|
+
# =gemset gem_or_set_name[,] ...
|
411
|
+
# =gemset +gem_or_set_name[,] ...
|
412
|
+
# =gemset -gem_or_set_name[,] ...
|
413
|
+
|
414
|
+
gemset_name = parse_base_action_from_expression_string( expression_string )
|
415
|
+
|
416
|
+
gemset = create_gemset( gemset_name )
|
417
|
+
|
418
|
+
parse_gem_names_from_expression_string( gemset, expression_string )
|
419
|
+
|
420
|
+
return self
|
421
|
+
|
422
|
+
end
|
423
|
+
|
424
|
+
###################################################
|
425
|
+
# self.parse_base_action_from_expression_string #
|
426
|
+
###################################################
|
427
|
+
|
428
|
+
def self.parse_base_action_from_expression_string( expression_string )
|
429
|
+
|
430
|
+
base_action = nil
|
431
|
+
|
432
|
+
# get rid of =
|
433
|
+
expression_string.slice!( 0, 1 )
|
434
|
+
|
435
|
+
if whitespace_index = expression_string =~ /\s/
|
436
|
+
base_action = expression_string.slice!( 0, whitespace_index )
|
437
|
+
else
|
438
|
+
base_action = expression_string.dup
|
439
|
+
expression_string.clear
|
440
|
+
end
|
441
|
+
|
442
|
+
expression_string.strip!
|
443
|
+
|
444
|
+
return base_action
|
445
|
+
|
446
|
+
end
|
447
|
+
|
448
|
+
#################################################
|
449
|
+
# self.parse_gem_names_from_expression_string #
|
450
|
+
#################################################
|
451
|
+
|
452
|
+
def self.parse_gem_names_from_expression_string( array, expression_string, require_exist = false )
|
453
|
+
|
454
|
+
while next_whitespace_index = expression_string =~ /\s/
|
455
|
+
parse_gem_name_from_expression_string( array, expression_string, next_whitespace_index )
|
456
|
+
end
|
457
|
+
|
458
|
+
# also slice till the end
|
459
|
+
parse_gem_name_from_expression_string( array, expression_string, expression_string.length, require_exist )
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
################################################
|
464
|
+
# self.parse_gem_name_from_expression_string #
|
465
|
+
################################################
|
466
|
+
|
467
|
+
###
|
468
|
+
# Helper method to slice gem name from expression string and add or subtract from gemset.
|
469
|
+
#
|
470
|
+
# @param gemset
|
471
|
+
#
|
472
|
+
# Gemset instance.
|
473
|
+
#
|
474
|
+
# @param expression_string
|
475
|
+
#
|
476
|
+
# Expression string.
|
477
|
+
#
|
478
|
+
# @param slice_to_index
|
479
|
+
#
|
480
|
+
# Index to slice expression string to.
|
481
|
+
#
|
482
|
+
def self.parse_gem_name_from_expression_string( array, expression_string, slice_to_index, require_exist = false )
|
483
|
+
|
484
|
+
gem_name = expression_string.slice!( 0, slice_to_index )
|
485
|
+
|
486
|
+
unless gem_name.empty?
|
487
|
+
|
488
|
+
case gem_name[ -1 ]
|
489
|
+
when ','
|
490
|
+
gem_name.slice!( -1, 1 )
|
491
|
+
end
|
492
|
+
|
493
|
+
should_add = true
|
494
|
+
|
495
|
+
case gem_name[ 0 ]
|
496
|
+
when '+'
|
497
|
+
gem_name.slice!( 0, 1 )
|
498
|
+
when '-'
|
499
|
+
gem_name.slice!( 0, 1 )
|
500
|
+
array.delete( gem_name.to_sym )
|
501
|
+
should_add = false
|
502
|
+
else
|
503
|
+
end
|
504
|
+
|
505
|
+
# ensure we have 'gem-subname' rather than 'gem/subname'
|
506
|
+
# we really just need one or the other consistently
|
507
|
+
gem_name.gsub!( '/', '-' )
|
508
|
+
|
509
|
+
gem_name = gem_name.to_sym
|
510
|
+
|
511
|
+
if require_exist
|
512
|
+
unless @enabled_gems.has_key?( gem_name ) or
|
513
|
+
@disabled_gems.has_key?( gem_name ) or
|
514
|
+
@gemsets.has_key?( gem_name)
|
515
|
+
raise Exception::ExpressionError::UnknownGemOrGemsetName.new( gem_name, @line_number )
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
if should_add
|
520
|
+
array.push( gem_name )
|
521
|
+
end
|
522
|
+
|
523
|
+
expression_string.strip!
|
524
|
+
|
525
|
+
end
|
526
|
+
|
527
|
+
return gem_name
|
528
|
+
|
529
|
+
end
|
530
|
+
|
531
|
+
########################
|
532
|
+
# self.create_gemset #
|
533
|
+
########################
|
534
|
+
|
535
|
+
def self.create_gemset( gemset_name )
|
536
|
+
|
537
|
+
gemset_name = gemset_name.to_sym
|
538
|
+
|
539
|
+
unless gemset = @gemsets[ gemset_name ]
|
540
|
+
@gemsets[ gemset_name ] = gemset = ::Array::Unique.new
|
541
|
+
end
|
542
|
+
|
543
|
+
return gemset
|
544
|
+
|
545
|
+
end
|
546
|
+
|
547
|
+
#################
|
548
|
+
# self.gemset #
|
549
|
+
#################
|
550
|
+
|
551
|
+
def self.gemset( gemset_name )
|
552
|
+
|
553
|
+
return @gemsets[ gemset_name.to_sym ]
|
554
|
+
|
555
|
+
end
|
556
|
+
|
557
|
+
####################################
|
558
|
+
# self.parse_location_expression #
|
559
|
+
####################################
|
560
|
+
|
561
|
+
###
|
562
|
+
# Parse expression string that has been determined as a location expression.
|
563
|
+
#
|
564
|
+
# @param expression_string
|
565
|
+
#
|
566
|
+
# String describing location expression.
|
567
|
+
#
|
568
|
+
# @return [Object] Self.
|
569
|
+
#
|
570
|
+
def self.parse_location_expression( expression_string )
|
571
|
+
|
572
|
+
# @directory_name gem_or_set_name[,] ...
|
573
|
+
|
574
|
+
directory_name = parse_base_action_from_expression_string( expression_string )
|
575
|
+
|
576
|
+
directory_name = directory_name.to_sym
|
577
|
+
|
578
|
+
unless @named_directories.has_key?( directory_name )
|
579
|
+
raise Exception::MalformedExpression::UnknownDirectoryName.new( directory_name, @line_number )
|
580
|
+
end
|
581
|
+
|
582
|
+
unless directory_members = @locations[ directory_name ]
|
583
|
+
@locations[ directory_name ] = directory_members = ::Array::Unique.new
|
584
|
+
end
|
585
|
+
|
586
|
+
parse_gem_names_from_expression_string( directory_members, expression_string )
|
587
|
+
|
588
|
+
directory_members.each do |this_gem_or_gemset|
|
589
|
+
if gemset = @gemsets[ this_gem_or_gemset ]
|
590
|
+
gemset.each do |this_gem|
|
591
|
+
@gem_locations[ this_gem ] = directory_name
|
592
|
+
end
|
593
|
+
else
|
594
|
+
@gem_locations[ this_gem_or_gemset ] = directory_name
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
return self
|
599
|
+
|
600
|
+
end
|
601
|
+
|
602
|
+
##########################################
|
603
|
+
# self.parse_enable_disable_expression #
|
604
|
+
##########################################
|
605
|
+
|
606
|
+
###
|
607
|
+
# Parse expression string that has been determined as a enable/disable expression.
|
608
|
+
#
|
609
|
+
# @param expression_string
|
610
|
+
#
|
611
|
+
# String describing enable/disable expression.
|
612
|
+
#
|
613
|
+
# @return [Object] Self.
|
614
|
+
#
|
615
|
+
def self.parse_enable_disable_expression( expression_string )
|
616
|
+
|
617
|
+
# !enable
|
618
|
+
# !disable
|
619
|
+
# !enable gem_or_set_name[,] ...
|
620
|
+
# !disable gem_or_set_name[,] ...
|
621
|
+
|
622
|
+
# enable or disable
|
623
|
+
enable_or_disable = parse_base_action_from_expression_string( expression_string )
|
624
|
+
|
625
|
+
gems = nil
|
626
|
+
unless expression_string.empty?
|
627
|
+
gems = ::Array::Unique.new
|
628
|
+
parse_gem_names_from_expression_string( gems, expression_string )
|
629
|
+
end
|
630
|
+
|
631
|
+
case enable_or_disable = enable_or_disable.to_sym
|
632
|
+
|
633
|
+
when :enable
|
634
|
+
|
635
|
+
if gems
|
636
|
+
gems.each do |this_gem, true_value|
|
637
|
+
@enabled_gems.push( this_gem )
|
638
|
+
@disabled_gems.delete( this_gem )
|
639
|
+
end
|
640
|
+
else
|
641
|
+
@disabled_gems.delete_if do |this_gem, true_value|
|
642
|
+
@enabled_gems.push( this_gem )
|
643
|
+
true
|
644
|
+
end
|
645
|
+
@enable_for_all = true
|
646
|
+
end
|
647
|
+
|
648
|
+
when :disable
|
649
|
+
|
650
|
+
if gems
|
651
|
+
gems.each do |this_gem, true_value|
|
652
|
+
@disabled_gems.push( this_gem )
|
653
|
+
@enabled_gems.delete( this_gem )
|
654
|
+
end
|
655
|
+
else
|
656
|
+
@enabled_gems.delete_if do |this_gem, true_value|
|
657
|
+
@disabled_gems.push( this_gem )
|
658
|
+
true
|
659
|
+
end
|
660
|
+
@enable_for_all = false
|
661
|
+
end
|
662
|
+
|
663
|
+
end
|
664
|
+
|
665
|
+
# do we have gems?
|
666
|
+
|
667
|
+
return self
|
668
|
+
|
669
|
+
end
|
670
|
+
|
671
|
+
#############################################
|
672
|
+
# self.parse_general_load_path_expression #
|
673
|
+
#############################################
|
674
|
+
|
675
|
+
###
|
676
|
+
# Parse expression string that has been determined as a general directory expression.
|
677
|
+
#
|
678
|
+
# @param expression_string
|
679
|
+
#
|
680
|
+
# String describing general directory expression.
|
681
|
+
#
|
682
|
+
# @return [Object] Self.
|
683
|
+
#
|
684
|
+
def self.parse_general_load_path_expression( expression_string )
|
685
|
+
|
686
|
+
# path/to/directory, ~/path/to/directory, /path/to/directory
|
687
|
+
# +directory_name/path/from/directory
|
688
|
+
|
689
|
+
case expression_string[ 0 ]
|
690
|
+
|
691
|
+
when '@'
|
692
|
+
|
693
|
+
path_parts = expression_string.split( '/' )
|
694
|
+
named_directory = path_parts.shift
|
695
|
+
named_directory.slice!( 0, 1 )
|
696
|
+
named_directory = named_directory.to_sym
|
697
|
+
expression_string = ::File.expand_path( ::File.join( directory( named_directory ), path_parts ) )
|
698
|
+
|
699
|
+
end
|
700
|
+
|
701
|
+
@general_load_paths.push( ::File.expand_path( expression_string ) )
|
702
|
+
|
703
|
+
return self
|
704
|
+
|
705
|
+
end
|
706
|
+
|
707
|
+
####################################################
|
708
|
+
# self.parse_remove_general_load_path_expression #
|
709
|
+
####################################################
|
710
|
+
|
711
|
+
###
|
712
|
+
# Parse expression string that has been determined as a remove-general-directory expression.
|
713
|
+
#
|
714
|
+
# @param expression_string
|
715
|
+
#
|
716
|
+
# String describing remove general directory expression.
|
717
|
+
#
|
718
|
+
# @return [Object] Self.
|
719
|
+
#
|
720
|
+
def self.parse_remove_general_load_path_expression( expression_string )
|
721
|
+
|
722
|
+
# -path/to/directory, -~/path/to/directory, -/path/to/directory
|
723
|
+
|
724
|
+
expression_string.slice!( 0, 1 )
|
725
|
+
path_string = expression_string.dup
|
726
|
+
expression_string.clear
|
727
|
+
|
728
|
+
@general_load_paths.delete( ::File.expand_path( path_string ) )
|
729
|
+
|
730
|
+
return self
|
731
|
+
|
732
|
+
end
|
733
|
+
|
734
|
+
##################
|
735
|
+
# self.require #
|
736
|
+
##################
|
737
|
+
|
738
|
+
###
|
739
|
+
# Filters requires to match configuration specifications for loading development paths instead of production gems.
|
740
|
+
#
|
741
|
+
# @param gem_name_or_path
|
742
|
+
#
|
743
|
+
# Gem name or path to file passed to {::Object#require}.
|
744
|
+
#
|
745
|
+
# @return [true,false] Whether Development handled gem_name_or_path (true) or processing should continue (false).
|
746
|
+
#
|
747
|
+
def self.require( gem_name_or_path )
|
748
|
+
|
749
|
+
did_load = false
|
750
|
+
|
751
|
+
# if our path ends with an extension we are not requiring a gem and thus not responsible for managing it
|
752
|
+
if ::File.extname( gem_name_or_path ).empty?
|
753
|
+
|
754
|
+
gem_name = gem_name_or_path.to_s
|
755
|
+
|
756
|
+
# ensure we have 'gem-subname' rather than 'gem/subname'
|
757
|
+
# we really just need one or the other consistently
|
758
|
+
gem_directory_name = gem_name.gsub( '/', '-' )
|
759
|
+
|
760
|
+
# look for gem name in enabled gems/gemsets
|
761
|
+
if @enabled_gems.include?( gem_name.to_sym ) or
|
762
|
+
@enable_for_all && ! @disabled_gems.include?( gem_name.to_sym )
|
763
|
+
|
764
|
+
if directory_name = @gem_locations[ gem_name.to_sym ] and
|
765
|
+
load_path = directory( directory_name ) and
|
766
|
+
gem_name_at_load_path?( load_path, gem_directory_name, true )
|
767
|
+
|
768
|
+
load_gem_from_path( load_path, gem_directory_name )
|
769
|
+
did_load = true
|
770
|
+
|
771
|
+
else
|
772
|
+
# look in each path for gem - use first to match
|
773
|
+
@general_load_paths.each do |this_load_path|
|
774
|
+
|
775
|
+
# look for gem name at load path
|
776
|
+
if gem_name_at_load_path?( this_load_path, gem_name )
|
777
|
+
load_gem_from_path( this_load_path, gem_name )
|
778
|
+
did_load = true
|
779
|
+
end
|
780
|
+
|
781
|
+
end
|
782
|
+
|
783
|
+
end
|
784
|
+
|
785
|
+
end
|
786
|
+
|
787
|
+
end
|
788
|
+
|
789
|
+
return did_load
|
790
|
+
|
791
|
+
end
|
792
|
+
|
793
|
+
#################################
|
794
|
+
# self.gem_name_at_load_path? #
|
795
|
+
#################################
|
796
|
+
|
797
|
+
###
|
798
|
+
# Query whether gem name is present at load path.
|
799
|
+
#
|
800
|
+
# @param load_path
|
801
|
+
#
|
802
|
+
# Path where gem directory might be located.
|
803
|
+
#
|
804
|
+
# @param gem_directory_name
|
805
|
+
#
|
806
|
+
# Name of gem. Assumes gem-subname is used rather than gem/subname.
|
807
|
+
#
|
808
|
+
# @return [true,false] Whether gem name is present.
|
809
|
+
#
|
810
|
+
def self.gem_name_at_load_path?( load_path, gem_directory_name, require_gem_at_path = false )
|
811
|
+
|
812
|
+
exists_at_load_path = false
|
813
|
+
|
814
|
+
gem_name = gem_directory_name.gsub( '-', '/' )
|
815
|
+
gem_path = ::File.join( load_path, gem_directory_name )
|
816
|
+
|
817
|
+
gem_require_file = ::File.join( gem_path, 'lib', gem_name ) + '.rb'
|
818
|
+
|
819
|
+
if ::Dir.exist?( ::File.expand_path( gem_path ) ) and
|
820
|
+
::File.exist?( ::File.expand_path( gem_require_file ) )
|
821
|
+
|
822
|
+
exists_at_load_path = true
|
823
|
+
|
824
|
+
end
|
825
|
+
|
826
|
+
return exists_at_load_path
|
827
|
+
|
828
|
+
end
|
829
|
+
|
830
|
+
#############################
|
831
|
+
# self.load_gem_from_path #
|
832
|
+
#############################
|
833
|
+
|
834
|
+
###
|
835
|
+
# Load gem from gem path. Assumes gem is present at path.
|
836
|
+
#
|
837
|
+
# @param load_path
|
838
|
+
#
|
839
|
+
# Path where gem directory might be located.
|
840
|
+
#
|
841
|
+
# @param gem_directory_name
|
842
|
+
#
|
843
|
+
# Name of gem. Assumes gem-subname is used rather than gem/subname.
|
844
|
+
#
|
845
|
+
# @return [true,false] Whether gem name is present.
|
846
|
+
#
|
847
|
+
def self.load_gem_from_path( load_path, gem_directory_name )
|
848
|
+
|
849
|
+
gem_name = gem_directory_name.gsub( '-', '/' )
|
850
|
+
gem_path = ::File.join( load_path, gem_directory_name )
|
851
|
+
|
852
|
+
gem_require_file = ::File.join( gem_path, 'lib', gem_name ) + '.rb'
|
853
|
+
|
854
|
+
require_relative( ::File.expand_path( gem_require_file ) )
|
855
|
+
|
856
|
+
end
|
857
|
+
|
858
|
+
end
|
859
|
+
|
860
|
+
require_relative 'development/require.rb'
|
861
|
+
|
862
|
+
require_relative 'development/exception/expression_error.rb'
|
863
|
+
require_relative 'development/exception/expression_error/unknown_directory_name.rb'
|
864
|
+
require_relative 'development/exception/expression_error/unknown_gem_or_gemset_name.rb'
|
865
|
+
|
866
|
+
require_relative 'development/exception/malformed_expression.rb'
|
867
|
+
require_relative 'development/exception/malformed_expression/malformed_named_directory_expression.rb'
|
868
|
+
require_relative 'development/exception/malformed_expression/malformed_gemset_expression.rb'
|
869
|
+
require_relative 'development/exception/malformed_expression/malformed_location_expression.rb'
|
870
|
+
require_relative 'development/exception/malformed_expression/malformed_enable_disable_expression.rb'
|
871
|
+
require_relative 'development/exception/malformed_expression/malformed_general_directory_expression.rb'
|
872
|
+
require_relative 'development/exception/malformed_expression/malformed_remove_general_directory_expression.rb'
|
873
|
+
|
874
|
+
###
|
875
|
+
# Object is enabled with Development require functionality.
|
876
|
+
#
|
877
|
+
class ::Object
|
878
|
+
include ::Development::Require
|
879
|
+
end
|
880
|
+
|
881
|
+
#::Development.load_configuration_file( ::File.join( '~', ConfigurationFileName ) )
|