vitae 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -22,12 +22,12 @@ Great, it's created our two named CVs in the my_cv_project/cvs directory from a
22
22
  Once your done editing, start up the server:
23
23
  $: cd my_cv_project
24
24
  $: vitae server
25
- Serving CVs at 0.0.0.0:3141
25
+ Serving 2 CVs at http://0.0.0.0:3141/ from my_cv_project
26
26
 
27
27
  Enjoy!
28
28
 
29
29
  === Future plans
30
- 1. Data should actually be read from the yaml files.
30
+ 1. All data read from the yaml files should be displayed sensibly.
31
31
  2. Vitae should com bundled with better default templates - CSS, JS and YAML.
32
32
  3. Support for multiple themes.
33
33
  4. The clever stuff
data/bin/vitae CHANGED
@@ -1,67 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require "rubygems"
3
- require "thor"
4
-
5
2
  require File.expand_path('../../lib/vitae', __FILE__)
6
-
7
- module Vitae
8
- class CLI < Thor
9
- include Thor::Actions
10
-
11
- def self.source_root
12
- File.expand_path('../../lib/vitae/templates', __FILE__)
13
- end
14
-
15
-
16
- default_task :create
17
-
18
- desc "create [project_name]", "Create a new vitae project"
19
- def create(name, *cv_names)
20
- self.destination_root = name
21
- puts "creating #{name}..."
22
-
23
- if cv_names.size > 0
24
- cv_names.each do |cv_name|
25
- @name = cv_name
26
- template "cvs/default.yaml.tt", "cvs/#{cv_name}.yaml"
27
- end
28
- else
29
- templates = Dir.glob(File.join(CLI.source_root, "cvs/*.yaml*")).
30
- map{|f| File.basename(f)} -["default.yaml.tt"]
31
-
32
- templates.each do |template_name|
33
- template "cvs/#{template_name}", "cvs/#{template_name.sub(/\.tt$/, '')}"
34
- end
35
- end
36
-
37
- themes = Dir.glob(File.join(CLI.source_root, "themes/*")).map{|f| File.basename(f)}
38
- themes.each do |theme_name|
39
- copy_file "themes/#{theme_name}/application.js"
40
- copy_file "themes/#{theme_name}/application.css"
41
- end
42
-
43
- end
44
-
45
- desc "server [-p 3000]", "Start the vitae server."
46
- def server
47
- require "rack"
48
-
49
- just_pretend = ARGV.delete "--pretend"
50
- port_set_by_user = ARGV.any? { |a| %w[-p --port].include?( a ) }
51
-
52
- server = Rack::Server.new.tap do |s|
53
- s.options[:config] = File.join(File.dirname(__FILE__), "../config.ru")
54
- s.options[:Port] = 3141 unless port_set_by_user
55
- end
56
-
57
- require "vitae/server/server"
58
- Server.project_root = Dir.pwd
59
-
60
- say "Serving CVs at #{server.options[:Host]}:#{server.options[:Port]}", :green
61
- server.start unless just_pretend
62
- end
63
-
64
- end
65
- end
3
+ require "vitae/cli"
66
4
 
67
5
  Vitae::CLI.start
data/lib/vitae.rb CHANGED
@@ -1,2 +1,26 @@
1
1
  $:.unshift File.dirname(File.expand_path(__FILE__))
