vitae 0.1.1 → 0.1.2

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.
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