├── .gitignore
├── .rb
├── Gemfile
├── Gemfile.lock
├── README.md
├── custom.css
├── example.rb
├── jobless.gemspec
├── lib
├── document.rb
├── errors.rb
├── group.rb
├── item.rb
├── jobless.rb
└── template
│ ├── style.css
│ └── template.html.erb
├── spec
├── document_spec.rb
├── fixtures
│ ├── custom_style.css
│ └── vcr_cassettes
│ │ ├── github_invalid.yml
│ │ └── github_jobless.yml
├── group_spec.rb
├── item_spec.rb
├── jobless_spec.rb
└── spec_helper.rb
└── test_examples
└── small.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | [._]*.s[a-w][a-z]
2 | [._]s[a-w][a-z]
3 | *.un~
4 | Session.vim
5 | .netrwhist
6 | *~
7 | *.html
8 | *.gem
9 |
--------------------------------------------------------------------------------
/.rb:
--------------------------------------------------------------------------------
1 | require_relative 'lib/jobless'
2 |
3 | Jobless.cv do
4 | name "Filip Defar"
5 | email "dabrorius@gmail.com"
6 | location "Zagreb, Croatia"
7 | homepage "http://dabrorius.github.io"
8 |
9 | employment do
10 | entry do
11 | title "Full-stack Rails developer"
12 | company "DirectVest"
13 | technologies "Ruby, Ruby on Rails, Javascript"
14 | homepage "http://google.com"
15 | description "Created an API wrapper to a Folio Institutional service. "\
16 | "Created a full test suite for existing features. "\
17 | "Refactored the application (From 1.5 to 3.9+ GPA on CodeClimate). "\
18 | "Created multistep forms for the investment process. "\
19 | "Created a dashboard that heavily relies on external APIs, "\
20 | "as well as low level caching for increased performance."
21 | start_date "June 2015"
22 | end_date "Current"
23 | end
24 | entry do
25 | title "Full-stack Rails developer"
26 | company "BizRevr"
27 | technologies "Ruby, Ruby on Rails, Elasticsearch"
28 | description "Helping design and develop new features. "\
29 | "Created an interface for bulk editing records similar to excel "\
30 | "spreadsheet. Technologies: Ruby on Rails, Javascript, jQuery"
31 | start_date "April 2015"
32 | end_date "June 2015"
33 | end
34 | entry do
35 | title "Full-stack Rails developer"
36 | company "ThinkCERCA"
37 | technologies "Ruby, Ruby on Rails, Javascript"
38 | description "I worked in a team with 6 other developers. "\
39 | "My job consisted of maintaining, improving and adding new feature to "\
40 | "existing Rails, backbone.js and Ember.js code."
41 | start_date "July 2014"
42 | end_date "Februrary 2015"
43 | end
44 | entry do
45 | title "Full-stack Rails developer"
46 | company "CampWire"
47 | technologies "Ruby, Ruby on Rails, Javascript"
48 | description "I created a working payment system with Stripe. "\
49 | "Wrote a full test suite for existing application. Maintained "\
50 | "and refactored existing code. Introduced new developers to the "\
51 | "application."
52 | start_date "February 2013"
53 | end_date "July 2014"
54 | end
55 | end
56 |
57 | education do
58 | entry do
59 | title "M.S. Computer Science"
60 | company "The University of Zagreb"
61 | start_date "2011"
62 | end_date "2013"
63 | end
64 | entry do
65 | title "B.S. Software Engineering"
66 | company "The University of Zagreb"
67 | start_date "2007"
68 | end_date "2011"
69 | end
70 | end
71 |
72 | open_source do
73 | entry do
74 | title "burek gem"
75 | homepage "https://github.com/dabrorius/burek"
76 | description "A ruby/rails gem for managing translations easy way."
77 | end
78 | entry do
79 | title "markov-noodles"
80 | homepage "https://github.com/dabrorius/markovnoodles"
81 | description "Markov noodles is a minimalistic library for generating "\
82 | "text using Markov chains."
83 | end
84 | entry do
85 | title "grape"
86 | homepage "https://github.com/rubygrape/grape"
87 | description "I am a contributer to grape an opinionated microframework "\
88 | "for creating RESTlike APIs in Ruby."
89 | end
90 | entry do
91 | title "CatMe iOS App"
92 | homepage "https://itunes.apple.com/us/app/id994659934"
93 | description "CatMe is a simple native mobile app that allows you to "\
94 | "virtually pet cats! Move your finger over a cat photo and you will "\
95 | "hear it purr. It's free, adfree and completely useless."
96 | end
97 | end
98 |
99 | other_experience do
100 | entry do
101 | title "StackOverflow"
102 | homepage "http://stackoverflow.com/users/735143/dabrorius"
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'activesupport'
4 | gem 'rspec'
5 | gem 'webmock'
6 | gem 'vcr'
7 | gem 'byebug'
8 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | activesupport (5.2.3)
5 | concurrent-ruby (~> 1.0, >= 1.0.2)
6 | i18n (>= 0.7, < 2)
7 | minitest (~> 5.1)
8 | tzinfo (~> 1.1)
9 | addressable (2.6.0)
10 | public_suffix (>= 2.0.2, < 4.0)
11 | byebug (11.0.1)
12 | concurrent-ruby (1.1.5)
13 | crack (0.4.3)
14 | safe_yaml (~> 1.0.0)
15 | diff-lcs (1.3)
16 | hashdiff (1.0.0)
17 | i18n (1.6.0)
18 | concurrent-ruby (~> 1.0)
19 | minitest (5.11.3)
20 | public_suffix (3.1.1)
21 | rspec (3.8.0)
22 | rspec-core (~> 3.8.0)
23 | rspec-expectations (~> 3.8.0)
24 | rspec-mocks (~> 3.8.0)
25 | rspec-core (3.8.2)
26 | rspec-support (~> 3.8.0)
27 | rspec-expectations (3.8.4)
28 | diff-lcs (>= 1.2.0, < 2.0)
29 | rspec-support (~> 3.8.0)
30 | rspec-mocks (3.8.1)
31 | diff-lcs (>= 1.2.0, < 2.0)
32 | rspec-support (~> 3.8.0)
33 | rspec-support (3.8.2)
34 | safe_yaml (1.0.5)
35 | thread_safe (0.3.6)
36 | tzinfo (1.2.5)
37 | thread_safe (~> 0.1)
38 | vcr (5.0.0)
39 | webmock (3.6.2)
40 | addressable (>= 2.3.6)
41 | crack (>= 0.3.2)
42 | hashdiff (>= 0.4.0, < 2.0.0)
43 |
44 | PLATFORMS
45 | ruby
46 |
47 | DEPENDENCIES
48 | activesupport
49 | byebug
50 | rspec
51 | vcr
52 | webmock
53 |
54 | BUNDLED WITH
55 | 2.0.2
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jobless
2 | Jobless is a simple DSL written in Ruby for generating CVs.
3 | It generates HTML files, which can be easily converted to PDF format if needed.
4 | Here's an example of Jobless code.
5 |
6 | ```ruby
7 | Jobless.cv do
8 | name "John Doe"
9 | phone "(+1) 123 456 789"
10 | email "john.doe@gmail.com"
11 |
12 | employment do
13 | entry do
14 | title "Full-stack Rails developer"
15 | company "Royal Programming Squad"
16 | start_date "April 2015"
17 | end_date "June 2015"
18 | end
19 | entry do
20 | title "C# Developer"
21 | company "Microsoft"
22 | start_date "January 2015"
23 | end_date "April 2015"
24 | end
25 | end
26 | end
27 | ```
28 |
29 | You can see a larger source example [here](https://github.com/dabrorius/jobless/blob/master/example.rb) which gets compiled into [this](http://dabrorius.github.io/cv.html).
30 |
31 | ## Installation
32 |
33 | You can install jobless via RubyGems
34 |
35 | ```
36 | gem install jobless
37 | ```
38 |
39 | Then just create a new ruby file, require jobless and start coding. When you
40 | run that file your CV will be generated.
41 |
42 | ## How to start?
43 |
44 | The best way to start is by forking the [example repository](https://github.com/dabrorius/jobless-example).
45 | It's convinient because you can use your GitHub repo to store the CV.
46 |
47 | ## Available keywords
48 | ### Personal info
49 | Following keywords are available on top level:
50 | * name
51 | * email
52 | * location
53 | * address
54 | * phone
55 | * homepage
56 | * github
57 | * blog
58 |
59 | ### Groups and entries
60 | Key part of a CV are lists of you archievements grouped in different categories.
61 | You can add groups to your CV with _group_ keyword. Each group consists
62 | of multiple entries
63 |
64 | ```ruby
65 | group "Group title" do
66 | entry do
67 | end
68 | end
69 | ```
70 |
71 | For convenience, Jobless provides several keywords with pre-defined titles.
72 | * employment
73 | * education
74 | * open_source
75 | * other_experience
76 | * work_experience
77 | * summary
78 | * skills
79 | * projects
80 | * awards
81 |
82 | ```ruby
83 | education do
84 | entry do
85 | title "Ruby on Rails Course"
86 | end
87 | end
88 | ```
89 |
90 | ### Entry
91 | Entry describes one instance of your experience. For example one job position or
92 | open source project.
93 | Following keywords are available for each entry.
94 |
95 | * title
96 | * company
97 | * homepage
98 | * technologies
99 | * description
100 | * start_date
101 | * end_date
102 |
103 | They can also have multiple __bulletin__ keywords, each of which will be rendered
104 | as one bulletin item.
105 |
106 | ### GitHub Repo
107 | GitHub Repo is a special type of entry that given a repository name will fetch
108 | and automatically fill in title, homepage and description via GitHub API.
109 |
110 | ```ruby
111 | open_source do
112 | github_repo 'dabrorius/jobless'
113 | end
114 | ```
115 |
116 | You can also override existing or add new keywords to that entry.
117 |
118 | ```ruby
119 | open_source do
120 | github_repo 'dabrorius/jobless' do
121 | description 'A different descrpition'
122 | technologies 'Ruby'
123 | end
124 | end
125 | ```
126 |
127 | ## Style
128 | If you don't like the default style, you can provide your custom stylesheet to
129 | be used in the CV.
130 |
131 | ```ruby
132 | Jobless.cv do
133 | name "John Doe"
134 | email "john.doe@gmail.com"
135 | stylesheet "my_awesome_style.css"
136 | end
137 | ```
138 |
139 | ## Ports
140 |
141 | - Clojure [jobless-clj](https://github.com/jbristow/jobless-clj) by [jbristow](https://github.com/jbristow)
142 |
--------------------------------------------------------------------------------
/custom.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/example.rb:
--------------------------------------------------------------------------------
1 | require 'jobless'
2 |
3 | Jobless.cv do
4 | name "Filip Defar"
5 | email "filip.defar@gmail.com"
6 | location "Zagreb, Croatia"
7 | address "Naserov Trg 4"
8 | homepage "http://dabrorius.github.io"
9 |
10 | employment do
11 | entry do
12 | title "Full-stack Rails developer"
13 | company "DirectVest"
14 | technologies "Ruby, Ruby on Rails, Javascript"
15 | bulletin "Created a wrapper for Folio Institutional API."
16 | bulletin "Created a full test suite for existing features."
17 | bulletin "Refactored the application (From 1.5 to 3.9+ GPA on CodeClimate)."
18 | bulletin "Created multistep forms for the investment process."
19 | bulletin "Created a dashboard that heavily relies on external APIs "\
20 | "as well as low level caching for increased performance."
21 | start_date "June 2015"
22 | end_date "November 2015"
23 | end
24 | entry do
25 | title "Full-stack Rails developer"
26 | company "BizRevr"
27 | technologies "Ruby, Ruby on Rails, Elasticsearch"
28 | bulletin "Helping design and develop new features. "
29 | bulletin "Created an interface for bulk editing records similar to excel "\
30 | "spreadsheet."
31 | start_date "April 2015"
32 | end_date "June 2015"
33 | end
34 | entry do
35 | title "Full-stack Rails developer"
36 | company "ThinkCERCA"
37 | technologies "Ruby, Ruby on Rails, Javascript"
38 | description "I worked on a team with six other developers. "\
39 | "My job consisted of maintaining, improving and adding new features to "\
40 | "existing Rails, backbone.js and Ember.js code."
41 | start_date "July 2014"
42 | end_date "Februrary 2015"
43 | end
44 | entry do
45 | title "Full-stack Rails developer"
46 | company "CampWire"
47 | technologies "Ruby, Ruby on Rails, Javascript"
48 | bulletin "I created a working payment system with Stripe. "
49 | bulletin "I wrote a full test suite for the existing code."
50 | bulletin "Maintained and refactored existing code."
51 | bulletin "Introduced new developers to the application."
52 | start_date "February 2013"
53 | end_date "July 2014"
54 | end
55 | end
56 |
57 | education do
58 | entry do
59 | title "M.S. Computer Science"
60 | company "The University of Zagreb"
61 | start_date "2011"
62 | end_date "2013"
63 | end
64 | entry do
65 | title "B.S. Software Engineering"
66 | company "The University of Zagreb"
67 | start_date "2007"
68 | end_date "2011"
69 | end
70 | end
71 |
72 | open_source do
73 | github_repo "dabrorius/jobless"
74 | github_repo "dabrorius/markov-noodles"
75 | github_repo "ruby-grape/grape" do
76 | description "I am a contributor to grape, an opinionated micro-framework "\
77 | "for creating REST-like APIs in Ruby."
78 | end
79 | github_repo "dabrorius/burek"
80 | end
81 |
82 | other_experience do
83 | entry do
84 | title "CatMe iOS App"
85 | homepage "https://itunes.apple.com/us/app/id994659934"
86 | description "CatMe is a simple native mobile app that allows you to "\
87 | "virtually pet cats! Move your finger over a cat photo and you will "\
88 | "hear it purr. It's free, ad-free and completely useless."
89 | end
90 | entry do
91 | title "StackOverflow"
92 | homepage "http://stackoverflow.com/users/735143/dabrorius"
93 | end
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/jobless.gemspec:
--------------------------------------------------------------------------------
1 | Gem::Specification.new do |s|
2 | s.name = 'jobless'
3 | s.version = '0.2.3'
4 | s.date = Date.today.to_s
5 | s.summary = 'Generate your CV with Ruby'
6 | s.description = 'Jobless is a simple DSL for creating a CV in HTML format.'
7 | s.authors = ['Filip Defar']
8 | s.email = 'filip.defar@gmail.com'
9 | s.files = ['lib/jobless.rb',
10 | 'lib/document.rb',
11 | 'lib/group.rb',
12 | 'lib/item.rb',
13 | 'lib/errors.rb',
14 | 'lib/template/style.css',
15 | 'lib/template/template.html.erb']
16 | s.homepage = 'https://github.com/dabrorius/jobless'
17 | s.license = 'MIT'
18 |
19 | s.add_runtime_dependency 'activesupport', '~> 4.0'
20 | end
21 |
22 |
--------------------------------------------------------------------------------
/lib/document.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 | require 'active_support/core_ext/string/inflections'
3 |
4 | module Jobless
5 | class Document
6 | attr_reader :groups, :data
7 |
8 | PERSONAL_ATTRIBUTES = %w(
9 | name
10 | email
11 | location
12 | address
13 | homepage
14 | phone
15 | github
16 | blog
17 | ).freeze
18 |
19 | GROUP_NAMES = %w(
20 | employment
21 | education
22 | open_source
23 | other_experience
24 | work_experience
25 | summary
26 | skills
27 | projects
28 | awards
29 | ).freeze
30 |
31 | def initialize
32 | @data = {}
33 | @groups = []
34 | @template = File.expand_path('../template/template.html.erb', __FILE__)
35 | @stylesheet = File.expand_path('../template/style.css', __FILE__)
36 | end
37 |
38 | # Define methods for setting personal data
39 | PERSONAL_ATTRIBUTES.each do |attribute_name|
40 | define_method(attribute_name) do |attribute = nil|
41 | if attribute
42 | @data[attribute_name.to_sym] = attribute
43 | else
44 | @data[attribute_name.to_sym]
45 | end
46 | end
47 | end
48 |
49 | def group(name, &block)
50 | group = Group.new(name)
51 | group.instance_eval(&block)
52 | @groups.push(group)
53 | end
54 |
55 | GROUP_NAMES.each do |group_name|
56 | define_method(group_name) do |&block|
57 | group(group_name.titleize, &block)
58 | end
59 | end
60 |
61 | def template(template)
62 | @template = template
63 | end
64 |
65 | def stylesheet(file_path)
66 | @stylesheet = file_path
67 | end
68 |
69 | def css
70 | File.read(@stylesheet)
71 | end
72 |
73 | def write_to_file(filename)
74 | renderer = ERB.new(File.read(@template))
75 | generated_html = renderer.result(binding)
76 | File.open(filename, 'w') do |file|
77 | file.write(generated_html)
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/errors.rb:
--------------------------------------------------------------------------------
1 | module Jobless
2 | module Error
3 | class GitHubApi < StandardError; end
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/group.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/core_ext/string/inflections'
2 | require 'net/http'
3 | require 'json'
4 |
5 | module Jobless
6 | class Group
7 | attr_reader :items, :name, :type
8 |
9 | def initialize(name)
10 | @name = name
11 | @type = name.parameterize
12 | @items = []
13 | end
14 |
15 | def item(&block)
16 | item = Item.new
17 | item.instance_eval(&block)
18 | @items.push(item)
19 | end
20 |
21 | alias entry item
22 |
23 | def github_repo(url, &block)
24 | repo_name = url.match(/([^\/]*)\/([^\/]*)$/).captures.join('/')
25 | repo_data = fetch_github_repo_data(repo_name)
26 | item = Item.new
27 | item.instance_eval do
28 | title repo_data['name']
29 | description repo_data['description']
30 | homepage repo_data['html_url']
31 | end
32 | item.instance_eval(&block) if block_given?
33 | @items.push item
34 | end
35 |
36 | private
37 |
38 | def fetch_github_repo_data(repo_name)
39 | url = "https://api.github.com/repos/#{repo_name}"
40 | uri = URI.parse(url)
41 | response = Net::HTTP.get_response(uri)
42 | if response.code == '200'
43 | JSON.parse(response.body)
44 | else
45 | fail(Error::GitHubApi, 'GitHub API responded with an unexpected '\
46 | "status: '#{response.code}', while trying to fetch following "\
47 | "repository: '#{repo_name}'.")
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/item.rb:
--------------------------------------------------------------------------------
1 | module Jobless
2 | class Item
3 | attr_reader :data, :bulletins
4 |
5 | PERSONAL_ATTRIBUTES = %w(
6 | title
7 | company
8 | homepage
9 | technologies
10 | description
11 | start_date
12 | end_date
13 | ).freeze
14 |
15 | def initialize
16 | @data = {}
17 | @bulletins = []
18 | end
19 |
20 | # Define methods for setting personal data
21 | PERSONAL_ATTRIBUTES.each do |attribute_name|
22 | define_method(attribute_name) do |attribute = nil|
23 | if attribute
24 | @data[attribute_name.to_sym] = attribute
25 | else
26 | @data[attribute_name.to_sym]
27 | end
28 | end
29 | end
30 |
31 | def bulletin(content)
32 | @bulletins.push content
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/jobless.rb:
--------------------------------------------------------------------------------
1 | require_relative 'document'
2 | require_relative 'group'
3 | require_relative 'item'
4 | require_relative 'errors'
5 |
6 | module Jobless
7 | def self.cv(filename = 'cv.html', &block)
8 | instance = Document.new
9 | instance.instance_eval(&block)
10 | instance.write_to_file(filename)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/template/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: #333;
3 | font-family: helvetica;
4 | font-size: 12px;
5 | }
6 | h1, h2, h3, .title {
7 | color: #000;
8 | font-family: helvetica;
9 | }
10 | h1 {
11 | float: left;
12 | font-size: 50px;
13 | margin-top: 20px;
14 | }
15 | h3 {
16 | font-size: 16px;
17 | margin-top: 0;
18 | }
19 | a, a:active, a:link, a:hover, a:visited {
20 | color: #444499;
21 | }
22 | ul {
23 | padding-left: 1em;
24 | }
25 | #base {
26 | padding: 2em;
27 | }
28 | .technologies {
29 | font-size: 14px;
30 | font-style: italic;
31 | }
32 | .time-span {
33 | position: absolute;
34 | right: 0;
35 | top: 0;
36 | font-size: 12px;
37 | }
38 | .item {
39 | border-bottom: 1px solid #ddd;
40 | margin-bottom: 1em;
41 | position: relative;
42 | }
43 | .homepage {
44 | font-size: 14px;
45 | }
46 | .personal-info {
47 | float: right;
48 | margin-top: 20px;
49 | text-align: right;
50 | }
51 | .flexbox {
52 | page-break-inside: avoid;
53 | display: -ms-flex;
54 | display: -webkit-flex;
55 | display: flex;
56 | margin-top: 20px;
57 | padding-top: 10px;
58 | }
59 | .group-title {
60 | width: 20%;
61 | padding: 10px;
62 | }
63 | .group {
64 | width: 80%;
65 | padding: 10px;
66 | }
67 | @media screen {
68 | #base {
69 | margin-left: auto;
70 | margin-right: auto;
71 | max-width: 850px;
72 | background-color: #fff;
73 | }
74 | body {
75 | background-color: #222;
76 | font-size: 16px;
77 | }
78 | h3 {
79 | font-size: 20px;
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/lib/template/template.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | CV
4 |
5 |
8 |
9 |
10 |
11 |
<%= name %>
12 |
13 | <% if email %><%= email %><% end %>
14 | <% if location %><%= location %><% end %>
15 | <% if address %><%= address %><% end %>
16 | <% if phone %><%= phone %><% end %>
17 | <% if homepage %>
<%= homepage %><% end %>
18 | <% if github %>
<%= github %><% end %>
19 | <% if blog %>
<%= blog %><% end %>
20 |
21 |
22 |
23 | <% groups.each do |group| %>
24 |
25 |
26 |
<%= group.name %>
27 |
28 |
29 | <% group.items.each do |item| %>
30 |
31 |
<%= item.title %>
32 | <% if item.company %> - <%= item.company %> <% end %>
33 | <% if item.homepage %>
34 |
37 | <% end %>
38 | <% if item.technologies %>
39 |
40 | <%= item.technologies %>
41 |
42 | <% end %>
43 |
<%= item.description %>
44 | <% if item.bulletins %>
45 |
46 | <% item.bulletins.each do |bulletin| %>
47 | - <%= bulletin %>
48 | <% end %>
49 |
50 | <% end %>
51 |
52 | <%= item.start_date %>
53 | <% if item.end_date %> - <%= item.end_date %> <% end %>
54 |
55 |
56 | <% end %>
57 |
58 |
59 | <% end %>
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/spec/document_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'document'
3 | require 'group'
4 |
5 | describe Jobless::Document do
6 | let(:document) { Jobless::Document.new }
7 |
8 | describe '#name' do
9 | it 'stores correct value' do
10 | document.name 'John Doe'
11 | expect(document.data).to eq(name: 'John Doe')
12 | expect(document.name).to eq('John Doe')
13 | end
14 | end
15 |
16 | describe '#location' do
17 | it 'stores correct value' do
18 | document.location 'Zagreb, Croatia'
19 | expect(document.data).to eq(location: 'Zagreb, Croatia')
20 | expect(document.location).to eq('Zagreb, Croatia')
21 | end
22 | end
23 |
24 | describe '#address' do
25 | it 'stores correct value' do
26 | document.address 'Blue Street 72b'
27 | expect(document.data).to eq(address: 'Blue Street 72b')
28 | expect(document.address).to eq('Blue Street 72b')
29 | end
30 | end
31 |
32 | describe '#homepage' do
33 | it 'stores correct value' do
34 | document.homepage 'http://google.com'
35 | expect(document.data).to eq(homepage: 'http://google.com')
36 | expect(document.homepage).to eq('http://google.com')
37 | end
38 | end
39 |
40 | describe '#email' do
41 | it 'stores correct value' do
42 | document.email 'email@example.com'
43 | expect(document.data).to eq(email: 'email@example.com')
44 | expect(document.email).to eq('email@example.com')
45 | end
46 | end
47 |
48 | describe '#phone' do
49 | it 'stores correct value' do
50 | document.phone '1-123-456-789'
51 | expect(document.data).to eq(phone: '1-123-456-789')
52 | expect(document.phone).to eq('1-123-456-789')
53 | end
54 | end
55 |
56 | describe '#github' do
57 | it 'stores correct value' do
58 | document.github 'https://github.com/johndoe'
59 | expect(document.data).to eq(github: 'https://github.com/johndoe')
60 | expect(document.github).to eq('https://github.com/johndoe')
61 | end
62 | end
63 |
64 | describe '#blog' do
65 | it 'stores correct value' do
66 | document.blog 'http://blog.example.com'
67 | expect(document.data).to eq(blog: 'http://blog.example.com')
68 | expect(document.blog).to eq('http://blog.example.com')
69 | end
70 | end
71 |
72 | describe '#group' do
73 | it 'creates a new group' do
74 | expect do
75 | document.group 'Experience' do
76 | end
77 | end.to change { document.groups.count }.from(0).to(1)
78 | expect(document.groups[0].name).to eq 'Experience'
79 | end
80 | end
81 |
82 | describe '#employment' do
83 | it 'calls #group with appropriate parameters' do
84 | expect(document).to receive(:group).
85 | with("Employment").and_yield
86 | document.employment do
87 | end
88 | end
89 | end
90 |
91 | describe '#education' do
92 | it 'calls #group with appropriate parameters' do
93 | expect(document).to receive(:group).
94 | with("Education").and_yield
95 | document.education do
96 | end
97 | end
98 | end
99 |
100 | describe '#open_source' do
101 | it 'calls #group with appropriate parameters' do
102 | expect(document).to receive(:group).
103 | with("Open Source").and_yield
104 | document.open_source do
105 | end
106 | end
107 | end
108 |
109 | describe '#other_experience' do
110 | it 'calls #group with appropriate parameters' do
111 | expect(document).to receive(:group).
112 | with("Other Experience").and_yield
113 | document.other_experience do
114 | end
115 | end
116 | end
117 |
118 | describe '#summary' do
119 | it 'calls #group with appropriate parameters' do
120 | expect(document).to receive(:group).
121 | with("Summary").and_yield
122 | document.summary do
123 | end
124 | end
125 | end
126 |
127 | describe '#skills' do
128 | it 'calls #group with appropriate parameters' do
129 | expect(document).to receive(:group).
130 | with("Skills").and_yield
131 | document.skills do
132 | end
133 | end
134 | end
135 | describe '#projects' do
136 | it 'calls #group with appropriate parameters' do
137 | expect(document).to receive(:group).
138 | with("Projects").and_yield
139 | document.projects do
140 | end
141 | end
142 | end
143 | describe '#awards' do
144 | it 'calls #group with appropriate parameters' do
145 | expect(document).to receive(:group).
146 | with("Awards").and_yield
147 | document.awards do
148 | end
149 | end
150 | end
151 | describe '#work_experience' do
152 | it 'calls #group with appropriate parameters' do
153 | expect(document).to receive(:group).
154 | with("Work Experience").and_yield
155 | document.work_experience do
156 | end
157 | end
158 | end
159 | end
160 |
--------------------------------------------------------------------------------
/spec/fixtures/custom_style.css:
--------------------------------------------------------------------------------
1 | body {
2 | border: 1px solid black;
3 | }
4 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_cassettes/github_invalid.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://api.github.com/repos/dabrorius/jobles
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | Accept-Encoding:
11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12 | Accept:
13 | - "*/*"
14 | User-Agent:
15 | - Ruby
16 | Host:
17 | - api.github.com
18 | response:
19 | status:
20 | code: 404
21 | message: Not Found
22 | headers:
23 | Server:
24 | - GitHub.com
25 | Date:
26 | - Tue, 24 Nov 2015 14:20:08 GMT
27 | Content-Type:
28 | - application/json; charset=utf-8
29 | Transfer-Encoding:
30 | - chunked
31 | Status:
32 | - 404 Not Found
33 | X-Ratelimit-Limit:
34 | - '60'
35 | X-Ratelimit-Remaining:
36 | - '42'
37 | X-Ratelimit-Reset:
38 | - '1448374931'
39 | X-Github-Media-Type:
40 | - github.v3
41 | X-Xss-Protection:
42 | - 1; mode=block
43 | X-Frame-Options:
44 | - deny
45 | Content-Security-Policy:
46 | - default-src 'none'
47 | Access-Control-Allow-Credentials:
48 | - 'true'
49 | Access-Control-Expose-Headers:
50 | - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
51 | X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
52 | Access-Control-Allow-Origin:
53 | - "*"
54 | Strict-Transport-Security:
55 | - max-age=31536000; includeSubdomains; preload
56 | X-Content-Type-Options:
57 | - nosniff
58 | X-Github-Request-Id:
59 | - 8D8A0B5A:7FBD:1563A1EB:56547218
60 | body:
61 | encoding: ASCII-8BIT
62 | string: '{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}'
63 | http_version:
64 | recorded_at: Tue, 24 Nov 2015 14:20:08 GMT
65 | recorded_with: VCR 2.9.3
66 |
--------------------------------------------------------------------------------
/spec/fixtures/vcr_cassettes/github_jobless.yml:
--------------------------------------------------------------------------------
1 | ---
2 | http_interactions:
3 | - request:
4 | method: get
5 | uri: https://api.github.com/repos/dabrorius/jobless
6 | body:
7 | encoding: US-ASCII
8 | string: ''
9 | headers:
10 | Accept-Encoding:
11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12 | Accept:
13 | - "*/*"
14 | User-Agent:
15 | - Ruby
16 | Host:
17 | - api.github.com
18 | response:
19 | status:
20 | code: 200
21 | message: OK
22 | headers:
23 | Server:
24 | - GitHub.com
25 | Date:
26 | - Tue, 24 Nov 2015 14:00:16 GMT
27 | Content-Type:
28 | - application/json; charset=utf-8
29 | Transfer-Encoding:
30 | - chunked
31 | Status:
32 | - 200 OK
33 | X-Ratelimit-Limit:
34 | - '60'
35 | X-Ratelimit-Remaining:
36 | - '46'
37 | X-Ratelimit-Reset:
38 | - '1448374931'
39 | Cache-Control:
40 | - public, max-age=60, s-maxage=60
41 | Last-Modified:
42 | - Thu, 05 Nov 2015 23:36:06 GMT
43 | Etag:
44 | - W/"d07d8419c676c456123d8d9ec7eb6ca5"
45 | Vary:
46 | - Accept
47 | - Accept-Encoding
48 | X-Github-Media-Type:
49 | - github.v3
50 | X-Xss-Protection:
51 | - 1; mode=block
52 | X-Frame-Options:
53 | - deny
54 | Content-Security-Policy:
55 | - default-src 'none'
56 | Access-Control-Allow-Credentials:
57 | - 'true'
58 | Access-Control-Expose-Headers:
59 | - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
60 | X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
61 | Access-Control-Allow-Origin:
62 | - "*"
63 | Strict-Transport-Security:
64 | - max-age=31536000; includeSubdomains; preload
65 | X-Content-Type-Options:
66 | - nosniff
67 | X-Served-By:
68 | - 76d9828c7e4f1d910f7ba069e90ce976
69 | X-Github-Request-Id:
70 | - 8D8A0B5A:1AD5:2C89F8EC:56546D70
71 | body:
72 | encoding: ASCII-8BIT
73 | string: '{"id":42043935,"name":"jobless","full_name":"dabrorius/jobless","owner":{"login":"dabrorius","id":690113,"avatar_url":"https://avatars.githubusercontent.com/u/690113?v=3","gravatar_id":"","url":"https://api.github.com/users/dabrorius","html_url":"https://github.com/dabrorius","followers_url":"https://api.github.com/users/dabrorius/followers","following_url":"https://api.github.com/users/dabrorius/following{/other_user}","gists_url":"https://api.github.com/users/dabrorius/gists{/gist_id}","starred_url":"https://api.github.com/users/dabrorius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dabrorius/subscriptions","organizations_url":"https://api.github.com/users/dabrorius/orgs","repos_url":"https://api.github.com/users/dabrorius/repos","events_url":"https://api.github.com/users/dabrorius/events{/privacy}","received_events_url":"https://api.github.com/users/dabrorius/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dabrorius/jobless","description":"A
74 | ruby DSL for generating CVs.","fork":false,"url":"https://api.github.com/repos/dabrorius/jobless","forks_url":"https://api.github.com/repos/dabrorius/jobless/forks","keys_url":"https://api.github.com/repos/dabrorius/jobless/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dabrorius/jobless/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dabrorius/jobless/teams","hooks_url":"https://api.github.com/repos/dabrorius/jobless/hooks","issue_events_url":"https://api.github.com/repos/dabrorius/jobless/issues/events{/number}","events_url":"https://api.github.com/repos/dabrorius/jobless/events","assignees_url":"https://api.github.com/repos/dabrorius/jobless/assignees{/user}","branches_url":"https://api.github.com/repos/dabrorius/jobless/branches{/branch}","tags_url":"https://api.github.com/repos/dabrorius/jobless/tags","blobs_url":"https://api.github.com/repos/dabrorius/jobless/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dabrorius/jobless/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dabrorius/jobless/git/refs{/sha}","trees_url":"https://api.github.com/repos/dabrorius/jobless/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dabrorius/jobless/statuses/{sha}","languages_url":"https://api.github.com/repos/dabrorius/jobless/languages","stargazers_url":"https://api.github.com/repos/dabrorius/jobless/stargazers","contributors_url":"https://api.github.com/repos/dabrorius/jobless/contributors","subscribers_url":"https://api.github.com/repos/dabrorius/jobless/subscribers","subscription_url":"https://api.github.com/repos/dabrorius/jobless/subscription","commits_url":"https://api.github.com/repos/dabrorius/jobless/commits{/sha}","git_commits_url":"https://api.github.com/repos/dabrorius/jobless/git/commits{/sha}","comments_url":"https://api.github.com/repos/dabrorius/jobless/comments{/number}","issue_comment_url":"https://api.github.com/repos/dabrorius/jobless/issues/comments{/number}","contents_url":"https://api.github.com/repos/dabrorius/jobless/contents/{+path}","compare_url":"https://api.github.com/repos/dabrorius/jobless/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dabrorius/jobless/merges","archive_url":"https://api.github.com/repos/dabrorius/jobless/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dabrorius/jobless/downloads","issues_url":"https://api.github.com/repos/dabrorius/jobless/issues{/number}","pulls_url":"https://api.github.com/repos/dabrorius/jobless/pulls{/number}","milestones_url":"https://api.github.com/repos/dabrorius/jobless/milestones{/number}","notifications_url":"https://api.github.com/repos/dabrorius/jobless/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dabrorius/jobless/labels{/name}","releases_url":"https://api.github.com/repos/dabrorius/jobless/releases{/id}","created_at":"2015-09-07T09:28:31Z","updated_at":"2015-11-05T23:36:06Z","pushed_at":"2015-11-10T15:37:30Z","git_url":"git://github.com/dabrorius/jobless.git","ssh_url":"git@github.com:dabrorius/jobless.git","clone_url":"https://github.com/dabrorius/jobless.git","svn_url":"https://github.com/dabrorius/jobless","homepage":"","size":345,"stargazers_count":143,"watchers_count":143,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":12,"mirror_url":null,"open_issues_count":5,"forks":12,"open_issues":5,"watchers":143,"default_branch":"master","network_count":12,"subscribers_count":3}'
75 | http_version:
76 | recorded_at: Tue, 24 Nov 2015 14:00:16 GMT
77 | recorded_with: VCR 2.9.3
78 |
--------------------------------------------------------------------------------
/spec/group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'errors'
3 | require 'group'
4 | require 'item'
5 |
6 | describe Jobless::Group do
7 | subject(:group) { Jobless::Group.new("Other experience") }
8 |
9 | describe '#initialize' do
10 | context 'infers type from name' do
11 | it { expect(group.type).to eq('other-experience') }
12 | end
13 | end
14 |
15 | describe '#item' do
16 | it 'creates a new item' do
17 | expect do
18 | group.item do
19 | end
20 | end.to change { group.items.count }.from(0).to(1)
21 | end
22 | end
23 |
24 | describe '#github_repo' do
25 | let(:new_item) { group.items.first }
26 | let(:repository_url) { 'https://github.com/dabrorius/jobless' }
27 |
28 | context 'when a URL is given' do
29 | around { |example| VCR.use_cassette("github_jobless") { example.run } }
30 |
31 | it 'creates a new item' do
32 | expect { group.github_repo repository_url }.
33 | to change { group.items.count }.from(0).to(1)
34 | end
35 |
36 | it "fills item's attributes with data from GitHub API" do
37 | group.github_repo repository_url
38 | expect(new_item.title).to eq('jobless')
39 | expect(new_item.description).to eq('A ruby DSL for generating CVs.')
40 | expect(new_item.homepage).to eq('https://github.com/dabrorius/jobless')
41 | end
42 |
43 | it 'allows user to override fetched attributes' do
44 | group.github_repo repository_url do
45 | title "Different title"
46 | end
47 | expect(new_item.title).to eq('Different title')
48 | expect(new_item.description).to eq('A ruby DSL for generating CVs.')
49 | expect(new_item.homepage).to eq(repository_url)
50 | end
51 |
52 | it 'allows adding of new attributes' do
53 | group.github_repo repository_url do
54 | technologies "Ruby"
55 | end
56 | expect(new_item.technologies).to eq('Ruby')
57 | end
58 | end
59 |
60 | context 'when a repository name is given' do
61 | around { |example| VCR.use_cassette("github_jobless") { example.run } }
62 |
63 | it 'creates a new item' do
64 | expect do
65 | group.github_repo 'dabrorius/jobless'
66 | end.to change { group.items.count }.from(0).to(1)
67 | end
68 |
69 | it "fills item's attributes with data from GitHub API" do
70 | group.github_repo 'dabrorius/jobless'
71 | expect(new_item.title).to eq('jobless')
72 | expect(new_item.description).to eq('A ruby DSL for generating CVs.')
73 | expect(new_item.homepage).to eq(repository_url)
74 | end
75 | end
76 |
77 | context 'when invalid url is given' do
78 | around { |example| VCR.use_cassette("github_invalid") { example.run } }
79 |
80 | it 'raises an error' do
81 | expect do
82 | group.github_repo 'https://github.com/dabrorius/jobles'
83 | end.to raise_exception(Jobless::Error::GitHubApi)
84 | end
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/spec/item_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'item'
3 |
4 | describe Jobless::Item do
5 | let(:item) { Jobless::Item.new }
6 |
7 | describe '#title' do
8 | it 'stores correct value' do
9 | item.title 'My first job'
10 | expect(item.data).to eq(title: 'My first job')
11 | expect(item.title).to eq('My first job')
12 | end
13 | end
14 |
15 | describe '#company' do
16 | it 'stores correct value' do
17 | item.company 'GitHub'
18 | expect(item.data).to eq(company: 'GitHub')
19 | expect(item.company).to eq('GitHub')
20 | end
21 | end
22 |
23 | describe '#homepage' do
24 | it 'stores correct value' do
25 | item.homepage 'http://google.com'
26 | expect(item.data).to eq(homepage: 'http://google.com')
27 | expect(item.homepage).to eq('http://google.com')
28 | end
29 | end
30 |
31 | describe '#technologies' do
32 | it 'stores correct value' do
33 | item.technologies 'Ruby, COBOL'
34 | expect(item.data).to eq(technologies: 'Ruby, COBOL')
35 | expect(item.technologies).to eq('Ruby, COBOL')
36 | end
37 | end
38 |
39 | describe '#description' do
40 | it 'stores correct value' do
41 | item.description 'Lorem ipsum dolor sem.'
42 | expect(item.data).to eq(description: 'Lorem ipsum dolor sem.')
43 | expect(item.description).to eq('Lorem ipsum dolor sem.')
44 | end
45 | end
46 |
47 | describe '#start_date' do
48 | it 'stores correct value' do
49 | item.start_date 'June 2014'
50 | expect(item.data).to eq(start_date: 'June 2014')
51 | expect(item.start_date).to eq('June 2014')
52 | end
53 | end
54 |
55 | describe '#end_date' do
56 | it 'stores correct value' do
57 | item.end_date 'July 2015'
58 | expect(item.data).to eq(end_date: 'July 2015')
59 | expect(item.end_date).to eq('July 2015')
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/jobless_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'jobless'
3 |
4 | describe Jobless do
5 | let(:file_content) { File.read('test_cv.html') }
6 | after { File.delete('test_cv.html') }
7 |
8 | describe "personal data" do
9 | before do
10 | Jobless.cv('test_cv.html') do
11 | name 'John doe'
12 | location 'Zagreb'
13 | address 'Candy Street 9'
14 | homepage 'http://google.com'
15 | email 'mail@example.com'
16 | phone '1-123-456-789'
17 | github 'https://github.com/johndoe'
18 | blog 'http://blog.example.com'
19 | end
20 | end
21 |
22 | it "embeds name" do
23 | expect(file_content).to include('John doe')
24 | end
25 |
26 | it "embeds location" do
27 | expect(file_content).to include('Zagreb')
28 | end
29 |
30 | it "embeds address" do
31 | expect(file_content).to include('Candy Street 9')
32 | end
33 |
34 | it "embeds homepage" do
35 | expect(file_content).to include('http://google.com')
36 | end
37 |
38 | it "embeds email" do
39 | expect(file_content).to include('mail@example.com')
40 | end
41 |
42 | it "embeds phone" do
43 | expect(file_content).to include('1-123-456-789')
44 | end
45 | it "embeds github" do
46 | expect(file_content).to include('https://github.com/johndoe')
47 | end
48 | it "embeds blog" do
49 | expect(file_content).to include('http://blog.example.com')
50 | end
51 | end
52 |
53 | describe "groups" do
54 | before do
55 | Jobless.cv('test_cv.html') do
56 | group "Example group" do
57 | entry do
58 | title "Entry name"
59 | company "GitHub"
60 | technologies "Ruby, Rails"
61 | description "Lorem ipsum dolor sem"
62 | bulletin "Bulletin 1"
63 | bulletin "Bulletin 2"
64 | start_date "June 2014"
65 | end_date "August 2015"
66 | end
67 | end
68 | end
69 | end
70 |
71 | it "embeds group name" do
72 | expect(file_content).to include('Example group')
73 | end
74 |
75 | it "embeds entry title" do
76 | expect(file_content).to include('Entry name')
77 | end
78 |
79 | it "embeds company" do
80 | expect(file_content).to include('GitHub')
81 | end
82 |
83 | it "embeds technologies" do
84 | expect(file_content).to include('Ruby, Rails')
85 | end
86 |
87 | it "embeds description" do
88 | expect(file_content).to include('Lorem ipsum dolor sem')
89 | end
90 |
91 | it "embeds bulletins" do
92 | expect(file_content).to include('Bulletin 1')
93 | expect(file_content).to include('Bulletin 2')
94 | end
95 |
96 | it "embeds start date" do
97 | expect(file_content).to include('June 2014')
98 | end
99 |
100 | it "embeds end date" do
101 | expect(file_content).to include('August 2015')
102 | end
103 | end
104 |
105 | describe "custom CSS" do
106 | before do
107 | Jobless.cv('test_cv.html') do
108 | stylesheet File.expand_path("../fixtures/custom_style.css", __FILE__)
109 | end
110 | end
111 | it "embeds specified stylesheet" do
112 | expect(file_content).to include('border: 1px solid black;')
113 | end
114 | end
115 | end
116 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'webmock'
2 | require 'vcr'
3 |
4 | VCR.configure do |config|
5 | config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
6 | config.hook_into :webmock
7 | end
8 |
--------------------------------------------------------------------------------
/test_examples/small.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/jobless'
2 |
3 | Jobless.cv "small.html" do
4 | name "Filip Defar"
5 | email "filip.defar@gmail.com"
6 | location "Zagreb, Croatia"
7 | address "Naserov Trg 4"
8 | homepage "http://dabrorius.github.io"
9 |
10 | employment do
11 | entry do
12 | title "Full-stack Rails developer"
13 | company "DirectVest"
14 | technologies "Ruby, Ruby on Rails, Javascript"
15 | bulletin "Created a wrapper for Folio Institutional API."
16 | bulletin "Created a full test suite for existing features."
17 | bulletin "Refactored the application (From 1.5 to 3.9+ GPA on CodeClimate)."
18 | bulletin "Created multistep forms for the investment process."
19 | bulletin "Created a dashboard that heavily relies on external APIs "\
20 | "as well as low level caching for increased performance."
21 | start_date "June 2015"
22 | end_date "November 2015"
23 | end
24 | entry do
25 | title "Full-stack Rails developer"
26 | company "BizRevr"
27 | technologies "Ruby, Ruby on Rails, Elasticsearch"
28 | bulletin "Helping design and develop new features. "
29 | bulletin "Created an interface for bulk editing records similar to excel "\
30 | "spreadsheet."
31 | start_date "April 2015"
32 | end_date "June 2015"
33 | end
34 | end
35 |
36 | education do
37 | entry do
38 | title "M.S. Computer Science"
39 | company "The University of Zagreb"
40 | start_date "2011"
41 | end_date "2013"
42 | end
43 | entry do
44 | title "B.S. Software Engineering"
45 | company "The University of Zagreb"
46 | start_date "2007"
47 | end_date "2011"
48 | end
49 | end
50 |
51 | other_experience do
52 | entry do
53 | title "CatMe iOS App"
54 | homepage "https://itunes.apple.com/us/app/id994659934"
55 | description "CatMe is a simple native mobile app that allows you to "\
56 | "virtually pet cats! Move your finger over a cat photo and you will "\
57 | "hear it purr. It's free, ad-free and completely useless."
58 | end
59 | entry do
60 | title "StackOverflow"
61 | homepage "http://stackoverflow.com/users/735143/dabrorius"
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------