Class: Distil::Project

Inherits:
Configurable show all
Includes:
ErrorReporter, FileVendor, JavascriptFileValidator
Defined in:
lib/distil/project.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from JavascriptFileValidator

#validate_javascript_files

Methods included from ErrorReporter

#error, error, #has_errors?, #has_warnings?, #ignore_warnings, #ignore_warnings=, #report, #total_error_count, #total_warning_count, warning, #warning

Methods included from FileVendor

#cache_file, #file_from_path

Methods inherited from Configurable

alias_config_key, #configure_with, #key_for_alias

Constructor Details

#initialize(path, config = {}, parent = nil) ⇒ Project

Returns a new instance of Project.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/distil/project.rb', line 67

def initialize(path, config={}, parent=nil)
  @path= path
  @parent= parent
  @folder= File.dirname(@path)
  @source_path= File.expand_path(@folder)
  @output_path= File.join(@folder, DEFAULT_OUTPUT_FOLDER)
  @doc_output_path= File.join(@folder, DEFAULT_DOC_OUTPUT_FOLDER)
  @include_paths= [@folder]
  @include_files= []
  @asset_aliases= {}
  @dependency_aliases= {}
  @assets= Set.new
  @source_files= Set.new
  @subprojects= []
  @libraries= parent ? parent.libraries : []
  @libraries_by_name= parent ? parent.libraries_by_name : {}
  @languages= []
  @additional_globals= []
  @name= File.basename(@folder, ".*")
  @raw_sources = []
  @synchronise_assets = false
  
  ignore_warnings= false

  child_config= config.dup
  child_config.delete("targets")
  child_config.delete("require")
  
  Dir.chdir(@folder) do
    configure_with config do |c|

      c.with :name do |name|
        @name= name
      end
      
      c.with :output_folder do |output_folder|
        self.output_path= File.expand_path(output_folder)
      end
      
      c.with :doc_folder do |doc_folder|
        @doc_output_path= File.expand_path(doc_folder)
      end
      
      c.with :source_folder do |source_folder|
        @source_path= File.expand_path(source_folder)
      end

      c.with :export do |export|
        export=@name.as_identifier if true==export
        @global_export= export
      end
      
      c.with_each :globals do |global|
        @additional_globals << global
      end
      
      c.with_each :languages do |language|
        @languages << language
      end
      
      c.with_each :require do |library|
        library= Library.new(library, self)
        @libraries << library
        @libraries_by_name[library.name]= library
      end

      c.with :source do |source_files|
        @raw_sources= source_files
      end
    
      c.with_each :targets do |target|
        target_config= child_config.dup
        target_config.deep_merge!(target)
        @subprojects << Project.new(path, target_config, self)
      end
      
    end # configure_with
  end

end

Instance Attribute Details

#additional_globalsObject (readonly)

Returns the value of attribute additional_globals.



24
25
26
# File 'lib/distil/project.rb', line 24

def additional_globals
  @additional_globals
end

#asset_aliasesObject (readonly)

Returns the value of attribute asset_aliases.



18
19
20
# File 'lib/distil/project.rb', line 18

def asset_aliases
  @asset_aliases
end

#assetsObject (readonly)

Returns the value of attribute assets.



18
19
20
# File 'lib/distil/project.rb', line 18

def assets
  @assets
end

#dependency_aliasesObject (readonly)

Returns the value of attribute dependency_aliases.



26
27
28
# File 'lib/distil/project.rb', line 26

def dependency_aliases
  @dependency_aliases
end

#doc_output_pathObject (readonly)

Returns the value of attribute doc_output_path.



22
23
24
# File 'lib/distil/project.rb', line 22

def doc_output_path
  @doc_output_path
end

#doc_src_pathObject (readonly)

Returns the value of attribute doc_src_path.



27
28
29
# File 'lib/distil/project.rb', line 27

def doc_src_path
  @doc_src_path
end

#folderObject (readonly)

Returns the value of attribute folder.



