bpm 1.0.0.beta.6 → 1.0.0.beta.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +26 -0
  2. data/TODO.md +0 -3
  3. data/lib/bpm/cli/base.rb +3 -3
  4. data/lib/bpm/default.json +5 -1
  5. data/lib/bpm/errors.rb +8 -0
  6. data/lib/bpm/generator.rb +8 -0
  7. data/lib/bpm/init_generator.rb +1 -1
  8. data/lib/bpm/package.rb +113 -11
  9. data/lib/bpm/pipeline/format_processor.rb +28 -0
  10. data/lib/bpm/pipeline/generated_asset.rb +2 -2
  11. data/lib/bpm/pipeline/package_pipeline.rb +79 -0
  12. data/lib/bpm/pipeline/plugin_asset.rb +7 -8
  13. data/lib/bpm/pipeline/plugin_processor.rb +52 -0
  14. data/lib/bpm/pipeline.rb +89 -26
  15. data/lib/bpm/project.rb +55 -29
  16. data/lib/bpm/version.rb +1 -1
  17. data/lib/bpm.rb +3 -1
  18. data/spec/cli/list_spec.rb +1 -0
  19. data/spec/cli/pack_spec.rb +125 -103
  20. data/spec/fixtures/projects/coffee/coffee.json +17 -0
  21. data/spec/fixtures/projects/coffee/index.html.handlebars +1 -0
  22. data/spec/fixtures/projects/coffee/lib/main.coffee +1 -0
  23. data/spec/fixtures/projects/coffee/packages/coffee-script/compiler.js +4 -0
  24. data/spec/fixtures/projects/coffee/packages/coffee-script/lib/main.js +1 -0
  25. data/spec/fixtures/projects/coffee/packages/coffee-script/package.json +15 -0
  26. data/spec/fixtures/projects/coffee/packages/handlebars/format.js +6 -0
  27. data/spec/fixtures/projects/coffee/packages/handlebars/lib/main.js +2 -0
  28. data/spec/fixtures/projects/coffee/packages/handlebars/package.json +16 -0
  29. data/spec/fixtures/projects/coffee/packages/spade/lib/main.js +1 -0
  30. data/spec/fixtures/projects/coffee/packages/spade/package.json +14 -0
  31. data/spec/fixtures/projects/coffee/packages/spade/transport.js +3 -0
  32. data/spec/fixtures/projects/coffee/templates/section.handlebars +1 -0
  33. data/spec/fixtures/projects/minitrans/packages/transport/package.json +5 -1
  34. data/spec/fixtures/projects/transporter/packages/transport/package.json +5 -1
  35. data/spec/package_pipeline_spec.rb +30 -0
  36. data/spec/package_spec.rb +328 -316
  37. data/spec/plugins/format_spec.rb +38 -0
  38. data/spec/plugins/transport_spec.rb +1 -1
  39. data/spec/support/matchers.rb +1 -2
  40. metadata +57 -24
data/CHANGELOG.md CHANGED
@@ -1,4 +1,30 @@
1
1
 
2
+ * First cut at support for formats.
3
+ * Also introduces new requirement for defining transport and format plugins
4
+ in the package.json. Now you must use the "bpm:provides" keyword:
5
+
6
+ New format for defining a format or transport plugin:
7
+
8
+ "bpm:provides": {
9
+ "transport": {
10
+ "main": "path/to/transport/plugin"
11
+ },
12
+
13
+ "format:EXT": {
14
+ "default:mime": "application/javascript",
15
+ "main": "path/to/format/plugin"
16
+ }
17
+ }
18
+
19
+ # 1.0.0.beta.7
20
+
21
+ * fixed bug where local packages that are indirect dependencies of other
22
+ remote packages could cause bpm to error out unable to find dependencies.
23
+
24
+ * improved logging slightly
25
+
26
+ # 1.0.0.beta.6 (yanked)
27
+
2
28
  * bpm now passes a context object with build settings and a minify option
