Class: QED::Command

Inherits:
Object show all
Defined in:
lib/qed/command.rb

Overview

QED Command-line tool.

TODO: Merge Command with Session ?

Constant Summary collapse

CONFIG_PATTERN =

Configuration directory ‘.qed`, `.config/qed` or `config/qed`. In this directory special configuration files can be placed to autmatically effect qed execution. In particular you can add a `profiles.yml` file to setup convenient execution scenarios.

"{.,.config/,config/}qed"
ROOT_PATTERN =

Glob pattern used to search for project’s root directory.

'{.ruby,.git/,.hg/,_darcs/,.qed/,.config/qed/,config/qed/}'
OMIT_PATHS =

Directory names to omit from automatic selection.

%w{applique helpers support sample samples fixture fixtures}
HOME =

Home directory.

File.expand_path('~')
DEMO_TYPES =

Default recognized demos file types.

%w{qed rdoc md markdown}
CODE_TYPES =
%w{rb}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCommand

TODO: Should extension and profile have a common reference?



94
95
96
97
98
99
100
101
102
103
104
# File 'lib/qed/command.rb', line 94

def initialize
  @format    = :dotprogress
  #@extension = :default
  @profile   = :default
  @requires  = []
  @loadpath  = []
  @files     = []
  #@options   = {}

  @omit      = OMIT_PATHS
end

Instance Attribute Details

#filesObject

Files to be run.



70
71
72
# File 'lib/qed/command.rb', line 70

def files
  @files
end

#formatObject

Ouput format.



53
54
55
# File 'lib/qed/command.rb', line 53

def format
  @format
end

#loadpathObject

Paths to be added to $LOAD_PATH.



78
79
80
# File 'lib/qed/command.rb', line 78

def loadpath
  @loadpath
end

#modeObject

Parse mode.



87
88
89
# File 'lib/qed/command.rb', line 87

def mode
  @mode
end

#omitObject

Returns the value of attribute omit.



90
91
92
# File 'lib/qed/command.rb', line 90

def omit
  @omit
end

#profileObject (readonly)

Options defined by selected profile.



64
65
66
# File 'lib/qed/command.rb', line 64

def profile
  @profile
end

#requiresObject

Libraries to be required.



81
82
83
# File 'lib/qed/command.rb', line 81

def requires
  @requires
end

#rootObject

Move to root directory?



84
85
86
# File 'lib/qed/command.rb', line 84

def root
  @root
end

#traceObject (readonly)

Trace execution?



61
62
63
# File 'lib/qed/command.rb', line 61

def trace
  @trace
end

Class Method Details

.main(*argv) ⇒ Object

Instantiate a new Command object and call #execute.



48
49
50
# File 'lib/qed/command.rb', line 48

def self.main(*argv)
  new.execute(argv)
end

Instance Method Details

#config_directoryObject

Project’s QED configuation directory.



277
278
279
# File 'lib/qed/command.rb', line 277

def config_directory
  @config_directory ||= find_config #Dir[File.join(root_directory, CONFIG_PATTERN)].first
end

#demosObject

Returns a list of demo files. The files returned depends on the files attribute and if none given, then the current run mode.



180
181
182
183
184
185
186
187
188
# File 'lib/qed/command.rb', line 180

def demos
  @demos ||= (
    if mode == :comment
      demos_in_comment_mode
    else
      demos_in_normal_mode
    end
  )
end

#demos_gather(extensions = DEMO_TYPES) ⇒ Object

Gather list of demo files. Uses omit to remove certain files based on the name of their parent directory.



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/qed/command.rb', line 206

def demos_gather(extensions=DEMO_TYPES)
  files = self.files
  #files << default_location if files.empty?
  files = files.map{|pattern| Dir[pattern]}.flatten.uniq
  files = files.map do |file|
    if File.directory?(file)
      Dir[File.join(file,'**','*.{' + extensions.join(',') + '}')]
    else
      file
    end
  end
  files = files.flatten.uniq
  files = files.reject{ |f| f =~ Regexp.new('\/'+omit.join('|')+'\/') }
  files.map{|f| File.expand_path(f) }.uniq.sort
end

#demos_in_comment_modeObject

Collect default files to process in code comment mode.

TODO: Sure removing applique files is the best approach here?



198
199
200
201
202
# File 'lib/qed/command.rb', line 198

def demos_in_comment_mode
  files = demos_gather(CODE_TYPES)
  files = files.reject{ |f| f.index('applique/') }  # don't include applique files ???
  files
end

#demos_in_normal_modeObject

Collect default files to process in normal demo mode.



191
192
193
# File 'lib/qed/command.rb', line 191

def demos_in_normal_mode
  demos_gather(DEMO_TYPES)
end

#execute(argv) ⇒ Object

Run demonstrations.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/qed/command.rb', line 249

def execute(argv)
  parse(argv)

  abort "No documents." if demos.empty?

  prepare_loadpath
  require_libraries

  require_profile  # TODO: here or in chdir?

  jump = root || temporary_directory

  Dir.chdir(jump) do
    session.run
  end
end

#find_configObject

Locate configuration directory by seaching up the file hierachy relative to the working directory for one of the following paths:

  • .config/qed/

  • config/qed/

  • .qed/



368
369
370
# File 'lib/qed/command.rb', line 368

def find_config
  Dir[File.join(root_directory,CONFIG_PATTERN)].first
end

#find_root(path = nil) ⇒ Object

Locate project’s root directory. This is done by searching upward in the file heirarchy for the existence of one of the following path names, each group being tried in turn.

  • .git/

  • .hg/

  • _darcs/

  • .config/qed/

  • config/qed/

  • .qed/

  • .ruby

Failing to find any of these locations, resort to the fallback:

  • lib/



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/qed/command.rb', line 334

def find_root(path=nil)
  path = File.expand_path(path || Dir.pwd)
  path = File.dirname(path) unless File.directory?(path)

  root = lookup(ROOT_PATTERN, path)
  return root if root

  #root = lookup(path, '{.qed,.config/qed,config/qed}/')
  #return root if root

  #root = lookup(path, '{qed,demo,demos}/')
  #return root if root

  root = lookup('lib/', path)
  return root if root

  abort "QED failed to resolve project's root location.\n" +
        "QED looks for following entries to identify the root:\n" +
        "  .config/qed/\n" +
        "  config/qed/\n" +
        "  .qed/\n" +
        "  .ruby\n" +
        "  lib/\n" +
        "Please add one of them to your project to proceed."
end

#lookup(glob, path = Dir.pwd) ⇒ Object

Lookup path glob, searching each higher directory in turn until just before the users home directory is reached or just before the system’s root directory.

TODO: include HOME directory in search?



377
378
379
380
381
382
383
# File 'lib/qed/command.rb', line 377

def lookup(glob, path=Dir.pwd)
  until path == HOME or path == '/' # until home or root
    mark = Dir.glob(File.join(path,glob), File::FNM_CASEFOLD).first
    return path if mark
    path = File.dirname(path)
  end
end

#optsObject

Instance of OptionParser



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
147
148
149
150
151
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
# File 'lib/qed/command.rb', line 107

def opts
  @opts ||= OptionParser.new do |opt|
    opt.banner = "Usage: qed [options] <files...>"

    opt.separator("Custom Profiles:") unless profiles.empty?

    profiles.each do |name, value|
      o = "--#{name}"
      opt.on(o, "#{name} custom profile") do
        self.profile = name
      end
    end

    opt.separator("Report Formats (pick one):")
    opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do
      self.format = :dotprogress
    end
    opt.on('--verbatim', '-v', "use verbatim reporter") do
      self.format = :verbatim
    end
    opt.on('--bullet', '-b', "use bullet-point reporter") do
      self.format = :bullet
    end
    opt.on('--html', '-h', "use underlying HTML reporter") do
      self.format = :html
    end
    #opt.on('--script', "psuedo-reporter") do
    #  self.format = :script  # psuedo-reporter
    #end
    opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
      self.format = format
    end
    opt.separator("Control Options:")
    opt.on('--root', '-R', "run from alternate directory") do |path|
      self.root = path
    end
    opt.on('--comment', '-c', "Run comment code.") do
      self.mode = :comment
    end
    opt.on('--profile', '-p NAME', "load runtime profile") do |name|
      self.profile = name
    end
    opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |paths|
      self.loadpath = paths.split(/[:;]/).map{|d| File.expand_path(d)}
    end
    opt.on('--require', "-r LIB", "require library") do |paths|
      self.requires = paths.split(/[:;]/)
    end
    opt.on('--trace', '-t', "show full backtraces for exceptions") do
      self.trace = true
    end
    opt.on('--debug', "exit immediately upon raised exception") do
      $VERBOSE = true # wish this were called $WARN
      $DEBUG   = true
    end
    opt.separator("Optional Commands:")
    opt.on_tail('--version', "display version") do
      puts "QED #{VERSION}"
      exit
    end
    opt.on_tail('--copyright', "display copyrights") do
      puts "Copyright (c) 2008 Thomas Sawyer, Apache 2.0 License"
      exit
    end
    opt.on_tail('--help', '-h', "display this help message") do
      puts opt
      exit
    end
  end
end

#parse(argv) ⇒ Object

Parse command-line options along with profile options.



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/qed/command.rb', line 223

def parse(argv)
  #@files = []
  opts.parse!(argv ||= ARGV.dup)
  #@files.concat(argv)
  @files = argv

  if @files.empty?
    puts "No files."
    puts opts
    exit
  end

  #if profile
  #if args = profiles[profile]
  #  argv = Shellwords.shellwords(args)
  #  opts.parse!(argv)
  #  @files.concat(argv)
  #end
  #end

  #options.each do |k,v|
  #  __send__("#{k}=", v)
  #end
end

#prepare_loadpathObject

Add to load path (from -I option).



292
293
294
# File 'lib/qed/command.rb', line 292

def prepare_loadpath
  loadpath.each{ |dir| $LOAD_PATH.unshift(dir) }
end

#profilesObject

Profile configurations.



282
283
284
285
286
287
288
289
# File 'lib/qed/command.rb', line 282

def profiles
  @profiles ||= (
    files = Dir["#{config_directory}/*.rb"]
    files.map do |file|
      File.basename(file).chomp('.rb')
    end
  )
end

#require_librariesObject

Require libraries (from -r option).



297
298
299
# File 'lib/qed/command.rb', line 297

def require_libraries
  requires.each{ |file| require(file) }
end

#require_profileObject

Require requirement file (from -e option).



302
303
304
305
306
307
# File 'lib/qed/command.rb', line 302

def require_profile
  return unless config_directory
  if file = Dir["#{config_directory}/#{profile}.rb"].first
    require(file)
  end
end

#root_directoryObject

Project’s root directory.



272
273
274
# File 'lib/qed/command.rb', line 272

def root_directory
  @root_directory ||= find_root
end

#sessionObject

Session instance.



267
268
269
# File 'lib/qed/command.rb', line 267

def session
  @session ||= Session.new(demos, :format=>format, :trace=>trace, :mode=>mode)
end

#temporary_directoryObject



310
311
312
313
314
315
316
# File 'lib/qed/command.rb', line 310

def temporary_directory
  @temporary_directory ||= (
    dir = File.join(root_directory, 'tmp', 'qed')
    FileUtils.mkdir_p(dir)
    dir
  )
end