penchant 0.2.24 → 0.2.26

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -32,6 +32,9 @@ source :rubygems
32
32
  # ensure git hooks are installed when a gemfile is processed, see below
33
33
  ensure_git_hooks!
34
34
 
35
+ # deploying to heroku and want 1.9.3 goodness?
36
+ ruby '1.9.3'
37
+
35
38
  gem 'rails', '3.2.3'
36
39
  # expands to:
37
40
  #
data/Rakefile CHANGED
@@ -1,22 +1,6 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
- begin
5
- require 'rspec/core/rake_task'
6
-
7
- RSpec::Core::RakeTask.new(:spec)
8
- rescue LoadError
9
- "#$! - no rspec"
10
- end
11
-
12
- begin
13
- require 'rspec/core/rake_task'
14
-
15
- RSpec::Core::RakeTask.new(:spec)
16
- rescue LoadError
17
- "#$! - no rspec"
18
- end
19
-
20
4
  begin
21
5
  require 'cucumber'
22
6
  require 'cucumber/rake/task'
@@ -28,5 +12,5 @@ rescue LoadError
28
12
  "#$! - no cucumber"
29
13
  end
30
14
 
31
- task :default => [ :spec, :cucumber ]
15
+ task :default => [ :cucumber ]
32
16
 
data/features/cli.feature CHANGED
@@ -1,6 +1,6 @@
1
1
  Feature: CLI
2
2
  Scenario: Switch back to the original pre-deployment environment
3
- Given I have the file "tmp/Gemfile.erb" with the content:
3
+ Given I have the file "tmp/Gemfile.penchant" with the content:
4
4
  """
5
5
  gem 'rake'
6
6
  """
@@ -12,7 +12,7 @@ Feature: CLI
12
12
  Then the file "tmp/Gemfile" should have the following content:
13
13
  """
14
14
  # generated by penchant, environment: local
15
- gem 'rake'
15
+ gem "rake"
16
16
  """
17
17
  And the output should include "fallback: other"
18
18
 
@@ -263,6 +263,7 @@ Feature: Gemfiles
263
263
  gem "two", {:require=>nil}
264
264
  """
265
265
 
266
+ @wip
266
267
  Scenario: Set the opposite environment in the environment defaults
267
268
  Given I have the file "Gemfile.penchant" with the content:
268
269
  """
@@ -411,3 +412,14 @@ Feature: Gemfiles
411
412
  gem "one"
