bpm 1.0.0.beta.6 → 1.0.0.beta.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.
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