2
- require "vitae/cv"
2
+ require "vitae/cv"
3
+ require "vitae/ordered_hash"
4
+
5
+ module Vitae
6
+ @@project_root = nil
7
+ def self.project_root
8
+ @@project_root
9
+ end
10
+
11
+ def self.project_root= root
12
+ Server.set :public, File.join(root, "themes") if root && defined?(Server)
13
+ @@project_root = root
14
+ end
15
+
16
+ module OrderedHashExtensions
17
+ def except(exceptions=[])
18
+ reject do |k, v|
19
+ exceptions.include? k
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ Hash.send :include, Vitae::OrderedHashExtensions
data/lib/vitae/cli.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "rubygems"
2
+ require "thor"
3
+
4
+ module Vitae
5
+ class CLI < Thor
6
+ include Thor::Actions
7
+
8
+ def self.source_root
9
+ File.expand_path('templates', File.dirname(__FILE__))
10
+ end
11
+
12
+
13
+ default_task :create
14
+
15
+ desc "create [project_name]", "Create a new vitae project"
16
+ def create(name, *cv_names)
17
+ self.destination_root = name
18
+ puts "creating #{name}..."
19
+
20
+ if cv_names.size > 0
21
+ cv_names.each do |cv_name|
22
+ @name = cv_name
23
+ @human_name = cv_name.split(/[^a-z]+/i).map{|w|w.capitalize}.join(" ")
24
+ @url_name = @name.gsub(/[^a-z\-]+/i, "-")
25
+ template "cvs/default.yaml.tt", "cvs/#{cv_name}.yaml"
26
+ end
27
+ else
28
+ templates = Dir.glob(File.join(CLI.source_root, "cvs/*.yaml*")).
29
+ map{|f| File.basename(f)} -["default.yaml.tt"]
30
+
31
+ templates.each do |template_name|
32
+ template "cvs/#{template_name}", "cvs/#{template_name.sub(/\.tt$/, '')}"
33
+ end
34
+ end
35
+
36
+ themes = Dir.glob(File.join(CLI.source_root, "themes/*")).map{|f| File.basename(f)}
37
+ themes.each do |theme_name|
38
+ copy_file "themes/#{theme_name}/application.js"
39
+ copy_file "themes/#{theme_name}/application.css"
40
+ end
41
+
42
+ end
43
+
44
+ desc "server [-p 3000]", "Start the vitae server."
45
+ def server(project_path=".")
46
+ require "rack"
47
+ just_pretend = ARGV.delete "--pretend"
48
+
49
+ port_set_by_user = ARGV.any? { |a| %w[-p --port].include?( a ) }
50
+
51
+ server = Rack::Server.new.tap do |s|
52
+ s.options[:config] = File.join(File.dirname(__FILE__), "..", "..", "config.ru")
53
+ s.options[:Port] = 3141 unless port_set_by_user
54
+ end
55
+
56
+ require "vitae/server/server"
57
+ Vitae::project_root = File.expand_path(project_path, Dir.pwd)
58
+
59
+ opts = server.options
60
+ cv_count = CV.all.size
61
+ cvs = "#{cv_count} CV#{'s' unless cv_count==1}"
62
+ project_dir_name = File.basename(Vitae::project_root)
63
+
64
+ say "Serving #{cvs} at http://#{opts[:Host]}:#{opts[:Port]}/ from #{project_dir_name}\n", :green
65
+ server.start unless just_pretend
66
+ end
67
+
68
+ end
69
+ end
data/lib/vitae/cv.rb CHANGED
@@ -1,40 +1,79 @@
1
+ require "yaml"
2
+
1
3
  class CV
4
+ attr_reader :file_name
2
5
 
3
- def self.all
4
- cvs = Dir.glob(File.join(Server.project_root, "cvs/*.yaml"))
5
- cvs.map do |cv|
6
- CV.new(cv)
6
+ class << self
7
+ def all
8
+ raise "Vitae::project_root is not set" unless Vitae::project_root
9
+ cvs = Dir.glob(File.join(Vitae::project_root, "cvs/*.yaml"))
10
+ cvs.map do |cv|
11
+ CV.new(cv)
12
+ end
7
13
  end
8
- end
9
-
10
- def self.find name
11
- all.find do |cv|
12
- cv.name == name
14
+
15
+ def find file_name
16
+ all.find do |cv|
17
+ cv.file_name == file_name
18
+ end
19
+ end
20
+
21
+ def first
22
+ all.first
23
+ end
24
+
25
+ def last
26
+ all.last
13
27
  end
14
28
  end
15
29
 
16
30
  def initialize yaml_file
17
- @name = File.basename(yaml_file, '.yaml')
31
+ @yaml_file = yaml_file
32
+ @file_name = File.basename(yaml_file, '.yaml')
18
33
  end
19
34
 
35
+
20
36
  def name
21
- @name
37
+ @name ||= data_hash.delete("name")
22
38
  end
23
39
 
24
- def human_name
25
- name.split("_").map{|w|w.capitalize}.join(" ")
40
+ def vitae_config
41
+ @vitae_config ||= data_hash.delete("vitae_config")
26
42
  end
27
43
 
28
- def to_s
29
- human_name
44
+ def data_hash
45
+ @data_hash ||= YAML::load_file(@yaml_file)
46
+ end
47
+
48
+ def [] key
49
+ data_hash[key]
50
+ end
51
+
52
+ def except exceptions=[]
53
+ data_hash.except( exceptions )
30
54
  end
31
55
 
56
+ def each &block
57
+ data_hash.each do |k, v|
58
+ yield k, v
59
+ end
60
+ end
61
+
62
+
32
63
  def link
33
- "/#{name}"
64
+ "/#{file_name}"
34
65
  end
35
66
 
36
67
  def theme
37
68
  "default"
38
69
  end
39
70
 