17
18
19
# File 'lib/distil/project.rb', line 17

def folder
  @folder
end

#global_exportObject (readonly)

Returns the value of attribute global_export.



23
24
25
# File 'lib/distil/project.rb', line 23

def global_export
  @global_export
end

#include_pathsObject (readonly)

Returns the value of attribute include_paths.



17
18
19
# File 'lib/distil/project.rb', line 17

def include_paths
  @include_paths
end

#languagesObject (readonly)

Returns the value of attribute languages.



20
21
22
# File 'lib/distil/project.rb', line 20

def languages
  @languages
end

#librariesObject (readonly)

Returns the value of attribute libraries.



19
20
21
# File 'lib/distil/project.rb', line 19

def libraries
  @libraries
end

#libraries_by_nameObject (readonly)

Returns the value of attribute libraries_by_name.



19
20
21
# File 'lib/distil/project.rb', line 19

def libraries_by_name
  @libraries_by_name
end

#nameObject (readonly)

Returns the value of attribute name.



17
18
19
# File 'lib/distil/project.rb', line 17

def name
  @name
end

#output_pathObject

Returns the value of attribute output_path.



17
18
19
# File 'lib/distil/project.rb', line 17

def output_path
  @output_path
end

#pathObject (readonly)

Returns the value of attribute path.



17
18
19
# File 'lib/distil/project.rb', line 17

def path
  @path
end

#project_typeObject (readonly)

Returns the value of attribute project_type.



20
21
22
# File 'lib/distil/project.rb', line 20

def project_type
  @project_type
end

#source_filesObject (readonly)

Returns the value of attribute source_files.



21
22
23
# File 'lib/distil/project.rb', line 21

def source_files
  @source_files
end

#source_pathObject (readonly)

Returns the value of attribute source_path.



17
18
19
# File 'lib/distil/project.rb', line 17

def source_path
  @source_path
end

#subprojectsObject (readonly)

Returns the value of attribute subprojects.



25
26
27
# File 'lib/distil/project.rb', line 25

def subprojects
  @subprojects
end

#synchronise_assetsObject (readonly)

Returns the value of attribute synchronise_assets.



29
30
31
# File 'lib/distil/project.rb', line 29

def synchronise_assets
  @synchronise_assets
end

Class Method Details

.find(dir = nil) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/distil/project.rb', line 35

def self.find(dir=nil)
  cwd= Dir.pwd
  dir ||= Dir.pwd
  while dir.length > 1
    return from_file(File.join(dir, BUILD_FILE)) if File.exists?(File.join(dir, BUILD_FILE))
    
    projects= Dir.glob(File.join(dir, "*.jsproj"))
    return from_file(projects.first) if 1==projects.size
    
    unless 0==projects.size
      puts "More than one candidate for Project:"
      projects.each { |e|
        puts "  #{path_relative_to_folder(e, cwd)}"
      }
      exit 1
    end
    
    dir= File.dirname(dir)
  end
  
  nil
end

.from_file(file) ⇒ Object



58
59
60
61
62
63
64
65
# File 'lib/distil/project.rb', line 58

def self.from_file(file)
  yaml= YAML::load_file(file)
  if File.exists?("#{file}.local")
    local_yaml= YAML::load_file("#{file}.local")
    yaml.deep_merge!(local_yaml)
  end
  new(file, yaml)
end

.path_relative_to_folder(path, folder) ⇒ Object



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/distil/project.rb', line 442

def self.path_relative_to_folder(path, folder)
  path= File.expand_path(path)
  outputFolder= File.expand_path(folder).to_s
  
  # Remove leading slash and split into parts
  file_parts= path.slice(1..-1).split('/');
  output_parts= outputFolder.slice(1..-1).split('/');

  common_prefix_length= 0

  file_parts.each_index { |i|
    common_prefix_length= i
    break if file_parts[i]!=output_parts[i]
  }

  return '../'*(output_parts.length-common_prefix_length) + file_parts[common_prefix_length..-1].join('/')