3
29
  to plugins - this will allow spade to support string loading.
4
30
 
data/TODO.md CHANGED
@@ -2,9 +2,6 @@
2
2
 
3
3
  ## Important Features
4
4
 
5
- * Multiple format support. Format processors supplied by dependencies
6
- should
7
- be used to resolve formats.
8
5
  * HTML manifest support [in general we need a way for packages to generate
9
6
  new assets]
10
7
  * cache-friendly URLs
data/lib/bpm/cli/base.rb CHANGED
@@ -277,7 +277,7 @@ module BPM
277
277
  package = local.pack(File.join(package_path, "package.json"), options[:email])
278
278
 
279
279
  if package.errors.empty?
280
- puts "Successfully built package: #{package.file_name}"
280
+ say "Successfully built package: #{package.file_name}"
281
281
  else
282
282
  failure_message = "BPM encountered the following problems building your package:"
283
283
  package.errors.each do |error|
@@ -298,7 +298,7 @@ module BPM
298
298
  begin
299
299
  package = local.unpack(path, options[:target])
300
300
  unpack_path = File.expand_path(File.join(Dir.pwd, options[:target], package.full_name))
301
- puts "Unpacked package into: #{unpack_path}"
301
+ say "Unpacked package into: #{unpack_path}"
302
302
  rescue Errno::EACCES, LibGems::FilePermissionError => ex
303
303
  abort "There was a problem unpacking #{path}:\n#{ex.message}"
304
304
  end
@@ -391,7 +391,7 @@ module BPM
391
391
  abort %{No packages found matching "#{names.join('", "')}".}
392
392
  else
393
393
  packages.each do |name, versions|
394
- puts "#{name} (#{versions.sort.reverse.join(", ")})"
394
+ say "#{name} (#{versions.sort.reverse.join(", ")})"
395
395
  end
396
396
  end
397
397
  end
data/lib/bpm/default.json CHANGED
@@ -5,5 +5,9 @@
5
5
  },
6
6
 
7
7
  "dependencies": {
8
- }
8
+ },
9
+
10
+ "summary": "(no summary)",
11
+ "description": "(no description)",
12
+ "homepage": "(no homepage)"
9
13
  }
data/lib/bpm/errors.rb CHANGED
@@ -48,4 +48,12 @@ module BPM
48
48
  end
49
49
  end
50
50
 
51
+ class TooManyTransportsError < BPM::Error
52
+ def format_message(pkg)
53
+ err = <<EOF
54
+ #{pkg.name} depends on #{pkg.provided_transports.size} packages that define transport plugins. Select a plugin by adding a `bpm:use:transport` property to the package.json
55
+ EOF
56
+ end
57
+ end
58
+
51
59
  end
data/lib/bpm/generator.rb CHANGED
@@ -45,6 +45,14 @@ module BPM
45
45
  [@template_path, self.class.source_root].compact
46
46
  end
47
47
 
48
+ def shell
49
+ @shell ||= Thor::Base.shell.new
50
+ end
51
+
52
+ def say(*args)
53
+ shell.say *args
54
+ end
55
+
48
56
  private
49
57
 
50
58
  def app_const
@@ -26,7 +26,7 @@ module BPM
26
26
  package.load_json
27
27
  new_json = JSON.pretty_generate(package.as_json)
28
28
  File.open("#{name}.json", "w"){|f| f.write new_json }
29
- puts "created #{name}.json from package.json"
29
+ say "created #{name}.json from package.json"
30
30
  end
31
31
 
32
32
  end
data/lib/bpm/package.rb CHANGED
@@ -26,10 +26,11 @@ module BPM
26
26
  "bpm:formats" => :hash,
27
27
  "bpm:transport" => :string,
28
28
  "bpm:use:transport" => :string,
29
- "bpm:minifier" => :string
29
+ "bpm:minifier" => :string,
30
+ "bpm:provides" => :hash
30
31
  }
31
32
 