71
+ def to_s
72
+ name
73
+ end
74
+
75
+ # def method_missing name, *args, &block
76
+ # data.send name, *args, &block
77
+ # end
78
+
40
79
  end
@@ -0,0 +1,188 @@
1
+ require 'yaml'
2
+
3
+ YAML.add_builtin_type("omap") do |type, val|
4
+ ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
5
+ end
6
+
7
+ module ActiveSupport
8
+ # The order of iteration over hashes in Ruby 1.8 is undefined. For example, you do not know the
9
+ # order in which +keys+ will return keys, or +each+ yield pairs. <tt>ActiveSupport::OrderedHash</tt>
10
+ # implements a hash that preserves insertion order, as in Ruby 1.9:
11
+ #
12
+ # oh = ActiveSupport::OrderedHash.new
13
+ # oh[:a] = 1
14
+ # oh[:b] = 2
15
+ # oh.keys # => [:a, :b], this order is guaranteed
16
+ #
17
+ # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations.
18
+ class OrderedHash < ::Hash #:nodoc:
19
+ def to_yaml_type
20
+ "!tag:yaml.org,2002:omap"
21
+ end
22
+
23
+ def to_yaml(opts = {})
24
+ YAML.quick_emit(self, opts) do |out|
25
+ out.seq(taguri, to_yaml_style) do |seq|
26
+ each do |k, v|
27
+ seq.add(k => v)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ # Hash is ordered in Ruby 1.9!
34
+ if RUBY_VERSION < '1.9'
35
+
36
+ # In MRI the Hash class is core and written in C. In particular, methods are
37
+ # programmed with explicit C function calls and polymorphism is not honored.
38
+ #
39
+ # For example, []= is crucial in this implementation to maintain the @keys
40
+ # array but hash.c invokes rb_hash_aset() originally. This prevents method
41
+ # reuse through inheritance and forces us to reimplement stuff.
42
+ #
43
+ # For instance, we cannot use the inherited #merge! because albeit the algorithm
44
+ # itself would work, our []= is not being called at all by the C code.
45
+
46
+ def initialize(*args, &block)
47
+ super
48
+ @keys = []
49
+ end
50
+
51
+ def self.[](*args)
52
+ ordered_hash = new
53
+
54
+ if (args.length == 1 && args.first.is_a?(Array))
55
+ args.first.each do |key_value_pair|
56
+ next unless (key_value_pair.is_a?(Array))
57
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
58
+ end
59
+
60
+ return ordered_hash
61
+ end
62
+
63
+ unless (args.size % 2 == 0)
64
+ raise ArgumentError.new("odd number of arguments for Hash")
65
+ end
66
+
67
+ args.each_with_index do |val, ind|
68
+ next if (ind % 2 != 0)
69
+ ordered_hash[val] = args[ind + 1]
70
+ end
71
+
72
+ ordered_hash
73
+ end
74
+
75
+ def initialize_copy(other)
76
+ super
77
+ # make a deep copy of keys
78
+ @keys = other.keys
79
+ end
80
+
81
+ def []=(key, value)
82
+ @keys << key unless has_key?(key)
83
+ super
84
+ end
85
+
86
+ def delete(key)
87
+ if has_key? key
88
+ index = @keys.index(key)
89
+ @keys.delete_at index
90
+ end
91
+ super
92
+ end
93
+
94
+ def delete_if
95
+ super
96
+ sync_keys!
97
+ self
98
+ end
99
+
100
+ def reject!
101
+ super
102
+ sync_keys!
103
+ self
104
+ end
105
+
106
+ def reject(&block)
107
+ dup.reject!(&block)
108
+ end
109
+
110
+ def keys
111
+ @keys.dup
112
+ end
113
+
114
+ def values
115
+ @keys.collect { |key| self[key] }
116
+ end
117
+
118
+ def to_hash
119
+ self
120
+ end
121
+
122
+ def to_a
123
+ @keys.map { |key| [ key, self[key] ] }
124
+ end
125
+
126
+ def each_key
127
+ @keys.each { |key| yield key }
128
+ end
129
+
130
+ def each_value
131
+ @keys.each { |key| yield self[key]}
132
+ end
133
+
134
+ def each
135
+ @keys.each {|key| yield [key, self[key]]}
136
+ end
137
+
138
+ alias_method :each_pair, :each
139
+
140
+ def clear
141
+ super
142
+ @keys.clear
143
+ self
144
+ end
145
+
146
+ def shift
147
+ k = @keys.first
148
+ v = delete(k)
149
+ [k, v]
150
+ end
151
+
152
+ def merge!(other_hash)
153
+ if block_given?
154
+ other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
155
+ else
156
+ other_hash.each { |k, v| self[k] = v }
157
+ end
158
+ self
159
+ end
160
+
161
+ alias_method :update, :merge!
162
+
163
+ def merge(other_hash, &block)
164
+ dup.merge!(other_hash, &block)
165
+ end
166
+
167
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
168
+ def replace(other)
169
+ super
170
+ @keys = other.keys
171
+ self
172
+ end
173
+
174
+ def invert
175
+ OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
176
+ end
177
+
178
+ def inspect
179
+ "#<OrderedHash #{super}>"
180
+ end
181
+
182
+ private
183
+ def sync_keys!
184
+ @keys.delete_if {|k| !has_key?(k)}
185
+ end
186
+ end
187
+ end
188
+ end
Binary file
@@ -9,8 +9,9 @@ module Helpers
9
9
  html