end

Instance Method Details

#add_alias_for_asset(alias_name, asset) ⇒ Object



349
350
351
352
353
354
355
# File 'lib/distil/project.rb', line 349

def add_alias_for_asset(alias_name, asset)
  if asset_aliases.include?(alias_name)
    error "Attempt to register asset with the same alias as another asset: #{alias_name}"
    return
  end
  asset_aliases[asset]= alias_name
end

#add_alias_for_file(alias_name, file) ⇒ Object



414
415
416
# File 'lib/distil/project.rb', line 414

def add_alias_for_file(alias_name, file)
  @dependency_aliases[alias_name]= file
end

#buildObject

Raises:



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/distil/project.rb', line 202

def build
  FileUtils.mkdir_p(output_path)
  
  if @parent.nil?
    libraries.each { |lib|
      lib.build
    }
  end
  
  puts "\n#{name}:\n\n" unless @parent

  failed = false
  
  @subprojects.each { |subproject|
    begin
      subproject.build
    rescue BuildFailure
      failed = true
    end
  }

  # Don't keep going if one of the sub-projects failed.
  raise BuildFailure if failed
  
  compute_source_files
  return if up_to_date?
  
  validate_files
  build_assets
  
  products.each { |product|
    product.build
  }
end

#build_assetsObject



335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/distil/project.rb', line 335

def build_assets
  symlink_assets
  
  if !@parent
    FileUtils.cp(File.join(ASSETS_DIR, 'distil.js'), output_path)
  end
  
  # if (RELEASE_MODE==mode)
  #   copy_assets
  # else
  #   symlink_assets
  # end
end

#cleanObject



237
238
239
240
241
242
# File 'lib/distil/project.rb', line 237

def clean
  compute_source_files
  products.each { |product|
    product.clean
  }
end

#compute_source_filesObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/distil/project.rb', line 152

def compute_source_files
  return if @source_files_computed
  @source_files_computed= true
  
  @raw_sources.each { |f|
    include_file(f)
  }
  
  inspected= Set.new
  ordered_files= []

  add_file= lambda { |f|
    return unless include_files.include?(f)
    return if inspected.include?(f)
    inspected << f
    
    if f.respond_to? :dependencies
      f.dependencies.each { |d|
        add_file.call d
      }
    end
    
    ordered_files << f
  }

  include_files.each { |f| add_file.call(f) }
  ordered_files.each { |f|
    next if f.is_a?(SourceFile) && f.is_asset
    
    used= false
    products.each { |p|
      used= true if p.include_file(f)
    }
    
    next if !used
    
    if !f.is_a?(Library)
      @source_files << f
      @assets.merge(f.assets) if f.assets
    end
  }
end

#copy_assetsObject



329
330
331
332
333
# File 'lib/distil/project.rb', line 329

def copy_assets
  assets.each { |a|
    a.copy_to(output_path, source_path)
  }
end

#find_file(path, content_type = nil, mode = nil) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/distil/project.rb', line 418

def find_file(path, content_type=nil, mode=nil)
  return path if File.exists?(path)
  
  include_paths.each { |i|
    f= File.join(i, path)
    return f if File.exists?(f)
  }
  
  # Check remote assets
  parts= path.split(File::SEPARATOR)
  asset_name= parts[0]
  file_path= File.join(parts.slice(1..-1))

  return nil unless @libraries_by_name.include?(asset_name)
  asset= @libraries_by_name[asset_name]
  
  return asset.file_for(content_type, nil, mode) if 1==parts.length
  
  f= File.join(asset.output_path, file_path)
  return f if File.exists?(f)
  
  nil
end

#glob(path) ⇒ Object



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/distil/project.rb', line 392

def glob(path)
  return path if File.exists?(path)
  
  files= []
  
  parts= path.split(File::SEPARATOR)
  library_name= parts[0]
  file_path= File.join(parts.slice(1..-1))

  if (@libraries_by_name.include?(library_name))
    library= @libraries_by_name[library_name]
    return Dir.glob(File.join(library.output_path, file_path))
  end
  
  files.concat(Dir.glob(path));
  
  include_paths.each { |i|
    files.concat(Dir.glob(File.join(i, path)))
  }
  return files