32
- PLUGIN_FIELDS = %w[bpm:formats bpm:minifier bpm:transport bpm:use:transport]
33
+ PLUGIN_FIELDS = %w[bpm:minifier]
33
34
 
34
35
  # Fields that can be loaded straight into the gemspec
35
36
  SPEC_FIELDS = %w[name email]
@@ -75,9 +76,9 @@ module BPM
75
76
  spec.licenses = licenses.map{|l| l["type"]}
76
77
  spec.executables = bin_files.map{|p| File.basename(p) } if bin_path
77
78
 
78
- spec.homepage = homepage || url
79
- spec.description = description || summary
80
- spec.summary = summary || description
79
+ spec.homepage = self.homepage || self.url
80
+ spec.description = self.description || self.summary
81
+ spec.summary = self.summary || self.description
81
82
 
82
83
  metadata = Hash[METADATA_FIELDS.map{|f| [f, send(c2u(f)) ] }]
83
84
  spec.requirements = [metadata.to_json]
@@ -124,6 +125,14 @@ module BPM
124
125
  as_json.to_json
125
126
  end
126
127
 
128
+ def shell
129
+ @shell ||= Thor::Base.shell.new
130
+ end
131
+
132
+ def say(*args)
133
+ shell.say *args
134
+ end
135
+
127
136
  def full_name
128
137
  "#{name}-#{version}"
129
138
  end
@@ -133,7 +142,10 @@ module BPM
133
142
  end
134
143
 
135
144
  def directory_files
136
- dir_names = directories.reject { |k,_| k == 'tests' }.values
145
+ dir_names = [bin_path, lib_path, tests_path]
146
+ dir_names += directories.reject { |k,_| dir_names.include?(k) }.values
147
+ dir_names.reject! { |t| t == tests_path }
148
+
137
149
  build_names = bpm_build.values.map do |hash|
138
150
  hash['directories'] || hash['assets']
139
151
  end
@@ -144,6 +156,12 @@ module BPM
144
156
  val
145
157
  end
146
158
 
159
+ bpm_provides.each do |_,values|
160
+ val = values['main']
161
+ val = val && val =~ /^#{name}\// ? val[name.size+1..-1]+'.js' : nil
162
+ build_names << val if val
163
+ end
164
+
147
165
  (dir_names+build_names).flatten.compact.uniq.map do |dir|
148
166
  glob_files(dir)
149
167
  end.flatten
@@ -207,6 +225,12 @@ module BPM
207
225
  ret.merge! minifier
208
226
  end
209
227
  end
228
+
229
+ bpm_provides.each do |_,opts|
230
+ next unless opts.is_a?(Hash) && opts['dependencies']
231
+ ret.merge! opts['dependencies']
232
+ end
233
+
210
234
  ret
211
235
  end
212
236
 
@@ -233,12 +257,12 @@ module BPM
233
257
  end
234
258
 
235
259
  def has_json?
236
- File.exist?(json_path)
260
+ !!json_path && File.exist?(json_path)
237
261
  end
238
262
 
239
263
  def validate_name_and_path
240
264
  filename = File.basename(root_path)
241
- unless filename==name || filename=="#{name}-#{version}"
265
+ unless name.nil? || name.empty? || filename==name || filename=="#{name}-#{version}"
242
266
  raise BPM::InvalidPackageNameError.new self
243
267
  end
244
268
  end
@@ -276,6 +300,10 @@ module BPM
276
300
  self.author = spec.authors.first
277
301
  self.version = spec.version.to_s
278
302
 
303
+ self.description = spec.description
304
+ self.summary = spec.summary
305
+ self.homepage = spec.homepage
306
+
279
307
  metadata = spec.requirements.first
280
308
  if metadata
281
309
  metadata = JSON.parse(metadata)
@@ -311,6 +339,69 @@ module BPM
311
339
  ret
312
340
  end
313
341
 