10
10
  end
11
11
 
12
- def link_to(text, link=nil, options={})
13
- link ||= text.split(/\s+/).join('_').downcase
12
+ def link_to(text, *args)
13
+ options = args.last.is_a?(Hash) ? args.pop : {}
14
+ link = args.first || text.split(/\s+/).join('_').downcase
14
15
  content_tag :a, text, options.merge({:href => link})
15
16
  end
16
17
 
@@ -5,27 +5,20 @@ require "vitae/server/helpers"
5
5
  class Server < Sinatra::Base
6
6
  helpers Helpers
7
7
  set :views, File.dirname(__FILE__) + '/views'
8
+ set :public, File.join(Vitae::project_root, "themes") rescue ''
8
9
 
9
10
  get '/' do
10
11
  @cvs = CV.all
11
12
  haml :index
12
13
  end
13
14
 
15
+ get "/favicon.ico" do
16
+ send_file File.join(File.dirname(__FILE__), "assets", "favicon.ico"), :type => 'image/x-icon', :disposition => 'inline'
17
+ end
18
+
14
19
  get '/:name' do
15
20
  @cv = CV.find( params[:name] )
16
21
  haml :show
17
22
  end
18
23
 
19
-
20
-
21
- @@project_root = nil
22
- def self.project_root
23
- @@project_root
24
- end
25
-
26
- def self.project_root= root
27
- set :public, File.join(root, "themes")
28
- @@project_root = root
29
- end
30
-
31
24
  end
@@ -1,3 +1,5 @@
1
+ %h1#title Vitae
1
2
  %ul
2
3
  - @cvs.each do |cv|
3
- %li= link_to cv.human_name, cv.link
4
+ %li
5
+ %h2= link_to cv.name, cv.link
@@ -1,10 +1,8 @@
1
1
  !!! 5
2
2
  %html
3
3
  %head
4
- %title Vitae #{ @cv && ": "+ @cv.human_name }
4
+ %title Vitae #{ @cv && ": "+ @cv.name }
5
5
  = include_theme_assets
6
6
  %body
7
- #header
8
- %h1#title= @cv && @cv.human_name || "Vitae"
9
7
  #content
10
8
  = yield
@@ -1 +1,21 @@
1
+ = haml :vcard
2
+ - @cv.except(%w[vitae_config contact]).each do |category, value|
3
+ %h2= category
4
+ .category{ :id => category }
5
+ - case value
6
+ - when String
7
+ = value
8
+ - when Hash
9
+ %ul.jobs-list
10
+ - value.each do |subcategory, subvalue|
11
+ %li.vevent.experience
12
+ %h3= subcategory
13
+ %p.job-description= subvalue
14
+
15
+ / - value.each do |subcategory, subvalue|
16
+ / %h3= subcategory
17
+ / .subcategory{ :id => subcategory }
18
+ / = subvalue
19
+ %br
20
+ %br
1
21
  =link_to "Vitae Home", "/"