412
413
  """
413
414
 
415
+ Scenario: Pass through the Ruby version
416
+ Given I have the file "Gemfile.penchant" with the content:
417
+ """
418
+ ruby '1.9.3'
419
+ """
420
+ When I rebuild the Gemfile for "local" mode
421
+ Then the file "Gemfile" should have the following content:
422
+ """
423
+ # generated by penchant, environment: local
424
+ ruby "1.9.3"
425
+ """
@@ -1,3 +1,3 @@
1
1
  Given /^I am on the "([^"]*)" platform$/ do |os|
2
- Penchant::Gemfile::PenchantFile.any_instance.stubs(:current_os).returns(os.to_sym)
2
+ Penchant::FileProcessor.any_instance.stubs(:current_os).returns(os.to_sym)
3
3
  end
@@ -0,0 +1,20 @@
1
+ module Penchant
2
+ class CustomProperty
3
+ def initialize(value)
4
+ @value = value
5
+ end
6
+
7
+ def process(values)
8
+ if @value.respond_to?(:call)
9
+ @value.call(*values).to_a
10
+ else
11
+ @value.collect do |k, v|
12
+ v = v.dup.gsub(%r{\$(\d+)}) { |m| values[m.to_i - 1 ] }
13
+
14
+ [ k, v ]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,11 @@
1
+ module Penchant
2
+ class Defaults
3
+ def initialize
4
+ @defaults = {}
5
+ end
6
+
7
+ def [](key)
8
+ @defaults[key.to_s] ||= {}
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module Penchant
2
+ class Env
3
+ attr_accessor :name
4
+
5
+ def initialize(name)
6
+ @name = name.to_s
7
+ end
8
+
9
+ def ==(other)
10
+ @name == other.name
11
+ end
12
+
13
+ def to_s
14
+ "@#{name}"
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,201 @@
1
+ module Penchant
2
+ class FileProcessor
3
+ attr_reader :environment, :is_deployment, :available_environments, :defined_git_repos
4
+
5
+ ANY_ENVIRONMENT = :any_environment
6
+
7
+ def self.result(data, *args)
8
+ new(data).result(*args)
9
+ end
10
+
11
+ def initialize(data)
12
+ @data = data
13
+ @available_environments = []
14
+ @defined_git_repos = []
15
+ @defaults = Defaults.new
16
+ @properties = PropertyStackBuilder.new(@defaults)
17
+
18
+ @_current_env_defaults = {}
19
+ end
20
+
21
+ def result(_env, _is_deployment)
22
+ @environment = _env.to_s.to_sym
23
+ @is_deployment = _is_deployment
24
+
25
+ @output = []
26
+
27
+ instance_eval(@data)
28
+
29
+ @output.join("\n")
30
+ end
31
+
32
+ def <<(string)
33
+ @output << string
34
+ end
35
+
36
+ def env(*args)
37
+ options = {}
38
+ options = args.pop if args.last.kind_of?(::Hash)
39
+
40
+ @available_environments += args
41
+
42
+ requested_env_defaults = _defaults_for(Env.new(environment))
43
+
44
+ if block_given?
45
+ if for_environment?(args)
46
+ @_current_env_defaults = requested_env_defaults
47
+ yield
48
+ @_current_env_defaults = {}
49
+ else
50
+ if opposite_environment = (options[:opposite] or requested_env_defaults[:opposite])
51
+ if for_environment?([ environment, args, opposite_environment ].flatten.uniq)
52
+ @_current_env_defaults = requested_env_defaults
53
+ @_strip_pathing_options = true
54
+ yield
55
+ @_strip_pathing_options = false
56
+ @_current_env_defaults = {}
57
+ end
58
+ end
59
+ end
60
+ else
61
+ Env.new(args.shift)
62
+ end
63
+ end
64
+
65
+ def property(name, hash = nil, &block)
66
+ @properties[name] = hash || block
67
+ end
68
+
69
+ def opposites(left, right)
70
+ @defaults[Env.new(left)][:opposite] = right
71
+ @defaults[Env.new(right)][:opposite] = left
72
+ end
73
+
74
+ def for_environment?(envs)
75
+ envs.include?(environment) || environment == ANY_ENVIRONMENT
76
+ end
77
+
78
+ def no_deployment
79
+ yield if !is_deployment
80
+ end
81
+
82
+ def ensure_git_hooks!
83
+ Penchant::Hooks.install!
84
+ end
85
+
86
+ def os(*args)
87
+ yield if args.include?(current_os)
88
+ end
89
+
90
+ def defaults_for(*args)
91
+ defaults = args.pop
92
+
93
+ args.flatten.each do |gem|
94
+ @defaults[gem].merge!(defaults)
95
+ end
96
+ end
97
+
98
+ protected
99
+ def args_to_string(args)
100
+ args.inspect[1..-2]
101
+ end
102
+
103
+ def split_args(args)
104
+ template = {}
105
+
106
+ while args.last.instance_of?(Hash)
107
+ template.merge!(args.pop)
108
+ end
109
+
110
+ [ args, template ]
111
+ end
112
+
113
+ def call_and_indent_output(block = nil, &given_block)
114
+ index = @output.length
115
+ (block || given_block).call
116
+ index.upto(@output.length - 1) do |i|
117
+ @output[i] = " " + @output[i]
118
+ end
119
+ end
120
+
121
+ def _defaults_for(gem_name)
122
+ result = @_current_env_defaults
123
+ result.merge(@defaults[gem_name] || {})
124
+ end
125
+
126
+ def current_os
127
+ require 'rbconfig'
128
+ case host_os = RbConfig::CONFIG['host_os']
129
+ when /darwin/
130
+ :darwin
131
+ when /linux/
132
+ :linux
133
+ else
134
+ host_os[%r{^[a-z]+}, 1].to_sym
135
+ end
136
+ end
137
+
138
+ def gem(*args)
139
+ gem_name = [ args.shift ]
140
+ template = {}
141
+
142
+ if args.last.kind_of?(::Hash)
143
+ template = args.pop
144
+ end
145
+
146
+ version = args.first
147
+
148
+ options = @properties.create_stack_for(template, @_strip_pathing_options).process_for_gem(gem_name.first, @_current_env_defaults)
149
+
150
+ args = [ gem_name.first ]
151
+ args << version if version
152
+
153
+ if options[:git]
154
+ @defined_git_repos << Penchant::Repo.new(options[:git])
155
+ end
156
+
157
+ args << options if !options.empty?
158
+
159
+ self << %{gem #{args_to_string(args)}}
160
+ end
161
+
162
+ def gems(*args)
163
+ gems, template = split_args(args)
164
+
165
+ gems.flatten.each do |gem_name|
166
+ options = @properties.create_stack_for(template, @_strip_pathing_options).process_for_gem(gem_name)
167
+
168
+ args = [ gem_name ]
169
+ args << options if !options.empty?
170
+
171
+ gem *args
172
+ end
173
+ end
174
+
175
+ def group(*args, &block)
176
+ self << ""
177
+ self << %{group #{args_to_string(args)} do}
178
+
179
+ call_and_indent_output(block)
180
+
181
+ self << %{end}
182
+ end
183
+
184
+ def ruby(*args)
185
+ passthrough :ruby, *args
186
+ end
187
+
188
+ def gemspec
189
+ passthrough :gemspec
190
+ end
191
+
192
+ def source(*args)
193
+ passthrough :source, *args
194
+ end
195
+
196
+ def passthrough(method, *args)
197
+ self << %{#{method} #{args_to_string(args)}}.strip
198
+ end
199
+ end
200
+ end
201
+
@@ -1,5 +1,3 @@
1
- require 'erb'
2
-
3
1
  module Penchant
4
2
  class Gemfile
5
3
  attr_reader :path, :is_deployment
@@ -51,28 +49,20 @@ module Penchant
51
49
  File.file?('.penchant')
52
50
  end
53
51
 
54
- def gemfile_erb_path
55
- file_in_path('Gemfile.erb')
56
- end
57
-
58
52
  def gemfile_penchant_path
59
53
  file_in_path('Gemfile.penchant')
60
54
  end
61
55
 
62
- def has_gemfile_erb?
63
- File.file?(gemfile_erb_path)
64
- end
65
-
66
56
  def has_gemfile_penchant?
67
57
  File.file?(gemfile_penchant_path)
68
58
  end
69
59
 
70
60
  def has_processable_gemfile?
71
- has_gemfile_erb? || has_gemfile_penchant?
61
+ has_gemfile_penchant?
72
62
  end
73
63
 
74
64
  def processable_gemfile_path
75
- has_gemfile_erb? ? gemfile_erb_path : gemfile_penchant_path
65
+ gemfile_penchant_path
76
66
  end
77
67
 
78
68
  def environment
@@ -83,329 +73,6 @@ module Penchant
83
73
  gemfile_header['deployment mode'] != nil
84
74
  end
85
75
 
86
- class Env
87
- attr_accessor :name
88
-
89
- def initialize(name)
90
- @name = name.to_s
91
- end
92
-
93
- def ==(other)
94
- @name == other.name
95
- end
96
-
97
- def to_s
98
- "@#{name}"
99
- end
100
- end
101
-
102
- class FileProcessor
103
- attr_reader :environment, :is_deployment, :available_environments, :defined_git_repos
104
-
105
- ANY_ENVIRONMENT = :any_environment
106
-
107
- def self.result(data, *args)
108
- new(data).result(*args)
109
- end
110
-
111
- def self.handle_result(&block)
112
- if block
113
- @handle_result = block
114
- else
115
- @handle_result
116
- end
117
- end
118
-
119
- def initialize(data)
120
- @data = data
121
- @available_environments = []
122
- @defined_git_repos = []
123
- @defaults = {}
124
- @properties = {}
125
-
126
- @_current_env_defaults = {}
127
- end
128
-
129
- def result(_env, _is_deployment)
130
- @environment = _env.to_s.to_sym
131
- @is_deployment = _is_deployment
132
-
133
- @output = []
134
-
135
- handle_result(@data)
136
-
137
- @output.join("\n")
138
- end
139
-
140
- def env(*args)
141
- options = {}
142
- options = args.pop if args.last.kind_of?(::Hash)
143
-
144
- @available_environments += args
145
-
146
- requested_env_defaults = _defaults_for(Env.new(environment))
147
-
148
- if block_given?
149
- if for_environment?(args)
150
- @_current_env_defaults = requested_env_defaults
151
- yield
152
- @_current_env_defaults = {}
153
- else
154
- if opposite_environment = (options[:opposite] or requested_env_defaults[:opposite])
155
- if for_environment?([ environment, args, opposite_environment ].flatten.uniq)
156
- @_current_env_defaults = requested_env_defaults
157
- @_strip_pathing_options = true
158
- yield
159
- @_strip_pathing_options = false
160
- @_current_env_defaults = {}
161
- end
162
- end
163
- end
164
- else
165
- Env.new(args.shift)
166
- end
167
- end
168
-
169
- def property(name, hash = nil, &block)
170
- @properties[name] = hash || block
171
- end
172
-
173
- def opposites(left, right)
174
- @defaults[Env.new(left).to_s] ||= {}
175
- @defaults[Env.new(left).to_s][:opposite] = right
176
-
177
- @defaults[Env.new(right).to_s] ||= {}
178
- @defaults[Env.new(right).to_s][:opposite] = left
179
- end
180
-
181
- def for_environment?(envs)
182
- envs.include?(environment) || environment == ANY_ENVIRONMENT
183
- end
184
-
185
- def no_deployment
186
- yield if !is_deployment
187
- end
188
-
189
- def ensure_git_hooks!
190
- Penchant::Hooks.install!
191
- end
192
-
193
- def os(*args)
194
- yield if args.include?(current_os)
195
- end
196
-
197
- def defaults_for(*args)
198
- defaults = args.pop
199
-
200
- args.flatten.each do |gem|
201
- @defaults[gem.to_s] ||= {}
202
- @defaults[gem.to_s].merge!(defaults)
203
- end
204
- end
205
-
206
- protected
207
- def args_to_string(args)
208
- args.inspect[1..-2]
209
- end
210
-
211
- def split_args(args)
212
- template = {}
213
-
214
- while args.last.instance_of?(Hash)
215
- template.merge!(args.pop)
216
- end
217
-
218
- [ args, template ]
219
- end
220
-
221
- def call_and_indent_output(block)
222
- index = @output.length
223
- block.call
224
- index.upto(@output.length - 1) do |i|
225
- @output[i] = " " + @output[i]
226
- end
227
- end
228
-
229
- def process_options(gem_name, template = {})
230
- properties = {}
231
-
232
- property_stack = template.to_a
233
-
234
- original_properties = process_option_stack(gem_name, property_stack)
235
-
236
- if @_strip_pathing_options
237
- [ :git, :branch, :path ].each { |key| original_properties.delete(key) }
238
- end
239
-
240
- properties = process_option_stack(gem_name, _defaults_for(gem_name).to_a).merge(original_properties)
241
-
242
- properties.delete(:opposite)
243
-
244
- Hash[properties.sort]
245
- end
246
-
247
- def process_option_stack(gem_name, stack)
248
- property_stack = stack.dup
249
- properties = {}
250
-
251
- while !property_stack.empty?
252
- key, value = property_stack.shift
253
-
254
- if property = @properties[key]
255
- values = [ value ].flatten
256
-
257
- if property.respond_to?(:call)
258
- property.call(*values).each do |k, v|
259
- property_stack.push([ k, v ])
260
- end
261
- else
262
- property.each do |k, v|
263
- v = v.dup.gsub(%r{\$(\d+)}) { |m| values[m.to_i - 1 ] }
264
- property_stack.push([ k, v ])
265
- end
266
- end
267
- else
268
- value = value % gem_name if value.respond_to?(:%)
269
-
270
- properties[key] = value
271
- end
272
- end
273
-
274
- properties
275
- end
276
-
277
- def _defaults_for(gem_name)
278
- result = @_current_env_defaults
279
- result.merge(@defaults[gem_name.to_s] || {})
280
- end
281
-
282
- def current_os
283
- require 'rbconfig'
284
- case host_os = RbConfig::CONFIG['host_os']
285
- when /darwin/
286
- :darwin
287
- when /linux/
288
- :linux
289
- else
290
- host_os[%r{^[a-z]+}, 1].to_sym
291
- end
292
- end
293
- end
294
-
295
- class ERBFile < FileProcessor
296
- def handle_result(data)
297
- $stderr.puts "ERB files are deprecated. Please convert them to the Ruby format."
298
-
299
- @output << ERB.new(data, nil, nil, '@_erbout').result(binding)
300
- end
301
-
302
- def env(check, template = {}, &block)
303
- if check.to_s == @environment.to_s
304
- original_erbout = @_erbout.dup
305
- @_erbout = ''
306
-
307
- output = instance_eval(&block).lines.to_a
308
-
309
- output.each do |line|
310
- if gem_name = line[%r{gem ['"]([^'"]+)['"]}, 1]
311
- new_line = line.rstrip
312
-
313
- if !(options = process_options(gem_name, template)).empty?
314
- new_line += ", #{options.inspect}"
315
- end
316
-
317
- line.replace(new_line + "\n")
318
- end
319
- end
320
-
321
- @_erbout = original_erbout + output.join
322
- end
323
- end
324
-
325
- def gems(*gems)
326
- template = {}
327
- template = gems.pop if gems.last.instance_of?(Hash)
328
-
329
- gems.flatten.each do |gem|
330
- @_current_gem = gem
331
- if block_given?
332
- yield
333
- else
334
- @_erbout += gem(template) + "\n"
335
- end
336
- end
337
- end
338
-
339
- def gem(template = {})
340
- output = "gem '#{@_current_gem}'"
341
- options = process_options(@_current_gem, template)
342
- if !options.empty?
343
- output += ", #{options.inspect}"
344
- end
345
- output
346
- end
347
- end
348
-
349
- class PenchantFile < FileProcessor
350
- def handle_result(data)
351
- instance_eval(data)
352
- end
353
-
354
- def gem(*args)
355
- gem_name = [ args.shift ]
356
- template = {}
357
-
358
- if args.last.kind_of?(::Hash)
359
- template = args.pop
360
- end
361
-
362
- version = args.first
363
-
364
- options = process_options(gem_name.first, template)
365
-
366
- args = [ gem_name.first ]
367
- args << version if version
368
-
369
- if options[:git]
370
- @defined_git_repos << Penchant::Repo.new(options[:git])
371
- end
372
-
373
- args << options if !options.empty?
374
-
375
- @output << %{gem #{args_to_string(args)}}
376
- end
377
-
378
- def gemspec
379
- @output << %{gemspec}
380
- end
381
-
382
- def gems(*args)
383
- gems, template = split_args(args)
384
-
385
- gems.flatten.each do |gem_name|
386
- options = process_options(gem_name, template)
387
-
388
- args = [ gem_name ]
389
- args << options if !options.empty?
390
-
391
- gem *args
392
- end
393
- end
394
-
395
- def group(*args, &block)
396
- @output << ""
397
- @output << %{group #{args_to_string(args)} do}
398
-
399
- call_and_indent_output(block)
400
-
401
- @output << %{end}
402
- end
403
-
404
- def source(*args)
405
- @output << %{source #{args_to_string(args)}}
406
- end
407
- end
408
-
409
76
  def available_environments
410
77
  process
411
78
  builder.available_environments
@@ -456,16 +123,7 @@ module Penchant
456
123
  end
457
124
 
458
125
  def builder
459
- return @builder if @builder
460
-
461
- klass = case File.extname(processable_gemfile_path)
462
- when '.penchant'
463
- PenchantFile
464
- when '.erb'
465
- ERBFile
466
- end
467
-
468
- @builder = klass.new(template)
126
+ @builder ||= FileProcessor.new(template)
469
127
  end
470
128
 
471
129
  def template