342
+ def merged_dependencies(*kinds)
343
+ kinds.inject({}) do |ret, kind|
344
+ deps = case kind
345
+ when :runtime
346
+ dependencies
347
+ when :development
348
+ dependencies_development
349
+ when :build
350
+ dependencies_build
351
+ end
352
+ ret.merge! deps
353
+ end
354
+ end
355
+
356
+ def used_dependencies(project)
357
+ if project.has_local_package?(self.name)
358
+ merged_dependencies(:runtime, :development)
359
+ else
360
+ merged_dependencies(:runtime)
361
+ end
362
+ end
363
+
364
+ def provided_formats
365
+ ret = {}
366
+ bpm_provides.each do | key, opts |
367
+ ret[key[7..-1]] = opts if key =~ /^format:/
368
+ end
369
+ ret
370
+ end
371
+
372
+ def used_formats(project)
373
+ pkgs=project.map_to_packages used_dependencies(project)
374
+ pkgs.inject({}) { |ret, pkg| ret.merge!(pkg.provided_formats) }
375
+ end
376
+
377
+ def provided_preprocessors
378
+ bpm_provides['preprocessors'] || []
379
+ end
380
+
381
+ def used_preprocessors(project)
382
+ pkgs=project.map_to_packages used_dependencies(project)
383
+ pkgs.map { |pkg| pkg.provided_postprocessors }.flatten
384
+ end
385
+
386
+ def provided_postprocessors
387
+ bpm_provides['postprocessors'] || []
388
+ end
389
+
390
+ def used_postprocessors(project)
391
+ pkgs=project.map_to_packages used_dependencies(project)
392
+ pkgs.map { |pkg| pkg.provided_postprocessors }.flatten
393
+ end
394
+
395
+ def provided_transport
396
+ bpm_provides['transport']
397
+ end
398
+
399
+ def used_transports(project)
400
+ pkgs=project.map_to_packages used_dependencies(project)
401
+ pkgs.map { |pkg| pkg.provided_transport }.compact.flatten
402
+ end
403
+
404
+
314
405
  # TODO: Make better errors
315
406
  # TODO: This might not work well with conflicting versions
316
407
  def local_deps(search_path=nil)
@@ -395,15 +486,26 @@ module BPM
395
486
  end
396
487
 
397
488
  def validate_summary
398
- summary || description
489
+ if (summary.nil? || summary.empty?) && (description.nil? || description.empty?)
490
+ add_error "Package requires a 'summary' field"
491
+ add_error "Package requires a 'description' field"
492
+ false
493
+ else
494
+ true
495
+ end
399
496
  end
400
497
 
401
498
  def validate_homepage
402
- homepage || url
499
+ if (homepage.nil? || homepage.empty?) && (url.nil? || url.empty?)
500
+ add_error "Package requires a 'homepage' field"
501
+ false
502
+ else
503
+ true
504
+ end
403
505
  end
404
506
 
405
507
  def add_error(message)
406
- self.errors << message
508
+ self.errors << message unless self.errors.include?(message)
407
509
  end
408
510
 
409
511
  def glob_files(path)
@@ -0,0 +1,28 @@
1
+ require 'sprockets'
2
+
3
+ module BPM
4
+
5
+ # A Template that will use a format plugin to compile the content
6
+ # Register a subclass of the template with the with_plugin
7
+ class FormatProcessor < BPM::PluginProcessor
8
+
9
+ def self.with_plugin(ext, plugin_opts)
10
+ ret = super plugin_opts, 'compileFormat'
11
+ ret.instance_eval do
12
+ @extension = ext
13
+ end
14
+ ret
15
+ end
16
+
17
+ def self.extension
18
+ @extension
19
+ end
20
+
21
+ def self.default_mime_type
22
+ @plugin_opts["mime:default"]
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+
@@ -74,9 +74,9 @@ module BPM
74
74
  private
75
75
 
76
76
  def build_source
77
- self.class.push_generating_asset self
77
+ BPM::GeneratedAsset.push_generating_asset self
78
78
  ret = minify super