@@ -0,0 +1,39 @@
1
+ .vcard
2
+ %h1#title.fn= @cv.name
3
+
4
+ %ul#quick-links
5
+ - @cv["contact"].each do |key, value|
6
+ - case value
7
+ - when /^https?:\/\//
8
+ %li
9
+ %span.label= key
10
+ = link_to value.sub(/^https?:\/\//, ''), value, :class => "url"
11
+ - when Hash
12
+ - case key
13
+ - when "phones"
14
+ - value.each do |phone, number|
15
+ %li.tel
16
+ %span.label.type= phone
17
+ %span.value= number
18
+ - when "address"
19
+ %li
20
+ %span.label= key
21
+ %span.adr
22
+ - value.each_with_index do |(address_field, address_value), i|
23
+ = succeed "#{', ' if i<value.size-1}" do
24
+ %span{:class => address_field}= address_value
25
+ - else
26
+ - case key
27
+ - when "nickname"
28
+ %li.nick
29
+ %span.label= key
30
+ %span.fn.nickname= value
31
+ - when /e-?mail/
32
+ %li
33
+ %span.label= key
34
+ - mailto_less = value.sub(/^mailto:/, '')
35
+ = link_to mailto_less, "mailto:#{mailto_less}", :class => "email"
36
+ - else
37
+ %li
38
+ %span.label= key
39
+ = value
@@ -1,5 +1,22 @@
1
- name: Arthur Gunn
1
+ --- !omap
2
+ - name: Arthur Gunn
2
3
 
3
- contact:
4
- homepage: http://arthurgunn.com
5
- email: arthur@gunn.co.nz
4
+ - vitae_config: !omap
5
+ - email: arthur@gunn.co.nz
6
+ - crypted_password: d7f42977c55e23fc92e4190c6adc07862a572879
7
+ - password_salt: 8f7a8cf04f4e
8
+
9
+ - contact: !omap
10
+ - nickname: Arthur
11
+ - e-mail: mailto:arthur@gunn.co.nz
12
+ - homepage: http://arthurgunn.com
13
+ - github: https://github.com/gunn
14
+ - address: !omap
15
+ - street-address: 96 London St.
16
+ - locality: City Rise
17
+ - region: Otago
18
+ - country-name: New Zealand
19
+ - postal-code: 9016
20
+ - phones: !omap
21
+ - home: (03) 477 99 44
22
+ - mobile: 027 477 7723
@@ -1,5 +1,11 @@
1
- name: <%= @name %>
1
+ --- !omap
2
+ - name: <%= @human_name %>
2
3
 
3
- contact:
4
- homepage: http://<%= @name %>.me/
5
- email: <%= @name %>@gmail.com
4
+ - vitae_config: !omap
5
+ - email: <%= @url_name %>@gmail.com
6
+ - crypted_password: d7f42977c55e23fc92e4190c6adc07862a572879
7
+ - password_salt: 8f7a8cf04f4e
8
+
9
+ - contact: !omap
10
+ - email: <%= @url_name %>@gmail.com
11
+ - homepage: http://<%= @url_name %>.me/
@@ -127,6 +127,12 @@ ul {
127
127
  width: 6em;
128
128
  padding-right: 1em;
129
129
  text-align: right;
130
+ vertical-align: top;
131
+ }
132
+
133
+ #quick-links li span.adr {
134
+ display: inline-block;
135
+ width: 15em;
130
136
  }
131
137
 
132
138
  #quick-links li a:hover, #quick-links li a:focus {
@@ -193,6 +199,14 @@ em {
193
199
  color: #333;
194
200
  }
195
201
 
202
+
203
+
204
+ .nick {
205
+ display: none;
206
+ }
207
+
208
+
209
+
196
210
  #summarize { color: #555; position: absolute; top: 1em; right: 1em; width: 10em; }
197
211
 
198
212
  #projects-contributed-to { margin-left: 1em; }
data/lib/vitae/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Vitae
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -4,28 +4,35 @@ require "nokogiri"
4
4
  class ActiveServerTest < VitaeServerTestCase
5
5
 
6
6
  test "the homepage contains lists the cvs we're hosting" do
7
- clear_test_dir
8
- vitae_create "resumes derek arthur sajal"
9
-
10
- get '/'
11
- assert last_response.ok?
12
- assert_matches %w[Derek Arthur Sajal]
13
- assert_select "a[href='/sajal']", "Sajal"
14
-
15
- check_includes_standard_assets
7
+ with_project :dkaz do
8
+ get '/'
9
+ assert last_response.ok?
10
+ assert_matches %w[Katya Derek Arthur Zeena]
11
+ assert_select "a[href='/katya']", "Katya"
12
+
13
+ check_includes_standard_assets
14
+ end
16
15
  end
17
16
 
18
17
  test "a CV gets its own show page" do
19
- clear_test_dir
20
- vitae_create "resumes arthur_gunn"
18
+ with_project :sals do
19
+ get '/sajal_shah'
20
+ assert last_response.ok?
21
21
 
22
- get '/arthur_gunn'
23
- assert last_response.ok?
22
+ assert_select "h1", "Sajal Shah"
23
+ assert_select "a[href='/']"
24
24
 
25
- assert_select "h1", "Arthur Gunn"
26
- assert_select "a[href='/']"
25
+ check_includes_standard_assets
26
+ end
27
+ end
28
+
29
+ test "a CV doesn't mindlessly blat out converted yaml data" do
30
+ with_project :default do
31
+ get '/arthur_gunn'
32
+ assert last_response.ok?
27
33
 
