├── .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/markov­noodles" 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/ruby­grape/grape" 87 | description "I am a contributer to grape an opinionated micro­framework "\ 88 | "for creating REST­like 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, ad­free 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 | --------------------------------------------------------------------------------