79
- self.class.pop_generating_asset
79
+ BPM::GeneratedAsset.pop_generating_asset
80
80
  ret
81
81
  end
82
82
 
@@ -0,0 +1,79 @@
1
+ require 'sprockets'
2
+
3
+ module BPM
4
+
5
+ # A sub-environment created for each package in the project. Requests for
6
+ # individual assets will usually end up going through one of these
7
+ # instances. This allows each package to have its own set of processors
8
+ # for each file.
9
+ class PackagePipeline < Sprockets::Environment
10
+
11
+ attr_reader :pipeline, :package
12
+
13
+ def shell
14
+ @shell ||= Thor::Base.shell.new
15
+ end
16
+
17
+ def initialize(pipeline, package)
18
+ @pipeline = pipeline
19
+ @package = package
20
+
21
+ super package.root_path
22
+
23
+ %w(text/css application/javascript).each do |kind|
24
+ unregister_processor kind, Sprockets::DirectiveProcessor
25
+ register_processor kind, BPM::DirectiveProcessor
26
+ end
27
+
28
+ # This gunks things up. I'm not a fan - PDW
29
+ unregister_postprocessor 'application/javascript', Sprockets::SafetyColons
30
+
31
+ package.used_formats(project).each do |ext, opts|
32
+ register_engine ".#{ext}", BPM::FormatProcessor.with_plugin(ext,opts)
33
+ end
34
+
35
+ package.used_preprocessors(project).each do |opts|
36
+ register_preprocessor opts['mime'],
37
+ BPM::PluginProcessor.with_plugin(opts, 'preprocess')
38
+ end
39
+
40
+ package.used_postprocessors(project).each do |opts|
41
+ register_postprocessor opts['mime'],
42
+ BPM::PluginProcessor.with_plugin(opts, 'postprocess')
43
+ end
44
+
45
+ opts = package.used_transports(project)
46
+ raise TooManyTransportsError(package) if opts.size>1
47
+ if opts.size>0
48
+ register_postprocessor 'application/javascript',
49
+ BPM::PluginProcessor.with_plugin(opts.first, 'compileTransport')
50
+ end
51
+
52
+ append_path package.root_path
53
+ end
54
+
55
+ def package_name
56
+ package.name
57
+ end
58
+
59
+ def project
60
+ pipeline.project
61
+ end
62
+
63
+ def mode
64
+ pipeline.mode
65
+ end
66
+
67
+ def plugin_context_for(logical_path)
68
+ pipeline.plugin_context_for logical_path
69
+ end
70
+
71
+ def resolve(*args)
72
+ super *args
73
+ rescue Sprockets::FileNotFound => e
74
+ raise Sprockets::FileNotFound, "#{e.message} in package '#{package_name}'"
75
+ end
76
+
77
+
78
+ end
79
+ end
@@ -5,7 +5,7 @@ module BPM
5
5
  # Defines an asset that represents a build plugin (such as a transport or
6
6
  # a minifier.) The generated asset will include dependencies of the plugin
7
7
  # module as well as the module itself.
8
- class PluginAsset < Sprockets::BundledAsset
8
+ class PluginAsset < BPM::GeneratedAsset
9
9
 
10
10
  def initialize(environment, module_name)
11
11
  pathname = Pathname.new(File.join(environment.project.root_path, '.bpm', 'plugins', module_name+'.js'))
@@ -18,15 +18,14 @@ module BPM
18
18
  super(environment, module_name, pathname, {})
19
19
  end
20
20
 
21
- protected
22
-
23
- def dependency_context_and_body
24
- @dependency_context_and_body ||= build_dependency_context_and_body
25
- end
26
-
27
21
  private
28
22
 
29
- # Note: logical path must be hte module
23
+ # do not minify plugin assets
24
+ def minify(hash)
25
+ hash
26
+ end
27
+
28
+ # Note: logical path must be the module
30
29
  def plugin_module
31
30
  project = environment.project
32
31
  parts = logical_path.split('/')