28
- check_includes_standard_assets
34
+ assert_no_select "h2", "vitae_config"
35
+ end
29
36
  end
30
37
 
31
38
  def check_includes_standard_assets theme="default"
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+ require "nokogiri"
3
+
4
+ class CvFormatTest < VitaeServerTestCase
5
+
6
+ test "a CV has a well formatted vcard" do
7
+ with_project :default do
8
+ get '/arthur_gunn'
9
+ assert last_response.ok?
10
+
11
+ assert_select ".vcard" do
12
+ assert_select "h1#title.fn", "Arthur Gunn"
13
+ assert_select "ul#quick-links li" do
14
+ assert_select "span.label", "github"
15
+ assert_select "a.url[href='https://github.com/gunn']", "github"
16
+ assert_select "span.label", "e-mail"
17
+ assert_select "a.email[href='mailto:arthur@gunn.co.nz']", "arthur@gunn.co.nz"
18
+ end
19
+ assert_select "span.adr" do
20
+ assert_select "span.street-address", "96 London St."
21
+ assert_select "span.postal-code", "9016"
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+ end
data/test/test_helper.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  require 'fileutils'
2
2
  require 'test/unit'
3
+ require "stringio"
3
4
 
4
- require File.expand_path('../../lib/vitae', __FILE__)
5
- require 'vitae/server/server'
5
+ require 'rubygems'
6
6
  require 'rack/test'
7
7
 
8
- VITAE_TEST_DIR = ::File.expand_path('../../tmp', __FILE__)
9
- VITAE_EXECUTABLE = ::File.expand_path('../../bin/vitae', __FILE__)
8
+ require File.expand_path('../../lib/vitae', __FILE__)
9
+ require 'vitae/server/server'
10
+ require "vitae/cli"
10
11
 
11
12
  class VitaeTestCase < Test::Unit::TestCase
12
13
  def self.test name, &block
@@ -14,31 +15,65 @@ class VitaeTestCase < Test::Unit::TestCase
14
15
  define_method name, &block
15
16
  end
16
17
 
18
+ @@projects = {
19
+ :default => "",
20
+ :sals => "sajal_shah",
21
+ :dkaz => "derek katya arthur zeena"
22
+ }
23
+
24
+ @@vitae_test_dir = File.expand_path('../../tmp', __FILE__)
25
+ @@vitae_executable = File.expand_path('../../bin/vitae', __FILE__)
26
+
17
27
  test "tests working here" do
18
28
  # assert(true, "Just to keep t/u happy.")
19
29
  end
20
30
 
21
31
  def vitae_test_dir
22
- FileUtils.mkdir_p VITAE_TEST_DIR
32
+ FileUtils.mkdir_p @@vitae_test_dir
23
33
  end
24
34
 
25
35
  def vitae_executable
26
- VITAE_EXECUTABLE
36
+ @@vitae_executable
27
37
  end
28
38
 
29
- %w[server].each do |command|
30
- define_method "vitae_#{command}" do |*args|
31
- args = args.first
32
- FileUtils.cd vitae_test_dir
33
- `#{vitae_executable} #{command} #{args} 2>&1`
34
- end
39
+ def vitae_server args=""
40
+ cli = Vitae::CLI.new
41
+ real_argv = ARGV
42
+ ARGV.replace(args.split(/\s+/))
43
+ get_stdout_and_stderr { cli.server }
44
+ ensure
45
+ ARGV.replace(real_argv)
35
46
  end
36
47
 
37
48
  def vitae_create args=""
38
49
  FileUtils.cd vitae_test_dir
39
- project_name = args.split(/\s+/).first || ""
40
- Server.project_root = File.join(vitae_test_dir, project_name)
41
- `#{vitae_executable} create #{args} 2>&1`
50
+ cli = Vitae::CLI.new
51
+ args = args.split(/\s+/)
52
+ project_name = args.first || ""
53
+ Vitae::project_root = File.join(vitae_test_dir, project_name)
54
+
55
+ get_stdout_and_stderr { cli.create(*args) }
56
+ end
57
+
58
+ def with_project name, &block
59
+ project_dir = File.join(vitae_test_dir, name.to_s)
60
+ vitae_create "#{project_dir} #{@@projects[name]}" if !File.exist?( project_dir )
61
+ Vitae::project_root = project_dir
62
+ FileUtils.cd project_dir
63
+ yield
64
+ ensure
65
+ Vitae::project_root = ''
66
+ end
67
+
68
+ def get_stdout_and_stderr &block
69
+ new_out = StringIO.new
70
+ real_stdout, $stdout = $stdout, new_out
71
+ real_stderr, $stderr = $stderr, new_out
72
+ yield
73
+ new_out.string
74
+ ensure
75
+ $stderr = real_stderr
76
+ $stdout = real_stdout
42
77
  end