end

#include_file(file) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/distil/project.rb', line 361

def include_file(file)
  return if file.nil?
  
  asset= @libraries_by_name[file]
  if (asset)
    @include_files << asset unless @include_files.include?(asset)
    return
  end
  
  matches= glob(file)
  matches= glob(File.join(source_path, file)) if matches.empty?
  
  if (matches.empty?)
    error("No matching files found for: #{file}")
    return
  end
  
  matches.each { |m|
    if File.directory?(m)
      include_file(File.join(m, "**/*"))
    else
      f= file_from_path(m)
      unless @include_files.include?(f)
        @include_files << f
        # determine language
        f.language= languages.find { |l| File.fnmatch?("**/#{l}/**", f.full_path) }
      end
    end
  }
end

#include_filesObject



357
358
359
# File 'lib/distil/project.rb', line 357

def include_files
  @include_files
end

#inspectObject



244
245
246
# File 'lib/distil/project.rb', line 244

def inspect
  "<#{self.class}:0x#{object_id.to_s(16)} name=#{name}>"
end

#notice_textObject



270
271
272
273
274
275
276
# File 'lib/distil/project.rb', line 270

def notice_text
  begin
    @notice_text ||= File.read(File.join(@folder, @notice)).strip
  rescue
    @notice_text ||= ""
  end
end

#productsObject



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/distil/project.rb', line 278

def products
  return @products unless @products.nil?
  
  @products= []
  langs= languages.empty? ? [nil] : languages
  
  Product.subclasses.each { |klass|
    langs.each { |lang|
      klass.variants.each { |v|
        @products << klass.new(self, lang, v)
      }
    }
  }
  
  @products
end

#relative_output_path_for(thing) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/distil/project.rb', line 257

def relative_output_path_for(thing)
  return nil if !thing
  thing_path= thing.is_a?(String) ? thing : thing.output_path
  # puts "relative_output_path_for:"
  # puts "  output_path= #{thing_path}"
  # unless thing.is_a?(String)
  #   puts "  full_path= #{thing.full_path}"
  #   puts "  relative_path= #{thing.relative_path}"
  #   puts "  source_path= #{source_path}"
  # end
  Project.path_relative_to_folder(thing_path, output_path)
end

#relative_path_for(thing) ⇒ Object



253
254
255
# File 'lib/distil/project.rb', line 253

def relative_path_for(thing)
  Project.path_relative_to_folder(thing.is_a?(String) ? thing : thing.full_path, path)
end


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/distil/project.rb', line 295

def symlink_assets
  Dir.chdir(output_path) do
    folders= []

    files= assets+source_files
    files.each { |a|
      next if (a.full_path).starts_with?(output_path)

      path= relative_output_path_for(a)
      
      parts= File.dirname(path).split(File::SEPARATOR)
      if ('.'==parts[0])
        source_path= a.path_relative_to(output_path)
        product_path= File.join(output_path, path)
        file_path= a.path_relative_to(source_path)
        FileUtils.rm product_path if File.exists? product_path
        File.symlink file_path, product_path
        next
      end

      folders << parts[0] if !folders.include?(parts[0])
    }

    folders.each { |f|
      target= f
      source= relative_output_path_for(File.join(source_path, f))

      FileUtils.rm target if File.symlink?(target)
      next if File.directory?(target)
      File.symlink source, target
    }
  end
end

#up_to_date?Boolean

Returns:

  • (Boolean)


195
196
197
198
199
200
# File 'lib/distil/project.rb', line 195

def up_to_date?
  products.each { |product|
    return false if !product.up_to_date?
  }
  return true
end

#validate_filesObject



148
149
150
# File 'lib/distil/project.rb', line 148

def validate_files
  validate_javascript_files
end