@@ -0,0 +1,52 @@
1
+ require 'sprockets'
2
+
3
+ module BPM
4
+
5
+ # A processor that will invoke a JavaScript-based plugin provided by a
6
+ # package in the system. The passed method name will be invoked on the
7
+ # plugin.
8
+ class PluginProcessor < Tilt::Template
9
+
10
+ def self.with_plugin(plugin_opts, method_name)
11
+ ret = Class.new(self)
12
+ ret.instance_eval do
13
+ @method_name = method_name
14
+ @plugin_opts = plugin_opts
15
+ end
16
+ ret
17
+ end
18
+
19
+ def self.method_name
20
+ @method_name
21
+ end
22
+
23
+ def self.plugin_name
24
+ @plugin_opts["main"]
25
+ end
26
+
27
+ def prepare
28
+ end
29
+
30
+ def evaluate(context, locals, &block)
31
+ ctx = context.environment.plugin_context_for self.class.plugin_name
32
+ project = context.environment.project
33
+ pkg, module_id = project.package_and_module_from_path file
34
+
35
+ filepath = file.to_s
36
+ out = ''
37
+
38
+ V8::C::Locker() do
39
+ ctx["DATA"] = data
40
+ ctx["CTX"] = BPM::PluginContext.new(pkg, module_id)
41
+ out = ctx.eval("BPM_PLUGIN.#{self.class.method_name}(DATA, CTX, '#{filepath}');")
42
+ end
43
+
44
+ out
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+
51
+
52
+
data/lib/bpm/pipeline.rb CHANGED
@@ -5,8 +5,15 @@ module BPM
5
5
 
6
6
  class Console
7
7
  def log(str)
8
- puts str
8
+ shell.say str
9
9
  end
10
+
11
+ private
12
+
13
+ def shell
14
+ @shell ||= Thor::Base.shell.new
15
+ end
16
+
10
17
  end
11
18
 
12
19
  # A BPM package-aware asset pipeline. Asset lookup respects package.json
@@ -17,6 +24,7 @@ module BPM
17
24
 
18
25
  attr_reader :project
19
26
  attr_reader :mode
27
+ attr_reader :package_pipelines
20
28
 
21
29
  # Pass in the project you want the pipeline to manage.
22
30
  def initialize(project, mode = :debug, include_preview = false)
@@ -24,34 +32,84 @@ module BPM
24
32
  @project = project
25
33
  @mode = mode
26
34
  @plugin_contexts = {}
35
+
36
+ # Create a pipeline for each package. Will be used for searching.
37
+ @package_pipelines = project.local_deps.map do |pkg|
38
+ BPM::PackagePipeline.new self, pkg
39
+ end
40
+ @package_pipelines << BPM::PackagePipeline.new(self, project)
27
41
 
28
42
  project_path = project.root_path
29
43
 
30
44
  super project_path
31
45
 
32
- # use custom directive processor
46
+ # Unregister built-in processors. We want most things served by the
47
+ # pipeline directly to just pass through. (package pipelines do the
48
+ # processing)
33
49
  %w(text/css application/javascript).each do |kind|
34
50
  unregister_processor kind, Sprockets::DirectiveProcessor
35
- register_processor kind, BPM::DirectiveProcessor
36
51
  end
37
-
38
- register_postprocessor 'application/javascript', BPM::TransportProcessor
39
- #register_postprocessor 'application/javascript', BPM::SourceURLProcessor
40
-
41
- # This gunks things up. I'm not a fan - PDW
42
52
  unregister_postprocessor 'application/javascript', Sprockets::SafetyColons
43
53
 
44
54
  # configure search paths
45
- append_path File.join project_path, '.bpm', 'packages'
46
- append_path File.dirname project_path
47
55
  append_path project.assets_root
48
56
  append_path project.preview_root if include_preview
49
57
  end