43
78
 
44
79
  def clear_test_dir
@@ -46,7 +81,7 @@ class VitaeTestCase < Test::Unit::TestCase
46
81
  end
47
82
 
48
83
  def vitae_file? path
49
- File.exist? ::File.expand_path(path, vitae_test_dir)
84
+ File.exist? File.expand_path(path, vitae_test_dir)
50
85
  end
51
86
 
52
87
  end
@@ -64,15 +99,43 @@ class VitaeServerTestCase < VitaeTestCase
64
99
  end
65
100
  end
66
101
 
67
- def assert_select selector, content=nil, text=last_response.body
68
- matches = Nokogiri::HTML.parse(text).css(selector)
69
- assert(matches.size>0, "No matches for the selector '#{selector}'.")
70
- if content
71
- matches.each do |el|
72
- return assert(true) if el.content.match content
102
+ def assert_no_select selector, content=nil
103
+ assert_select selector, content, :want_matches => false
104
+ end
105
+
106
+ def assert_select selector, content=nil, options={}
107
+ options = { :want_matches => true }.merge(options)
108
+
109
+ text = options[:text] || last_response.body
110
+ want_matches = options[:want_matches]
111
+
112
+ selector = "#{@parent_selector} #{selector}" if @parent_selector
113
+
114
+ elements = Nokogiri::HTML.parse(text).css(selector)
115
+ have_matches = elements.size>0
116
+
117
+ did_match = if content
118
+ elements.any? { |el| el.content.match content }
119
+ else
120
+ have_matches
121
+ end
122
+
123
+ if !want_matches
124
+ assert(!did_match, "Found matches for the selector '#{selector}', but we didn't want any.")
125
+ else
126
+ assert(did_match, "No matches for the selector '#{selector}'.") if !content
127
+ assert(did_match, "No matches for '#{content}' with the selector '#{selector}'.") if content
128
+ end
129
+
130
+ if block_given? && have_matches
131
+ begin
132
+ in_scope, @parent_selector = @parent_selector, selector
133
+ yield
134
+ ensure
135
+ @parent_selector = in_scope
73
136
  end
74
- assert(false, "No matches for '#{content}' with the selector '#{selector}'.")
75
137
  end
138
+
76
139
  end
77
140
 
78
141
  end
@@ -0,0 +1,51 @@
1
+ require "test_helper"
2
+
3
+ class UtilitiesTest < VitaeTestCase
4
+
5
+ test "we can capture stdout and stderr" do
6
+ captured_string = get_stdout_and_stderr do
7
+ puts "hello!"
8
+ print "h"
9
+ puts "i"
10
+ $stderr.print "bye!"
11
+ end
12
+ expected_string = "hello!\nhi\nbye!"
13
+
14
+ assert_equal(expected_string, captured_string)
15
+ end
16
+
17
+ test "yaml can be ordered" do
18
+ numbers_from_yaml = example_yaml_hash["numbers"].map { |k,v| "#{k} = #{v}" }.join("\n")
19
+
20
+ the_numbers = "one = 1\ntwo = 2\nthree = 3\nfour = 4\nfive = 5\nsix = 6"
21
+
22
+ assert_equal(the_numbers, numbers_from_yaml)
23
+ end
24
+
25
+ test "ordered hash items can be skipped" do
26
+ number_string = example_yaml_hash["numbers"].except(%w[one three four five]).map { |k,v| k }.join(", ")
27
+ assert_equal("two, six", number_string)
28
+ end
29
+
30
+ def example_yaml_hash
31
+ yaml_hash_string = %q{
32
+ --- !omap
33
+ - numbers: !omap
34
+ - one: 1
35
+ - two: 2
36
+ - three: 3
37
+ - four: 4
38
+ - five: 5
39
+ - six: 6
40
+ }
41
+ yaml_hash = YAML.load yaml_hash_string
42
+ end
43
+
44
+ test "CV.first and CV.last do their jobs" do
45
+ with_project :dkaz do
46
+ assert_equal "Arthur", CV.first.name
47
+ assert_equal "Zeena", CV.last.name
48
+ end
49
+ end
50
+
51
+ end
@@ -2,11 +2,6 @@ require "test_helper"
2
2
 
3
3
  class VitaeExecutableTest < VitaeTestCase
4
4
 