50
-
51
- def plugin_context_for(module_id)
52
- @plugin_contexts[module_id] ||= build_plugin_context(module_id)
58
+
59
+ # determines the proper pipeline for the path
60
+ def pipeline_for(path)
61
+ return nil if magic_paths.include?(path)
62
+ package_pipelines.find do |cur_pipeline|
63
+ path.to_s[cur_pipeline.package.root_path.to_s]
64
+ end
53
65
  end
54
66
 
67
+ def attributes_for(path)
68
+ if path.to_s[File.join(project.root_path, '.bpm')] || !Pathname.new(path).absolute?
69
+ return super(path)
70
+ end
71
+
72
+ pipeline = pipeline_for path
73
+ pipeline ? pipeline.attributes_for(path) : super(path)
74
+ end
75
+
76
+ def resolve(logical_path, options={}, &block)
77
+
78
+ magic_path = magic_paths.find do |path|
79
+ path =~ /#{Regexp.escape logical_path.to_s}(\..+)?$/
80
+ end
81
+
82
+ package_name = logical_path.to_s.sub(/#{Regexp.escape File::SEPARATOR}.+/,'')
83
+ pipeline = package_pipelines.find do |cur_pipeline|
84
+ cur_pipeline.package_name == package_name
85
+ end
86
+
87
+ if pipeline && magic_path.nil?
88
+ logical_path = logical_path.to_s[package_name.size+1..-1]
89
+ pipeline.resolve Pathname.new(logical_path), options, &block
90
+ else
91
+ super logical_path, options, &block
92
+ end
93
+
94
+ end
95
+
96
+ # Detect whenever we are asked to build some of the magic files and swap
97
+ # in a custom asset type that can generate the contents.
98
+ def build_asset(logical_path, pathname, options)
99
+ if magic_paths.include? pathname.to_s
100
+ BPM::GeneratedAsset.new(self, logical_path, pathname, options)
101
+ elsif pipeline = pipeline_for(pathname)
102
+ pipeline.build_asset logical_path, pathname, options
103
+ else
104
+ super logical_path, pathname, options
105
+ end
106
+ end
107
+
108
+ # Paths to files that should be built.
109
+ def magic_paths
110
+ @magic_paths ||= build_magic_paths
111
+ end
112
+
55
113
  # Returns an array of all the buildable assets in the current directory.
56
114
  # These are the assets that will be built when you compile the project.
57
115
  def buildable_assets
@@ -85,29 +143,34 @@ module BPM
85
143
 
86
144
  ret.sort.map { |x| find_asset x }.compact
87
145
  end
146
+
147
+ def plugin_context_for(logical_path)
148
+ @plugin_contexts[logical_path] ||= build_plugin_context(logical_path)
149
+ end
88
150
 
89
- # Detect whenever we are asked to build some of the magic files and swap
90
- # in a custom asset type that can generate the contents.
91
- def build_asset(logical_path, pathname, options)
151
+ protected
152
+
153
+ def build_magic_paths
92
154
  magic_paths = project.buildable_asset_filenames(mode).map do |filename|
93
155
  project.assets_root filename
94
156
  end
95
-
157
+
96
158
  magic_paths += project.buildable_asset_filenames(mode).map do |filename|
97
159
  project.preview_root filename
98
160
  end
99
-
100
- if magic_paths.include? pathname.to_s
101
- BPM::GeneratedAsset.new(self, logical_path, pathname, options)
102
- else
103
- super logical_path, pathname, options
104
- end
105
161
  end
106
-
162
+
163
+ # Pass along to package pipelines
164
+ def expire_index!
165
+ super
166
+ @magic_paths = nil
167
+ package_pipelines.each { |pipeline| pipeline.expire_index! }
168
+ end
169
+
107
170
  private
108
171
 
109
- def build_plugin_context(module_id)
110
- asset = BPM::PluginAsset.new(self, module_id)
172
+ def build_plugin_context(logical_path)
173
+ asset = BPM::PluginAsset.new(self, logical_path)
111
174
  plugin_text = asset.to_s
112
175
 
113
176
  ctx = nil