5
- test "create gives help when called without args" do
6
- output = vitae_create
7
- assert_match('"create" was called incorrectly', output)
8
- end
9
-
10
5
  test "create generates a project" do
11
6
  clear_test_dir
12
7
  output = vitae_create("my_cvs")
@@ -35,8 +30,14 @@ class VitaeExecutableTest < VitaeTestCase
35
30
  end
36
31
  end
37
32
 
38
- test "the server responds well" do
39
- assert_match("Serving CVs", vitae_server("--pretend"))
33
+ test "the server tells us how many CVs it's serving, where and how" do
34
+ with_project :dkaz do
35
+ assert_match("Serving 4 CVs at http://0.0.0.0:3141/ from dkaz\n", vitae_server("--pretend"))
36
+ end
37
+
38
+ with_project :sals do
39
+ assert_match("Serving 1 CV at http://0.0.0.0:3141/ from sals\n", vitae_server("--pretend"))
40
+ end
40
41
  end
41
42
 
42
43
  test "the server accepts a custom port" do
@@ -45,8 +46,13 @@ class VitaeExecutableTest < VitaeTestCase
45
46
  assert_match(":1121", vitae_server("--pretend -p 1121"))
46
47
  end
47
48
 
48
- test "the server gives rackup help" do
49
- assert_match("Usage: rackup", vitae_server("-h"))
50
- end
49
+ # test "the server gives rackup help" do
50
+ # assert_match("Usage: rackup", vitae_server("-h"))
51
+ # end
52
+ #
53
+ # test "create gives help when called without args" do
54
+ # output = vitae_create
55
+ # assert_match('"create" was called incorrectly', output)
56
+ # end
51
57
 
52
58
  end
data/vitae.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.email = ["arthur@gunn.co.nz"]
11
11
  s.homepage = "https://github.com/gunn/vitae"
12
12
  s.summary = %q{A structured CV publishing system.}
13
- s.description = %q{Vitae is to CVs what rubygems is to ruby code. Under heavy development, not very useful yet.}
13
+ s.description = %q{Vitae is to CVs what rubygems is to ruby code. Under heavy development, very under development.}
14
14
 
15
15
  s.add_dependency "sinatra", "~>1.1.0"
16
16
  s.add_dependency "haml", "~>3.0.0"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vitae
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Arthur Gunn
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-04 00:00:00 +13:00
18
+ date: 2010-12-08 00:00:00 +13:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -82,7 +82,7 @@ dependencies:
82
82
  version: 1.4.0
83
83
  type: :development
84
84
  version_requirements: *id004
85
- description: Vitae is to CVs what rubygems is to ruby code. Under heavy development, not very useful yet.
85
+ description: Vitae is to CVs what rubygems is to ruby code. Under heavy development, very under development.
86
86
  email:
87
87
  - arthur@gunn.co.nz
88
88
  executables:
@@ -101,13 +101,17 @@ files:
101
101
  - bin/vitae
102
102
  - config.ru
103
103
  - lib/vitae.rb
104
+ - lib/vitae/cli.rb
104
105
  - lib/vitae/cv.rb
106
+ - lib/vitae/ordered_hash.rb
105
107
  - lib/vitae/server/.gitignore
108
+ - lib/vitae/server/assets/favicon.ico
106
109
  - lib/vitae/server/helpers.rb
107
110
  - lib/vitae/server/server.rb
108
111
  - lib/vitae/server/views/index.haml
109
112
  - lib/vitae/server/views/layout.haml
110
113
  - lib/vitae/server/views/show.haml
114
+ - lib/vitae/server/views/vcard.haml
111
115
  - lib/vitae/templates/cvs/arthur_gunn.yaml
112
116
  - lib/vitae/templates/cvs/default.yaml.tt
113
117
  - lib/vitae/templates/themes/default/application.css
@@ -115,8 +119,10 @@ files:
115
119
  - lib/vitae/version.rb
116
120
  - test/active_server_test.rb
117
121
  - test/basic_server_test.rb
122
+ - test/cv_format_test.rb
118
123
  - test/tag_helpers_test.rb
119
124
  - test/test_helper.rb
125
+ - test/utilities_test.rb
120
126
  - test/vitae_executable_test.rb
121
127
  - vitae.gemspec
122
128
  has_rdoc: true
@@ -156,6 +162,8 @@ summary: A structured CV publishing system.
156
162
  test_files:
157
163
  - test/active_server_test.rb
158
164
  - test/basic_server_test.rb
165
+ - test/cv_format_test.rb
159
166
  - test/tag_helpers_test.rb
160
167
  - test/test_helper.rb
168
+ - test/utilities_test.rb
161
169
  - test/vitae_executable_test.rb