├── .gitignore ├── .ruby-version ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── TODO.md ├── bin └── publish ├── config.rb ├── config.ru ├── data ├── book.yml └── toc.yml ├── renumber.rb └── source ├── 01-preface.md ├── 02-creation.md ├── 03-learning_to_program.md ├── 03-learning_to_program ├── 01-learning_modes.md ├── 02-dont_believe.md ├── 03-formatting_code.md ├── 04-error_messages.md └── 05-using_google.md ├── 04-your_tools.md ├── 04-your_tools ├── 01-text_editor.md ├── 02-terminal.md ├── 03-ruby_runtime.md ├── 04-workflow.md └── 05-irb.md ├── 05-roadmap.md ├── 06-object_oriented_programming.md ├── 07-variables.md ├── 07-variables ├── 01-reusing_names.md └── 02-right_goes_first.md ├── 08-built_in_classes.md ├── 08-built_in_classes ├── 01-numbers.md ├── 02-strings.md ├── 03-true_false_nil.md ├── 04-symbols.md ├── 05-arrays.md └── 06-hashes.md ├── 09-objects.md ├── 09-objects ├── 01-classes.md ├── 02-instances.md ├── 03-methods.md ├── 04-calling.md ├── 05-arguments.md ├── 06-listing.md ├── 07-predicates.md ├── 08-bangs.md ├── 09-class_methods.md └── 20-notes.md ├── 10-writing_methods.md ├── 10-writing_methods ├── 01-constituents.md ├── 02-definition.md ├── 03-usage.md ├── 04-return_values.md ├── 05-scopes.md ├── 06-combining.md ├── 07-printing.md └── 20-control_flow.md ├── 11-writing_classes.md ├── 11-writing_classes ├── 01-definition.md ├── 02-methods.md ├── 03-initializers.md ├── 04-instance_variables.md ├── 05-attribute_readers.md ├── 06-attribute_writers.md ├── 07-state_and_behaviour.md ├── 08-interaction.md └── 09-self.md ├── 13-blocks.md ├── 13-blocks ├── 01-syntax.md ├── 02-arguments.md ├── 03-return_values.md ├── 04-ioc.md ├── 05-iterators.md └── 10-methods_using_blocks.md ├── 14-conditionals.md ├── 14-conditionals ├── 01-shorthand.md ├── 02-return_values.md └── 05-nothing_and_truth.md ├── 15-operators.md ├── 15-operators ├── 01-arithmetical.md ├── 02-logical.md ├── 03-comparison.md └── 04-methods.md ├── 70-bonus.md ├── 70-bonus ├── 00-string_interpolation.md ├── 01-top_level.md ├── 03-other_methods.md ├── 04-questions_commands.md ├── 10-alternative-syntax.md ├── 11-good_names.md ├── 12-write_new_method.md ├── 13-arguments_parameters.md └── 14-parentheses.md ├── 80-advanced.md ├── 80-advanced ├── 01-libraries.md ├── 02-modules.md ├── 03-private_methods.md ├── 04-procs.md ├── 05-yield.md └── 06-regular_expressions.md ├── 90-exercises.md ├── 90-exercises ├── 01-numbers.md ├── 02-strings.md ├── 03-arrays_1.md ├── 04-hashes_1.md ├── 05-methods_1.md ├── 06-arrays_2.md ├── 07-nested_arrays.md ├── 08-hashes_2.md ├── 09-truthiness.md ├── 10-email.md ├── 11-mailbox.md ├── 12-mailbox_text.md ├── 13-mailbox_html.md ├── 14-mailbox_file.md └── 15-mailbox_csv.md ├── 91-exercises_2.md ├── 91-exercises_2 ├── 01-numbers.md ├── 02-strings.md ├── 03-arrays_1.md ├── 04-hashes_1.md └── 99-notes.md ├── CNAME ├── assets ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── images │ └── favicon.png ├── javascripts │ ├── modernizr.js │ └── monstas.js └── stylesheets │ ├── _fonts.scss │ ├── _html5-reset.css │ ├── _layout.scss │ ├── _print.scss │ ├── _response.scss │ ├── _style.scss │ ├── monstas.scss │ └── syntax.css.erb ├── index.md ├── layouts └── layout.erb ├── print.erb ├── robots.txt ├── sitemap.xml.erb ├── solutions ├── 02-strings-3.html.md ├── 02-strings-4.html.md ├── 02-strings-5.html.md ├── 02-strings-6.html.md ├── 03-arrays_1-1.html.md ├── 03-arrays_1-2.html.md ├── 03-arrays_1-3.html.md ├── 03-arrays_1-4.html.md ├── 03-arrays_1-5.html.md ├── 04-hashes_1-1.html.md ├── 04-hashes_1-2.html.md ├── 04-hashes_1-3.html.md ├── 04-hashes_1-4.html.md ├── 04-hashes_1-5.html.md ├── 05-methods-1.html.md ├── 05-methods-2.html.md ├── 05-methods-3.html.md ├── 05-methods-4.html.md ├── 06-arrays_2-1.html.md ├── 06-arrays_2-2.html.md ├── 06-arrays_2-3.html.md ├── 06-arrays_2-4.html.md ├── 06-arrays_2-5.html.md ├── 06-arrays_2-6.html.md ├── 06-arrays_2-7.html.md ├── 07-nested_arrays-1.html.md ├── 07-nested_arrays-2.html.md ├── 08-hashes_2-1.html.md ├── 08-hashes_2-2.html.md ├── 08-hashes_2-3.html.md ├── 08-hashes_2-4.html.md ├── 09-truthiness-1.html.md ├── 09-truthiness-2.html.md ├── 10-email-1.html.md ├── 10-email-2.html.md ├── 11-mailbox-1.html.md ├── 12-mailbox_text-1.html.md ├── 13-mailbox_html-1.html.md ├── 14-mailbox_file-1.html.md └── 15-mailbox_csv-1.html.md └── solutions_2 ├── 01-numbers-5.html ├── 02-strings-1.html.md ├── 02-strings-3.html.md ├── 02-strings-4.html.md ├── 03-arrays_1-1.html.md ├── 03-arrays_1-2.html.md ├── 03-arrays_1-3.html.md ├── 03-arrays_1-4.html.md ├── 03-arrays_1-5.html.md ├── 03-arrays_1-6.html.md ├── 03-arrays_1-7.html.md ├── 04-hashes_1-1.html.md ├── 04-hashes_1-2.html.md ├── 04-hashes_1-3.html.md ├── 04-hashes_1-4.html.md ├── 04-hashes_1-5.html.md ├── 05-methods-1.html.md ├── 05-methods-2.html.md ├── 05-methods-3.html.md ├── 05-methods-4.html.md ├── 06-arrays_2-1.html.md ├── 06-arrays_2-2.html.md ├── 06-arrays_2-3.html.md ├── 06-arrays_2-4.html.md ├── 06-arrays_2-5.html.md ├── 06-arrays_2-6.html.md ├── 06-arrays_2-7.html.md ├── 07-nested_arrays-1.html.md ├── 07-nested_arrays-2.html.md ├── 08-hashes_2-1.html.md ├── 08-hashes_2-2.html.md ├── 08-hashes_2-3.html.md ├── 08-hashes_2-4.html.md ├── 09-truthiness-1.html.md ├── 09-truthiness-2.html.md ├── 10-email-1.html.md ├── 10-email-2.html.md ├── 11-mailbox-1.html.md ├── 12-mailbox_text-1.html.md ├── 13-mailbox_html-1.html.md ├── 14-mailbox_file-1.html.md └── 15-mailbox_csv-1.html.md /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | vendor/bundle 3 | build 4 | .sass-cache 5 | .DS_Store 6 | numbers.rb 7 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | script: ./bin/publish 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'middleman', '3.3.7' 4 | gem 'middleman-syntax', '2.0.0' 5 | gem 'middleman-toc' 6 | # gem 'middleman-livereload' 7 | gem 'redcarpet' 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby For Beginners 2 | 3 | *Ruby Monday Study Group curriculum for beginners* 4 | 5 | You can [read it online here](http://ruby-for-beginners.rubymonstas.org). 6 | 7 | This book is built using [middleman](http://middlemanapp.com). 8 | 9 | The source code is kept on the main branch. The `build` directory is 10 | `.gitignore`d and should be initialized with another git repository so it can 11 | be pushed to the `gh-pages` branch in order to deploy changes to the site. 12 | 13 | After making changes, once you've committed them, you can publish your 14 | changes to the website using: 15 | 16 | ``` 17 | ./bin/publish 18 | ``` 19 | 20 | License: Creative Commons Attribution-ShareAlike 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | CHAPTERS = FileList[ 2 | 'source/**/*.md' 3 | ] 4 | 5 | file 'ruby-for-beginners.pdf' => CHAPTERS do 6 | sh <<-CMD 7 | pandoc --toc --toc-depth=2 --latex-engine=xelatex -V documentclass=report -o ruby-for-beginners.pdf #{CHAPTERS} 8 | CMD 9 | end 10 | 11 | file 'ruby-for-beginners.epub' => CHAPTERS do 12 | sh <<-CMD 13 | pandoc --toc --toc-depth=2 -S --epub-metadata=data/book.yml -o ruby-for-beginners.epub #{CHAPTERS} 14 | CMD 15 | end 16 | 17 | task pdf: %W{ ruby-for-beginners.pdf } 18 | task epub: %W{ ruby-for-beginners.epub } 19 | 20 | task default: %W{ pdf epub } 21 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | An incomplete list of what's missing: 2 | 3 | * Check and re-think the order of topics. Some are Ruby language basics, some are 4 | tips and advice about tooling, searching, and general reflections on 5 | programming. We used about the same order of language basics, but then just 6 | anecdotally threw in other topics. I'm not sure how to best structure this 7 | in a book-style online resource. 8 | * Add more exercises, link to additional external exercises. 9 | * Add more topics that walk through the rest of the beginners curriculum, like 10 | writing to a file, using ERB to render a static HTML file, basics of HTTP, 11 | and building a Sinatra app that serves the same HTML. 12 | * Add a chapter about Ruby (e.g. optimized for happiness) 13 | * Add editor tips (shortcuts, syntax highlighting) 14 | * Add most common exception messages 15 | * Add a chapter about testing 16 | 17 | Notes: 18 | 19 | * Mention comments 20 | * Mention omitting curly braces for hashes on method calls 21 | * Bonus: calling methods is also referred as "sending messages", the method `send` 22 | * Bonus: default arguments 23 | * Bonus: splat operator for assignment and arguments 24 | 25 | Preface: Explain how the curriculum works (major milestones). Explain in more 26 | detail that everyone's supposed to go at their own pace, and not doing homework 27 | is completely acceptable. 28 | 29 | If local scopes are like rooms in a house, then instance variables are visible 30 | in every room in the house. 31 | 32 | 33 | In the chapter 02-creation talk about: 34 | 35 | * Writing code is creative. It is close to art, design, architecture and philosophy. 36 | * Heidegger: "language is the house of being" 37 | * all you need is a computer, internet connection and time 38 | * Building fun applications, or useful little tools (Debbie's lunch roulette) 39 | 40 | *I believe people want to express themselves when they program. They don’t want to fight with the language. Programming languages must feel natural to 41 | programmers. I tried to make people enjoy programming and concentrate on the 42 | fun and creative part of programming when they use Ruby.* - Yukihiro Matsumoto 43 | http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html 44 | 45 | 46 | Recommend to never copy and paste code, instead type it, and format it well. 47 | 48 | Move "String interpolation" up 49 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | git="git --git-dir=./build/.git --work-tree=./build" 6 | 7 | function run() { 8 | cmd=$@ 9 | echo $cmd 10 | eval "$cmd" 11 | } 12 | 13 | function setup() { 14 | run "mkdir -p build" 15 | run "$git init" 16 | run "$git remote add origin git@github.com:rubymonsters/ruby-for-beginners.git" 17 | run "$git fetch" 18 | run "$git reset --hard origin/gh-pages" 19 | } 20 | 21 | function commit() { 22 | local msg=$1 23 | run "git add -A ." 24 | run "git commit -am '$msg' || true" 25 | run "git push" 26 | } 27 | 28 | function compile() { 29 | run "bundle exec middleman build --verbose" 30 | } 31 | 32 | function push() { 33 | run "$git add -A ." 34 | run "$git commit -am 'build'" 35 | run "$git push -f origin HEAD:gh-pages" 36 | } 37 | 38 | if [ ! -d build/.git ]; then 39 | setup 40 | fi 41 | 42 | if [ -n "$1" ]; then 43 | commit "$1" 44 | fi 45 | 46 | compile 47 | push 48 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | require 'middleman_toc' 2 | 3 | set :layouts_dir, '/layouts' 4 | set :css_dir, 'assets/stylesheets' 5 | set :js_dir, 'assets/javascripts' 6 | set :images_dir, 'images' 7 | set :source, 'source' 8 | 9 | page '/sitemap.xml', layout: false 10 | page '/solutions/*', :layout => false 11 | page '/solutions_2/*', :layout => false 12 | 13 | ignore(/themes\/(?!#{data.book.theme.downcase}).*/) 14 | config.ignored_sitemap_matchers[:layout] = proc { |file| 15 | file.start_with?(File.join(config.source, 'layout.')) || file.start_with?(File.join(config.source, 'layouts/')) || !!(file =~ /themes\/.*\/layouts\//) 16 | } 17 | 18 | activate :syntax 19 | set :markdown_engine, :redcarpet 20 | set :markdown, :fenced_code_blocks => true, :smartypants => true, :no_intra_emphasis => true, :autolink => true, :strikethrough => true, :tables => true 21 | 22 | set :relative_links, false 23 | activate :relative_assets 24 | activate :minify_css 25 | activate :minify_javascript 26 | activate :asset_hash 27 | activate :toc 28 | 29 | helpers do 30 | def discover_page_title(page = current_page) 31 | if page.data.title 32 | return page.data.title # Frontmatter title 33 | elsif page.url == '/' 34 | return data.book.title 35 | elsif match = page.render(layout: false, no_images: true).match(/(.*?)<\/h1>/) 36 | return match[1] + ' | ' + data.book.title 37 | else 38 | filename = page.url.split(/\//).last.gsub('%20', ' ').titleize 39 | return filename.chomp(File.extname(filename)) + ' | ' + data.book.title 40 | end 41 | end 42 | 43 | def link_to_if_exists(*args, &block) 44 | url = args[0] 45 | 46 | resource = sitemap.find_resource_by_path(url) 47 | if resource.nil? 48 | block.call 49 | else 50 | link_to(*args, &block) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | run Middleman.server 2 | -------------------------------------------------------------------------------- /data/book.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ruby for Beginners 3 | author: Ruby Monstas 4 | github_url: https://github.com/ruby-monsters/learning_ruby 5 | domain: http://ruby-for-beginners.rubymonstas.org 6 | license_name: Attribution-ShareAlike 7 | license_url: https://creativecommons.org/licenses/by-sa/4.0 8 | theme: monstas 9 | -------------------------------------------------------------------------------- /data/toc.yml: -------------------------------------------------------------------------------- 1 | - index 2 | - preface 3 | - creation 4 | - path: learning_to_program 5 | children: 6 | - learning_modes 7 | - dont_believe 8 | - formatting_code 9 | - error_messages 10 | - using_google 11 | - path: your_tools 12 | children: 13 | - text_editor 14 | - terminal 15 | - ruby_runtime 16 | - workflow 17 | - irb 18 | - roadmap 19 | - object_oriented_programming 20 | - path: variables 21 | children: 22 | - reusing_names 23 | - right_goes_first 24 | - path: built_in_classes 25 | children: 26 | - numbers 27 | - strings 28 | - true_false_nil 29 | - symbols 30 | - arrays 31 | - hashes 32 | - path: objects 33 | children: 34 | - classes 35 | - instances 36 | - methods 37 | - calling 38 | - arguments 39 | - listing 40 | - predicates 41 | - bangs 42 | - path: writing_methods 43 | children: 44 | - constituents 45 | - definition 46 | - usage 47 | - return_values 48 | - scopes 49 | - combining 50 | - printing 51 | - path: writing_classes 52 | children: 53 | - definition 54 | - methods 55 | - initializers 56 | - instance_variables 57 | - attribute_readers 58 | - attribute_writers 59 | - state_and_behaviour 60 | - interaction 61 | - self 62 | - path: blocks 63 | children: 64 | - syntax 65 | - arguments 66 | - return_values 67 | - ioc 68 | - iterators 69 | - path: conditionals 70 | children: 71 | - nothing_and_truth 72 | - path: operators 73 | children: 74 | - arithmetical 75 | - logical 76 | - comparison 77 | - methods 78 | - path: bonus 79 | children: 80 | - string_interpolation 81 | - top_level 82 | - other_methods 83 | - questions_commands 84 | - alternative-syntax 85 | - good_names 86 | - parentheses 87 | - arguments_parameters 88 | - write_new_method 89 | - path: advanced 90 | children: 91 | - libraries 92 | - modules 93 | - private_methods 94 | - procs 95 | - yield 96 | - regular_expressions 97 | - path: exercises 98 | children: 99 | - numbers 100 | - strings 101 | - arrays_1 102 | - hashes_1 103 | - methods_1 104 | - arrays_2 105 | - nested_arrays 106 | - hashes_2 107 | - truthiness 108 | - email 109 | - mailbox 110 | - mailbox_text 111 | - mailbox_html 112 | - mailbox_file 113 | - mailbox_csv 114 | - path: exercises_2 115 | children: 116 | - numbers 117 | - strings 118 | - arrays_1 119 | - hashes_1 120 | -------------------------------------------------------------------------------- /source/02-creation.md: -------------------------------------------------------------------------------- 1 | # Programming is creation 2 | 3 | *Things coming to life* 4 | 5 | Programming is creation. Whenever you run a program a little universe is 6 | created. Things come to life and interact with each other, according to the 7 | rules that you, as their creator, define. 8 | 9 | Imagine you build an application like Twitter. Then, as a developer, in the 10 | programming language of your choice, you would say things like "Let there be 11 | users! And let there be tweets!", and once your application starts these things 12 | will come into existence. Next you would go ahead and define "Users can create 13 | tweets, and they can follow each other". And from now on, every time new users 14 | are created in the little universe that is your application, these users will 15 | have the ability to tweet and follow each other. 16 | 17 | -------------------------------------------------------------------------------- /source/03-learning_to_program/01-learning_modes.md: -------------------------------------------------------------------------------- 1 | # Learning modes 2 | 3 | *People are different* 4 | 5 | Different people learn things in different ways. 6 | 7 | Some like to listen to lectures, or read books that thoroughly explain concepts 8 | before they then start exercising, and experiment with what they just learned. 9 | Others like to get their hands dirty, and play with code until it does what 10 | they want. 11 | 12 | Some want to fully understand what a certain line of code does, and why. 13 | Others don't care that much about the details, and want their code to produce 14 | the right things instead. They might memorize what worked, and understand things 15 | more fully later. 16 | 17 | Some understand concepts in logical ways, and go precisely by their definitions. 18 | Others understand things better by coming up with good analogies and metaphors. 19 | And yet others tend to simply memorize things and how they get used. 20 | 21 | Therefore there is no one true path or one-size-fits-all approach to learning 22 | programming. Try different things, and pay attention to what works well for you, 23 | what is the most fun to you, and keeps you motivated. 24 | 25 | If you find it hard to keep motivated working through a certain online tutorial, 26 | or if you have a hard time fully understanding what an exercise is about, then 27 | try to pick a different approach, or talk to your study group. 28 | 29 | Consider meeting with others during the week, have some coffee and cake and 30 | hang out, read some more of this book, or do a few exercises together. 31 | 32 | Also, consider joining local programming events, like meetups, hackdays, or a 33 | conference once in a while. That gives you a broader perspective, even if you 34 | might not be able to fully understand everything all the time. Meeting with 35 | others and hearing about their experiences can also be a good source for 36 | inspiration and motivation. 37 | 38 | If you can't put a lot of time into learning programming, then consider 39 | spending at least a few minutes on it every day. For example, every morning, 40 | before you leave the house, take 10 minutes to read a page in a programming 41 | book, review an exercise, or read some code. Or spend some time with it 42 | on the train on your way home from work later. This will keep your subconscious 43 | mind busy with programming concepts, and help you pick things up more easily 44 | later. 45 | -------------------------------------------------------------------------------- /source/03-learning_to_program/04-error_messages.md: -------------------------------------------------------------------------------- 1 | # Reading error messages 2 | 3 | *Your new best friends* 4 | 5 | As programmers we want to build things that work. When we do exercises we want 6 | to figure out the solution to the given problem. Seeing error messages therefore 7 | has a negative connotation to it: we haven't managed to get it right! Right? 8 | 9 | Well, yes. But also, no. Errors can also indicate progress. Often error 10 | messages indicate that we've done the right first step, and now have to figure 11 | out the right next step. 12 | 13 | Whenever you find an error message printed on your terminal, don't fret. 14 | Instead, appreciate it. Read it carefully. Ruby is trying to give you a hand 15 | and help. Normally an error message tells you exactly what went wrong in your 16 | code, and where. Once you've understood what has happened look at your code and 17 | try to understand why it happened, and how you might fix it. 18 | 19 | Often, when you fix one thing, you'll get a different error message. 20 | That's progress. Rinse and repeat until your code does what you want. 21 | 22 | For a beginner, Ruby error messages can sometimes be hard to read. There are 23 | certain conventions used, e.g. for indicating there's an instance of 24 | a certain class involved. Backtraces might be hard to read and might look 25 | frightening. But all they do is tell you the exact path of execution Ruby 26 | took until it ended up raising an error. 27 | 28 | If you do not understand an error message and you have the opportunity to 29 | ask someone else, then do so. You'll get better and better at reading 30 | error messages over time. And you'll become friends with them. 31 | -------------------------------------------------------------------------------- /source/03-learning_to_program/05-using_google.md: -------------------------------------------------------------------------------- 1 | # Using Google 2 | 3 | *Someone else has already had that question* 4 | 5 | Whenever you have a question, and you're not sure where to start looking for an 6 | answer, simply go ahead and ask Google. Some other person most probably has had 7 | the same question before, and often times you will find a helpful blog post, 8 | tutorial, documentation, or answer on community sites like Stack Overflow. 9 | 10 | Finding the right terms to type into Google might take some time. Experiment 11 | with this. If one question does not yield a helpful result on the first page, 12 | try to rephrase your question. You'll get better and better at this over time. 13 | 14 | Always include the keyword "ruby" though, so you don't get answers for other 15 | programming languages. 16 | 17 | E.g. if you are wondering if there is a method that gets you all the keys from 18 | a hash, then google for "ruby hash get keys". If you are wondering if there's 19 | a method to align strings so they all have a certain length, then google for 20 | "ruby string align". 21 | 22 | Google results that link to the Ruby documentation on ruby-doc.org are often 23 | good ones to look at, but these pages also often are very long. Look at the 24 | summary of the page on Google, and the highlighted terms, then go to the 25 | page and use your browser's search feature to find them on the page. 26 | 27 | Also, results on Stack Overflow are often very worthwhile to read. This is a 28 | programming community site where people with all kinds of experience levels 29 | post questions, which then will be answered by others. People can rank answers, 30 | and the best ranked answer appears at the top. Often it is worth skimming 31 | through most of the answers, as sometimes the question asked is just slightly 32 | different from yours, but someone might still suggest a solution that might be 33 | similar to the one you are looking for. 34 | -------------------------------------------------------------------------------- /source/04-your_tools.md: -------------------------------------------------------------------------------- 1 | # Your tools 2 | 3 | When you start learning programming in Ruby you'll need three tools: A text 4 | editor, a terminal, and a Ruby runtime. 5 | 6 | As a programmer you'll spend tons of time working with your text editor and 7 | terminal (or "in" them, as we say). It makes sense for you to learn how to use 8 | these tools, and how to customize them so they fit your taste. 9 | 10 | There are also text editors that can be integrated into a terminal window. This 11 | means you don't have to switch between applications. There are also online 12 | services that integrate all of this as a web application, with a nice interface 13 | in your browser. 14 | 15 | Feel free to try all these things out, and play with them as much as you like. 16 | However, for our study groups, we have found that using the simplest tools 17 | possible, at least for a while, works best: It reduces the amount of things 18 | people need to learn in the beginning, and everyone learns the same few basic 19 | things that are required to run a Ruby program in this environment. 20 | -------------------------------------------------------------------------------- /source/04-your_tools/01-text_editor.md: -------------------------------------------------------------------------------- 1 | # Text Editor 2 | 3 | For our study groups we recommend using [Sublime Text](http://www.sublimetext.com) 4 | as a text editor, which you can download and run on Mac OSX, Ubuntu, or 5 | Windows. This is an editor that has been specifically designed for writing 6 | code, comes with a lot of great tools, and can be easily customized. 7 | 8 | Other text editors that might be worth looking at are Atom, Textmate 2, and, if 9 | you like to use some of the powerful oldschool tools from the early times of 10 | Unix, VIM and Emacs. These are all great editors to use. 11 | 12 | Whatever editor you use, you want it to insert 2 spaces when you hit the "tab" 13 | key, i.e. when you want to indent your code. Make sure your editor is 14 | configured to do this. 15 | 16 | For Sublime Text you can do the following: In the menu item "Sublime Text" go 17 | to "Preferences" and select "Settings - User". This opens up a configuration 18 | file that you can edit just like any other file. Make sure it looks like this: 19 | 20 | ```json 21 | { 22 | "tab_size": 2, 23 | "translate_tabs_to_spaces": true 24 | } 25 | ``` 26 | 27 | Also, we recommend enabling auto-saving your files. This will automatically 28 | save your changes when you switch to another application (like your terminal), 29 | and protect you from the mistake of forgetting to save: 30 | 31 | ```json 32 | { 33 | "tab_size": 2, 34 | "translate_tabs_to_spaces": true, 35 | "save_on_focus_lost": true 36 | } 37 | ``` 38 | 39 | Whenever you open a new file, make sure to save it with a filename that ends 40 | with `.rb` first. This will tell the editor that you want this to be a Ruby 41 | file. Your editor will start highlighting your code as Ruby code, and enable other 42 | Ruby specific editor features. Alternatively, select "Ruby" in the extensions 43 | menu at the bottom right. 44 | 45 | Some keyboard shortcuts that are extremely useful to know are: 46 | 47 | * In order to indent or unindent a single, or multiple lines of code, select 48 | them and hit `tab` or `shift-tab` respectively. 49 | * In order to comment single, or multiple lines of code, select them, and 50 | hit `cmd-/` on Mac OSX, or `ctrl-/` on Linux/Windows. 51 | * Cut out code with `cmd-x`, copy code with `cmd-c`, and paste it with 52 | `cmd-v` on Mac OSX, on Linux/Windows use the `ctrl` key instead. 53 | 54 | -------------------------------------------------------------------------------- /source/04-your_tools/02-terminal.md: -------------------------------------------------------------------------------- 1 | # Terminal 2 | 3 | Every operating system comes with some kind of terminal application built in, 4 | and they'll be fine to use for us. 5 | 6 | A terminal is an application that doesn't do much more but provide a window to 7 | run another program, called a "shell". A shell is a text-based program, so it 8 | does not have a window, and thus needs the terminal to be run on your graphical 9 | user interface. 10 | 11 | A shell is an interactive program that waits for you to type a command and hit 12 | enter (or "return"). It will then run the command, display any output, and 13 | then, again, sit and wait for you to type the next command. 14 | 15 | This is how working with computers was done in early times before there was the 16 | idea of a graphical user interface with clickable icons, windows, and a mouse as 17 | an input device. Instead, everything was done by typing commands. 18 | 19 | While this might take a little while for you to get used to, you'll discover 20 | why many programmers feel that working with the shell helps them be so much 21 | more productive and get simple things done so much quicker than using a mouse 22 | and a graphical interface. 23 | 24 | The default settings for most terminal applications that come built in to 25 | operating systems are quite poor, unfortunately. For example the Terminal.app 26 | on Mac OSX opens a tiny little mini window with a very small font. You want 27 | to make the window much (much!) bigger, and find the settings to pick 28 | a bigger font size. As a programmer, the terminal (along with your editor) is 29 | your new home. You want to be as comfortable reading and writing in your 30 | terminal as in any other application. 31 | 32 | In case you are using Microsoft Windows it is highly recommended to use any 33 | kind of unix based terminal. One option is Git-Bash (https://gitforwindows.org/). 34 | However, as Ruby and Microsoft Windows are not best friends, it is even better 35 | to install the Windows Subsystem for Linux which is available here for 36 | Windows 10: https://docs.microsoft.com/en-us/windows/wsl/install-win10. With 37 | this option you can install everything you need for your Ruby development 38 | under Ubuntu and use Bash as your main shell. 39 | 40 | All commands in the following texts are unix commands and will not work in a 41 | Microsoft Windows terminal. 42 | 43 | Once you have started the terminal program you will see your shell's prompt, 44 | and can start typing commands. You'll want to learn at least the command `cd` 45 | which allows you to navigate to a particular directory (where you have stored 46 | your code), and the command `ls` which lists the contents of a directory. 47 | 48 | Also learn how to use the command line completion using `tab`: For example, 49 | type `cd`, space, and the first two or so letters of the directory you want to 50 | navigate to, then hit `tab`. The shell will complete the rest of the directory 51 | name for you. If nothing happens, then there are multiple directory names 52 | starting with the same few letters. In this case hit `tab` twice, quickly, 53 | and your shell will display a list of choices. Type the next letter 54 | and hit `tab` again. 55 | 56 | -------------------------------------------------------------------------------- /source/04-your_tools/03-ruby_runtime.md: -------------------------------------------------------------------------------- 1 | # Ruby runtime 2 | 3 | The last tool you need is a Ruby runtime. This is a program that can execute 4 | Ruby code, and it is a program that can be run in your shell (much like `cd` 5 | and `ls`). 6 | 7 | If you have some code in a file with the name `hello.rb`, and you have 8 | navigated to the directory where the file is saved, then you can type the 9 | following and hit enter, in order to execute the code: 10 | 11 | ``` 12 | ruby hello.rb 13 | ``` 14 | 15 | This will tell the `ruby` runtime to interpret the contents of the file 16 | `hello.rb` as Ruby code, and execute it. 17 | 18 | One can also use this program to do other things. You probably won't need 19 | these a lot, but we think it's good to know. You can try these by typing 20 | these commands in your terminal. 21 | 22 | E.g. you can print out the version of the program: 23 | 24 | ``` 25 | ruby --version 26 | ``` 27 | 28 | Or execute Ruby code without storing it to a file: 29 | 30 | ``` 31 | ruby -e 'puts 123' 32 | ``` 33 | -------------------------------------------------------------------------------- /source/04-your_tools/04-workflow.md: -------------------------------------------------------------------------------- 1 | # Programming workflow 2 | 3 | In order to do programming excercises on your computer, play around with things 4 | you learned, and write actual, useful programs you want to learn the following 5 | workflow: 6 | 7 | * Write some code in your text editor. 8 | * Save the code to a file in a particular directory. The filename should end 9 | with `.rb`. 10 | * Open your terminal. 11 | * Navigate to that directory using `cd`. 12 | * Execute the file using `ruby`. 13 | * Switch back and forth between the text editor and terminal, so you 14 | can make small changes in your code, and then run it through `ruby` to see 15 | what it does. 16 | 17 | In order to switch back and forth between apps quickly you can use the keyboard 18 | shortcut `cmd-tab` on Mac OSX, and `alt-tab` on Ubuntu and Windows. 19 | 20 | In your shell you can use the `cursor up` key to go through your last used 21 | commands: you don't have to type `ruby hello.rb` again. Just hit `cursor up` 22 | and then enter to run it again. 23 | 24 | -------------------------------------------------------------------------------- /source/04-your_tools/05-irb.md: -------------------------------------------------------------------------------- 1 | # Interactive Ruby 2 | 3 | One other tool that is worth mentioning, and that comes with your Ruby runtime, 4 | is `irb`. You can start it by typing `irb` in your shell and hitting enter. 5 | 6 | Its name is short for "Interactive Ruby Shell", and yes, it is another kind of 7 | shell: Just like the shell running in your terminal `irb` is also a program 8 | that interactively waits for you to type something, and hit enter. However, 9 | since this is a Ruby shell, it will expect that you type Ruby code instead of 10 | system commands. 11 | 12 | IRB is pretty handy for quickly trying something out, and it is a great tool 13 | for exploring the language Ruby, and things that come built in. 14 | 15 | For example: 16 | 17 | ```ruby 18 | $ irb 19 | > puts "Hello world!" 20 | Hello world! 21 | => nil 22 | > 23 | ``` 24 | 25 | The first line starts the IRB program. Notice how the "prompt" indicator 26 | changes. The prompt will look a little different depending on your system and 27 | shell configuration, but often `$` is used to indicate that this is a 28 | system shell prompt, while `>` is used by IRB to indicate that this is an 29 | interactive Ruby shell. 30 | 31 | The second line is a piece of Ruby code. When you type this line and hit enter 32 | then Ruby will execute the code, and print out the text "Hello world!". 33 | 34 | It will then also print out the return value for this statement, which in this 35 | case is `nil`. This is something that you can simply ignore for the time being. 36 | 37 | On the last line you see IRB again waiting for input with a prompt. 38 | 39 | You can exit the IRB session, and get back to your system shell, by typing 40 | `exit` and hitting enter. Or you can also just hit `ctrl-d`, which does the 41 | same thing. 42 | -------------------------------------------------------------------------------- /source/07-variables/01-reusing_names.md: -------------------------------------------------------------------------------- 1 | # Reusing variable names 2 | 3 | It is also important to keep in mind that a name is unique, in the sense that 4 | the same name can only be assigned to one value (object) at a time. 5 | 6 | In other words, if you assign different values to the same variable, then 7 | assignments that happen later will simply overwrite previous ones. Like so: 8 | 9 | ```ruby 10 | number = 4 11 | number = number * 3 12 | puts number + 2 13 | ``` 14 | 15 | This, again, would output `14`. 16 | 17 |

18 | Variable names can be re-used, and re-assigned. 19 |

20 | 21 | The first line assigns the name `number` to the number `4`. The second line 22 | re-assigns it to another object. 23 | 24 | Getting back to our post-its metaphor ... this would stick a post-it with the 25 | name `number` on one thing, and then later take it off of it, and stick it on 26 | something else. 27 | 28 | Let's look at it under the microscope: 29 | 30 | * On the **first line** Ruby creates the number (object) `4`. 31 | * It then assigns the name `number` to it (sticks a post-it onto it). 32 | * On the **second line**, Ruby first looks at the stuff on the right side, 33 | and evaluates the expression `number * 3`. Doing so it will create 34 | the number (object) `3` and multiply it with the object that currently has 35 | the name `number`, which is `4`. This operation results in a new number 36 | (object) `12`. 37 | * Now Ruby is finally ready to stick the name `number` on the result `12`. 38 | I.e. *from now on* the name `number` refers to a different object, which is 39 | `12`. 40 | * On the **third line** Ruby will, again, first look at the expression `number + 2` 41 | on the right. It creates the object `2` and adds it to the object that currently 42 | has the name `number`. This results in a new number (object) `14`. 43 | * Finally Ruby passes this object `14` to `puts`, which outputs it to the 44 | screen. 45 | 46 | Of course, you would probably never actually write this exact code in practice 47 | since you can simply do all this in just one line instead: `puts 4 * 3 + 2`. 48 | 49 | However, sometimes you'll find or write code that assigns an initial value to a 50 | variable, and then keeps working on it for a few more lines. This sometimes is 51 | useful to break up long lines, and make code more readable. 52 | 53 |

54 | Using variable names can be useful to break up long lines and make code more 55 | expressive and readable. 56 |

57 | 58 | Also, Ruby has different kinds of variables. 59 | 60 | The kind of variable that we've introduced so far is called a *local variable*, 61 | and it's the one used most often. You will learn about another type of 62 | variables later when we talk about classes and objects. 63 | 64 |

65 | On formatting: Note that there are spaces around the assignment operator `=` 66 | as well as the arithmetical operators `+` and `*`. 67 |

68 | 69 | -------------------------------------------------------------------------------- /source/07-variables/02-right_goes_first.md: -------------------------------------------------------------------------------- 1 | # Things on the right go first 2 | 3 | One last thing that is worth mentioning about variables: 4 | 5 | Just as in traffic (well, at least in 6 | [most parts of the world](https://en.wikipedia.org/wiki/Right-_and_left-hand_traffic)), 7 | things on the right side go first :) 8 | 9 | For a variable assignment, in order to assign the thing on the right side to 10 | the name on the left side, Ruby first needs to figure out the thing on the 11 | right. As you will see later the same is true for many other expressions in 12 | Ruby. 13 | 14 | In our example above it will first create the object that is the number `1`, 15 | and then assign it to the name `number`. The following example makes that more 16 | clear: 17 | 18 | ```ruby 19 | number = 2 + 3 * 4 20 | puts number 21 | ``` 22 | 23 | When Ruby looks at the first line `number = 2 + 3 * 4` it notices that this is 24 | using the assignment operator `=`. Therefor, before it can assign the name 25 | `number` to the "thing" (object) on the right ... it first needs to know what 26 | that thing is. 27 | 28 | So, before she does anything else, Ruby will first look at the expression `2 + 29 | 3 * 4`, which will result in the number (object) `14`. She will *then* assign 30 | the name `number` to this object (i.e. evaluate the assignment operator `=`). 31 | 32 | You can imagine that in this moment, when Ruby starts evaluating the assignment 33 | `=` the code temporarily looks like this: `number = 14` (because the 34 | calculation has returned the number `14`). 35 | 36 | Does this make sense? 37 | 38 | Again, on the second line Ruby will then pass the thing with the name `number` 39 | (which is `14`) to `puts`, which will output it to the screen. 40 | 41 |

42 | Ruby evaluates the expression on the right first. 43 |

44 | -------------------------------------------------------------------------------- /source/08-built_in_classes.md: -------------------------------------------------------------------------------- 1 | # Built-In Data Types 2 | 3 | As mentioned before, Ruby comes with lots of things already baked in, and 4 | provides you with tons of tools to use and hit the road running. 5 | 6 | We'll look at some of the most common data types in Ruby. Data types are types 7 | of "things" that are mainly used to represent data, such as numbers, text, and 8 | other values. 9 | 10 | This is basically the "stuff" that you, as a Ruby programmers will work with, 11 | when we work with actual data, that is interesting to you, or your users in one 12 | way or the other. 13 | 14 | We will discusss the following data types: 15 | 16 | * Numbers 17 | * Strings (texts) 18 | * True, False, and Nil 19 | * Symbols 20 | * Arrays 21 | * Hashes 22 | 23 | These "kinds of things" (objects) cover like 98% of all built-in data types 24 | that you'll be using on a day to day basis, i.e. these are being used all over 25 | the place. There are more data types, but those are rather exotic, and used 26 | much less often. 27 | 28 | Numbers and Strings (which is just a strange name for "texts") are some of the 29 | most basic "things" that you deal with in Ruby on a regular basis. The are also 30 | just enough lego bricks for us to dive into more interesting topics, such as 31 | how objects, classes, and methods relate to each other, and how you can create 32 | your own ones. 33 | 34 | Before we do so we will also briefly mention the "things" `true`, `false`, and 35 | `nil`, just because these are things that we'll come across along the way 36 | anyway. 37 | 38 | Symbols also are very commonly used, but quite an odd concept. You normally 39 | wouldn't need to understand Symbols in order to write your own code. We'll 40 | still cover them briefly, and use them once in a while, just in case you find 41 | them elsewhere. 42 | 43 | Arrays and Hashes are "things" (objects) that are used to store other things, 44 | and they're super useful, and widely used. 45 | -------------------------------------------------------------------------------- /source/08-built_in_classes/01-numbers.md: -------------------------------------------------------------------------------- 1 | # Numbers 2 | 3 | Numbers are simply numbers. 4 | 5 | You can create one by writing it: `123` is Ruby code and it represents the 6 | number one-hundred-twenty-three. 7 | 8 | Negative numbers are created by prepending a minus `-`: This is the number 9 | minus-ninety-nine: `-99`. 10 | 11 | And of course there are decimal numbers, too. Again, you create one by writing 12 | it: `12.34`. 13 | 14 | You can also use an underscore to separate thousands places: E.g. `1_234.56` is the 15 | number one-thousand-two-hundred-thirty-four-point-five-six. However, this is 16 | optional. This is the exact same number in Ruby: `1234.56`. 17 | 18 |

19 | A number is defined by a series of digits, using a dot as a decimal mark, and 20 | optinally an underscore as a thousands separator. 21 |

22 | 23 | Note that different countries use different punctuation for decimal and 24 | thousands separators. Ruby is using the same notation used in the USA, which 25 | happens to be the exact opposite of what's used in Germany. 26 | 27 | ## Kinds of numbers 28 | 29 | Under the hood, for reasons that are mostly technical, there are actually 30 | different kinds of numbers: 31 | 32 | For example there are integer numbers (those without a fraction, i.e. a dot and 33 | decimal places), and depending on their size there are two kinds of them. For 34 | floating point numbers there are even more. Unless you are super curious, you 35 | can perfectly ignore all of that for now, and just think of numbers as numbers. 36 | 37 | However, when you do calculations with numbers, keep in mind that integer 38 | numbers ("integers") and decimal point numbers (floating point numbers, aka 39 | "floats") are different. 40 | 41 | If you do a calculation that uses integer number you'll always 42 | get an integer back: 43 | 44 | ```ruby 45 | $ irb 46 | > 1 + 2 47 | => 3 48 | ``` 49 | 50 | However, if any of the numbers involved is a float, then you'll get a float back: 51 | 52 | ```ruby 53 | $ irb 54 | > 1.0 + 2 55 | => 3.0 56 | > 1 + 2.0 57 | => 3.0 58 | ``` 59 | 60 |

61 | Mathematical operations result in a floating point number except if all numbers used are integer numbers. 62 |

63 | 64 | This is, for example, important when you do a division (`/` means "divide by"): 65 | 66 | ```ruby 67 | $ irb 68 | > 3 / 2 69 | => 1 70 | ``` 71 | 72 | As you can see any decimal places will be just cut off, since the result needs 73 | to be an integer number. 74 | 75 | However, if you use floats: 76 | 77 | ```ruby 78 | $ irb 79 | > 3.0 / 2 80 | => 1.5 81 | > 3 / 2.0 82 | => 1.5 83 | ``` 84 | 85 |

86 | Use floating point (decimal) numbers when doing devisions. 87 |

88 | 89 | Exercises: How about doing some of the [exercises on numbers](/exercises/numbers.html) 90 | next? 91 | -------------------------------------------------------------------------------- /source/08-built_in_classes/03-true_false_nil.md: -------------------------------------------------------------------------------- 1 | # True, False, and Nil 2 | 3 | There are three more "things" that we'd like to mention quickly, just because 4 | you'll see quite frequently: These are `true`, `false`, and `nil`. 5 | 6 | The first two, `true` and `false` are just what you think they are: 7 | 8 |

9 | The object true represents "truth", while false 10 | represents the opposite of it. 11 |

12 | 13 | In other words, in Ruby, `true` and `false` are also "things", just like 14 | numbers, Strings, Arrays, and Hashes. You can assign them to variables, pass 15 | them around, and otherwise use them. They're fairly simple things, but they're 16 | also very useful. 17 | 18 | The third object `nil` represents "nothing". Yeah, that's right. In Ruby 19 | there's a "thing" that represents the absence of things. This might be a 20 | interesting topic to discuss on a philosophical level, but for now we'll 21 | just see how it works in Ruby. 22 | 23 |

24 | The object nil represents "nothing". 25 |

26 | 27 | You'll see later that every operation ("method") in Ruby always returns 28 | exactly one thing (i.e. one object), and that's why there needs to be a 29 | "thing" that represents "nothing". 30 | 31 | This will start to feel pretty natural to you pretty soon. 32 | 33 | -------------------------------------------------------------------------------- /source/09-objects.md: -------------------------------------------------------------------------------- 1 | # Objects, Classes, Methods 2 | 3 | We've talked about objects quite a bit now, merely *using* that term as a 4 | synomym for "things". You've learned to do stuff with simple objects such as 5 | numbers and Strings, and the data structures Arrays and Hashes. You've also 6 | learned what a method is, how to define and use them, and how to pass things to 7 | them, and get others back. 8 | 9 | We're also prepared well enough to have a closer look at objects, classes 10 | (types of things) and methods, and how they relate to each other. 11 | 12 | As a sidenote, you may have heard the following. Rubyist love saying it: 13 | 14 |

15 | In Ruby everything is an object. 16 |

17 | 18 | Actually, this is a little bit of a lie. Even though it's being said so often, 19 | it isn't completely true: not *everything* is an object. There are a few things 20 | that are not objects. 21 | 22 | But almost everything is, and that's good enough to know for now. 23 | 24 |

25 | In Ruby almost everything is an object :) 26 |

27 | 28 | (Just in case you want to out-smart people at your local Ruby user group 29 | though, remember to ask "Really? Is an [if](conditionals.html) statement an 30 | object or a method? How about [self](/writing_classes/self.html)?") 31 | -------------------------------------------------------------------------------- /source/09-objects/01-classes.md: -------------------------------------------------------------------------------- 1 | # Objects have classes 2 | 3 | As mentioned [before](/object_oriented_programming.html) when you run a Ruby 4 | program a little universe (space, scope) is being created, and populated with 5 | concrete objects (things) as defined by your program. These things interact, 6 | and do useful stuff, using certain methods that you call. 7 | 8 | Also, each concrete thing (object) is an instance of a general idea or type, 9 | and these ideas are called *classes*. 10 | 11 | You can see that objects have classes when you open IRB in your terminal and 12 | ask an object for its class: 13 | 14 | ```ruby 15 | $ irb 16 | > "this is a string".class 17 | => String 18 | ``` 19 | 20 |

21 | Objects are concrete instances (manifestations) of classes. 22 |

23 | 24 | That means that the following sentence is true: In Ruby, `"a string"` *is a* 25 | `String`. 26 | 27 | Hmmmm, yeah, we kinda knew that already, right. However, you can also ask the 28 | same question in Ruby: 29 | 30 | ```ruby 31 | $ irb 32 | > "this is a string".is_a?(String) 33 | => true 34 | ``` 35 | 36 | So, the actual string knows that it *is a* `String`, which in Ruby means that 37 | it is an instance of the class `String`. You can do this for any object. E.g. 38 | `1.is_a?(Numeric)`, also returns `true`. This is pretty cool. 39 | 40 | We also say that an object is an *instance* of its class. Let's see what that 41 | means. 42 | 43 | -------------------------------------------------------------------------------- /source/09-objects/02-instances.md: -------------------------------------------------------------------------------- 1 | # Classes create objects 2 | 3 | *Objects are instances of classes.* 4 | 5 | Classes are kind of blue prints for the concrete objects. Every time a concrete 6 | object (such as the String `"one string"`, the String `"another string"`, the 7 | String `"yet another string"`, and so on ...) is created: 8 | 9 | We say that the class is *instantiated*: an object is created from it. 10 | 11 | What does that mean? 12 | 13 | Classes have a bunch of characteristics, but most importantly, every class 14 | defines a number of methods, which are specific to this type of thing (e.g. a 15 | String). 16 | 17 | Now, every time a new object is created ("instantiated") from this class this 18 | new object get ("inherits") all of these methods. 19 | 20 |

21 | Objects inherit methods from their classes. 22 |

23 | 24 | That's right. Objects have their own methods attached to them. 25 | 26 | We'll explain more about methods that belong to objects in the next chapter. 27 | And you'll see how you can define methods to your own classes, so they're then 28 | available on your objects when we start defining our 29 | [first, own class](/writing_classes). 30 | 31 | But for now we can already point out that all concrete Strings that you use in 32 | your code will all have the same methods defined (attached to them) ... because 33 | they're all created (instantiated) from the the same class. 34 | 35 | You can have a look at all the methods that the class `String` defines on 36 | Ruby's [documentation page](http://ruby-doc.org/core-2.2.0/String.html) for this 37 | class. 38 | 39 | Let's see how we can use these methods. 40 | 41 | -------------------------------------------------------------------------------- /source/09-objects/03-methods.md: -------------------------------------------------------------------------------- 1 | # Objects have methods 2 | 3 | *Methods are an object's behaviour* 4 | 5 | Objects have methods, allowing us to do interesting stuff with them. An 6 | object's methods are things that the object can *do*. 7 | 8 | Think about a person, like, a friend of yours. You can ask this person for 9 | their name (call a method), and they'll tell you (return it to you). Their name 10 | is a piece of knowledge that this person has, and the ability to tell it to you 11 | (respond to your question) is a piece of behaviour (a method) they have. 12 | 13 | We could also ask them to make, and bring a cup of tea for us. Or we could ask 14 | them to remember a phone number, or email password. 15 | 16 | As Rubyists we actually say that we "talk to objects", or "send messages" to 17 | them: We do so by using (calling) methods that they respond to. 18 | 19 | So, what can an object do? 20 | 21 | That depends on their class (type). Numbers can do things that are useful for 22 | numbers, obviously. You can do math, and ask them about their mathematical 23 | properties (e.g. "Are you an odd number?"). Strings (text) come with way more 24 | methods, and they're often related to text transformations. 25 | 26 |

27 | Methods add behaviour that is useful to have for a particular type of object. 28 |

29 | 30 | We've already used some methods in the previous chapters: E.g. `"hello".upcase` 31 | calls the method `upcase` on the String `"hello"`. 32 | 33 | Also, `class` and `is_a?` are methods defined on all objects in Ruby, and 34 | therefore also defined on the String `"a string"`: `"a string".is_a?(String)` 35 | answers with `true`. 36 | 37 |

38 | Some methods, such as class, is_a?, are defined on all objects. 39 |

40 | 41 | Let's move on to see how we can use (call) these methods though. 42 | -------------------------------------------------------------------------------- /source/09-objects/04-calling.md: -------------------------------------------------------------------------------- 1 | # Calling methods 2 | 3 | In Ruby, methods that belong to (are defined on) objects can be used (called) 4 | by adding a dot, and then the method name, like so: 5 | 6 | ```ruby 7 | object.method 8 | ``` 9 | 10 | That's like saying *Hey object, please do [method]*. 11 | 12 | Let's try that out in IRB. 13 | 14 | For example, the class `String` defines methods like `upcase` (*Give me an 15 | uppercase version of yours*), `downcase` (*What's the downcased version of 16 | yours*), and `length` (*Hey string, what's your length?*). 17 | 18 | Here's how we can call them: 19 | 20 | ```ruby 21 | $ irb 22 | > name = "Ruby Monstas" 23 | > name.upcase 24 | => "RUBY MONSTAS" 25 | > name.downcase 26 | => "ruby monstas" 27 | > name.length 28 | => 12 29 | ``` 30 | 31 | And so on. 32 | 33 | In other words, you first address, or mention, the object that you want to talk 34 | to, and then, with the dot `.`, "send a message" to the object by specifying 35 | the method name. We also say: "you call the method upcase on the string". 36 | 37 |

38 | A dot is used to call a method on an object. 39 |

40 | 41 | Imagine the string `name` is a person you can talk to. You can ask questions by 42 | "sending a message" to them, and they'll respond by sending (returning) 43 | something back. The term "sending messages" actually is used instead of 44 | "calling a method" in programming, and specifically in Ruby. 45 | 46 | So, you can ask the string to hand you an "upcased" version of itself. And 47 | it responds by doing so. Or you can ask it for its length, and it responds 48 | by returning the number `12` to you. 49 | 50 | You can have a look at all the methods that the class `String` defines 51 | (responds to) on Ruby's 52 | [documentation page for this class](http://ruby-doc.org/core-2.2.0/String.html). 53 | 54 | Most methods in Ruby work this way: You ask an object to return a bit of 55 | information about itself (such as a String's length), or a modified 56 | (transformed) version of itself (such as a downcased version of the String). 57 | 58 |

59 | Most methods are questions, and return a relevant value. 60 |

61 | 62 | Others modify the object itself, and some have so called side-effects, and 63 | modify something else, e.g. `puts` and `p` both output something to the 64 | screen. Other methods might save files, send emails, or store things to 65 | a database. 66 | 67 |

68 | Some methods are commands, and change the object, or the system (e.g. by saving a file). 69 |

70 | 71 | 72 | -------------------------------------------------------------------------------- /source/09-objects/05-arguments.md: -------------------------------------------------------------------------------- 1 | # Passing arguments 2 | 3 | *Extra information needed* 4 | 5 | Sometimes an object needs a 6 | little bit of extra information in order to do what you ask for. 7 | 8 | For example the class `String` defines the method `delete` which returns 9 | another String with some of the characters deleted. In order to do so, of 10 | course, it needs to know which characters we'd like to remove. 11 | 12 | We can pass things by appending parentheses `()` to the method call (the name). 13 | We can then include the extra bit of information needed (in our case another 14 | string) inside the parentheses like so: 15 | 16 | ```ruby 17 | $ irb 18 | > name = "Ruby Monstas" 19 | > name.delete("by Mo") 20 | => "Runstas" 21 | ``` 22 | 23 | Hm. Not sure what "Runstas" means. Ideas? Let us know. 24 | 25 | Anyhow, another example for a method that needs an argument is the method 26 | `prepend` on Strings. This method returns a new String with the given String 27 | prepended: 28 | 29 | ```ruby 30 | > name = "Ruby Monstas" 31 | > name.prepend("Oh, hello, ") 32 | => "Oh, hello, Ruby Monstas" 33 | ``` 34 | 35 | These extra bits of information are called *arguments*. We'll discuss them more 36 | once we get to define [our own methods](/writing_methods.html). 37 | 38 | Not all methods need these extra bits of information (arguments) in order to do 39 | their job. E.g. the method `length` on Strings knows the length of their 40 | String just so (because it knows its String). Sometimes they need one or more 41 | arguments though. 42 | 43 | So how do you know? 44 | 45 | You might remember, over time, for some important methods, but most of us also 46 | check the documentation quite frequently, too. Some times it's just fine to 47 | try it out quickly, and only check the documentation if it does not work as 48 | expected. 49 | 50 | -------------------------------------------------------------------------------- /source/09-objects/06-listing.md: -------------------------------------------------------------------------------- 1 | # Listing methods 2 | 3 | As said above, if you are curious what methods are defined on a certain object, 4 | then you can check the [Ruby documentation](http://ruby-doc.org/core-2.2.0/) 5 | for this class. (Usually the right page conveniently shows up at the top when 6 | you google for "ruby" and the class name.) 7 | 8 | However, you can also quickly pop into IRB and ask the object for its methods. 9 | That's right, `methods` is a method defined on all objects (just like `class`, 10 | and `is_a?`). 11 | 12 | When you call it then it will return (respond with) an Array with all the 13 | method names that the object has. 14 | 15 | It makes sense to sort the Array, so it is easier to read. Like so: 16 | 17 | ```ruby 18 | $ irb 19 | > "Ruby Monstas".methods.sort 20 | => [:*, :+, :<, :>, :[], :class, :downcase, :delete, :include?, :is_a?, :length, :prepend, :start_with?] 21 | ``` 22 | 23 | Yep, the method names come as Symbols, because they're considered code. 24 | 25 | If you do this yourself, you'll see that the String actually has a lot more 26 | methods. Many of these actually aren't used very often, but some are quite 27 | useful. We have stripped the Array down a little, because we want to talk about 28 | some of these methods more. 29 | 30 | ## Chaining method calls 31 | 32 | Btw the code above also demonstrates that methods can be "chained": When we 33 | call a method on an object it will return another object to us. We can then 34 | immediately call another method on that new object, and so on. 35 | 36 | In our example above the method `methods` returns a Array of names. And 37 | Arrays respond to (have) the method `sort`, so we can call this method 38 | immediately, by using another dot. 39 | 40 | We could chain some of the method calls from our String example above like so: 41 | 42 | ```ruby 43 | $ irb 44 | > name = "Ruby Monstas" 45 | > name.prepend("Oh, hello, ").upcase 46 | => "OH, HELLO, RUBY MONSTAS" 47 | ``` 48 | 49 | So we have a String `"Ruby Monstas"`, prepend another String to it, which 50 | returns a new String `"Oh, hello, Ruby Monstas"`, on which we immediately call 51 | the method upcase. 52 | 53 | Pretty handy. 54 | 55 | As you can see Ruby will first evaluate the bit `name.prepend("Oh, hello, ")`. 56 | It needs to do that so it knows the object (the new String) that is going 57 | to be returned from this, so it can then call the method `upcase` on it. 58 | 59 | Does this make sense? 60 | -------------------------------------------------------------------------------- /source/09-objects/07-predicates.md: -------------------------------------------------------------------------------- 1 | # Predicate methods 2 | 3 | If you check the list of methods on our String above you see that in Ruby we 4 | can have methods that end with a question mark `?`. What's up with that? 5 | 6 | By convention, in Ruby, these methods return either `true` or `false`. For 7 | example, we can ask a number if it is even or odd: 8 | 9 | ```ruby 10 | $ irb 11 | > 5.odd? 12 | => true 13 | 14 | > 5.even? 15 | => false 16 | ``` 17 | 18 | This makes them read like a question, which is pretty cool. 19 | 20 | Or you can ask the number if it's between two other numbers. Obviously this 21 | methods needs us to pass those two other numbers. So now we also have an example 22 | of a method that takes two arguments: 23 | 24 | ```ruby 25 | > 5.between?(1, 10) 26 | => true 27 | 28 | > 5.between?(11, 20) 29 | => false 30 | ``` 31 | 32 | These methods are called *predicate methods* in Ruby. Not quite sure why, maybe 33 | because of the historical math context of programming. 34 | 35 |

36 | Predicate methods that end with a question mark ? return either true or false. 37 |

38 | 39 | Strings also define some predicate methods: 40 | 41 | ```ruby 42 | > name = "Ruby Monstas" 43 | > name.start_with?("R") 44 | => true 45 | > name.start_with?("a") 46 | => false 47 | ``` 48 | 49 | Do you also think it's kinda odd that `name.start_with?("a")` reads almost like 50 | English, but not quite? Maybe the method could have been named `starts_with?` 51 | instead, right? That's true. This is because Matz, the creator of Ruby, is not 52 | a native English speaker, and some names sound right in Japanese when 53 | translated literally. 54 | 55 | Also: 56 | 57 | ```ruby 58 | > name = "Ruby Monstas" 59 | > name.include?("by") 60 | => true 61 | 62 | > name.include?("r") 63 | => false 64 | ``` 65 | 66 | When we check what methods there are defined on a number, we find some with the 67 | same name, but also different ones: 68 | 69 | ```ruby 70 | $ irb 71 | > 1.methods.sort 72 | => [:*, :+, :-, :/, :between?, :even?, :odd?, :zero?] 73 | ``` 74 | 75 | Let's try `zero?`: 76 | 77 | ```ruby 78 | > 0.zero? 79 | => true 80 | 81 | > 1.zero? 82 | => false 83 | ``` 84 | 85 | Arrays have the methods `include?`, and Hashes respond to `key?`: 86 | 87 | ```ruby 88 | > [1, 2].include?(1) 89 | => true 90 | 91 | > [1, 2].include?(3) 92 | => false 93 | 94 | > { "eins" => "one" }.key?("eins") 95 | => true 96 | 97 | > { "eins" => "one" }.key?("zwei") 98 | => false 99 | ``` 100 | 101 | Oh by the way, if you're curious why operators like `*`, `+`, `-` and so on are 102 | also listed here, check the chapter that explains that [operators are methods, 103 | too](/operators/methods.html). 104 | 105 | -------------------------------------------------------------------------------- /source/09-objects/08-bangs.md: -------------------------------------------------------------------------------- 1 | # Bang Methods 2 | 3 | All of the examples that we've discussed before have one thing in common: 4 | 5 | They are questions, and do not modify the object they are called on. 6 | 7 | For example: 8 | 9 | ```ruby 10 | name = "Ruby Monstas" 11 | puts name.downcase 12 | puts name 13 | ``` 14 | 15 | This will output: 16 | 17 | ```ruby 18 | ruby monstas 19 | Ruby Monstas 20 | ``` 21 | 22 | As you can see the method `downcase` has returned a new String, which is the 23 | lowercase version of the String that the method is being called on. When we 24 | output the original String on the next line, we can then see that it's still 25 | the same: The method `downcase` does not modify the String. 26 | 27 | However, there also are variants of some of these methods, which end in an 28 | exclamation mark `!`. These methods are called "bang methods", and they usually 29 | modify the object that they're being called on. 30 | 31 |

32 | Bang methods end with an exlamation mark, and often modify the object they are called on. 33 |

34 | 35 | For example, next to the method `downcase` Strings also have a method `downcase!`. 36 | 37 | Let's try that: 38 | 39 | ```ruby 40 | name = "Ruby Monstas" 41 | puts name.downcase! 42 | puts name 43 | ``` 44 | 45 | This will output: 46 | 47 | ```ruby 48 | ruby monstas 49 | ruby monstas 50 | ``` 51 | 52 | As you can see calling the method `downcase!` on the second line has modified 53 | the String itself (the object that `name` refers to), and *also* returned the 54 | new downcased version. 55 | 56 | Nowadays programmers have learned that using these methods has a number of 57 | disadvantages, and usually should be avoided, unless there are very good 58 | reasons for it (usually, there are none). 59 | 60 |

61 | Use bang methods with caution. 62 |

63 | 64 | We mostly mention these methods because you might see them used elsewhere. 65 | -------------------------------------------------------------------------------- /source/09-objects/09-class_methods.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/09-objects/09-class_methods.md -------------------------------------------------------------------------------- /source/09-objects/20-notes.md: -------------------------------------------------------------------------------- 1 | 2 | 53 | 54 | -------------------------------------------------------------------------------- /source/10-writing_methods.md: -------------------------------------------------------------------------------- 1 | # Writing Methods 2 | 3 | *A named block of code that takes input and returns output* 4 | 5 | Everything in programming is about data, and doing interesting things with it. 6 | If you think about Twitter, it essentially takes 140 characters of text 7 | (which is data) from their users and displays it to others. 8 | 9 | You have already learned about the most important primitive (simple) types of 10 | data in Ruby: numbers, strings, true, false and nil. And we've had a look at 11 | the two most common data structures, Arrays and Hashes. 12 | 13 | All of this is about the *data* part. Methods on the other hand are about the 14 | *doing interesting things* part. 15 | 16 | In this chapter we'll have a closer look at the anatomy of a method, and you'll 17 | learn how to implement, and use, your own methods. 18 | 19 | ## Methods define behaviour 20 | 21 | Methods are all about defining *behaviour*, so that they can be applied to 22 | different bits of data in different contexts easily. 23 | 24 | For example, there are methods that do things like: transforming a String, 25 | sorting a list, reading a CSV or Excel file, sending an email, signing in to 26 | Facebook, sending a Tweet. 27 | 28 | Another way of putting this is: Methods are a way of assigning a name to a 29 | certain piece of code. Just like a variable allows to "look up" or refer to 30 | the object that the name was assigned to ... methods allow to execute their 31 | code. 32 | 33 |

34 | Variables name things, methods name behaviour (code). 35 |

36 | 37 | Methods make code re-usable, by the way of packaging ("encapsulating") code and 38 | sticking a name on it. 39 | 40 | As you have seen Ruby comes with lots of methods predefined - written by 41 | experienced programmers. So, unless you're studying computer science and you're 42 | faced with the exercise of implementing your own, complicated sorting 43 | algorithms for data collections, ... you'll just use the method `sort` that 44 | already comes defined for Arrays in Ruby out of the box. 45 | 46 | Ok, let's see what makes up a method, and how we can define our own ones. 47 | 48 | -------------------------------------------------------------------------------- /source/10-writing_methods/01-constituents.md: -------------------------------------------------------------------------------- 1 | # What makes a method 2 | 3 | There are four things that constitute a method: 4 | 5 | * a name 6 | * a block of code 7 | * (optionally) accepting input 8 | * returning output 9 | 10 | Not all methods actually need input (so they don't take any), and not always do 11 | we care about the output that a method returns. 12 | 13 | Imagine a vending machine where you can chip in some money, press some buttons, 14 | and the machine will spin a few gears and wheels, and spit out the chocolate 15 | bar you were after. 16 | 17 | If the vending machine was a method, then your money, as well as the buttons 18 | you press are the *input*. The way the machine internally spins certain 19 | mechanics is the *block of code*, the stuff it does internally. And the product 20 | that it dispenses is the *return value*. 21 | 22 | Although this would be an odd thing to mention to a non-programmer, we could 23 | say that a vending machine is a way of "transforming" money and data into 24 | chocolate. 25 | 26 | Methods are a lot like that. 27 | 28 |

29 | Methods have a name, take some input, do something with it, and return a result. 30 |

31 | 32 | Programmers usually don't use the term "input" in this context. Instead we say 33 | that a method accepts a number of *arguments* (pieces of input). And instead of 34 | "output" we use the term *return value*: the thing that we get back from the 35 | method. 36 | 37 |

38 | A method's input is referred to as "arguments", while its output is called a 39 | "return value". 40 |

41 | 42 | This will become more clear in the following chapters. Let's define a method next. 43 | -------------------------------------------------------------------------------- /source/10-writing_methods/02-definition.md: -------------------------------------------------------------------------------- 1 | # Defining a method 2 | 3 | So far you have seen methods that "belong" to objects, or, in other words, 4 | are defined on objects, and can be called on objects. E.g. you have seen the 5 | method `downcase` which is defined on every string. 6 | 7 | However, Ruby also knows methods that are not defined on any of these objects. 8 | They're sort of "stand alone" methods. 9 | 10 | For example, you can try this in `irb`: 11 | 12 | ```ruby 13 | $ irb 14 | > is_a?(Object) 15 | true 16 | ``` 17 | 18 | We'll use this type of methods in this chapter because we want to focus on the 19 | characteristics of methods. If you're curious what's up with them have a look 20 | at the bonus chapter about the [top-level object](bonus/top_level.html). Later 21 | when you learn how to define your own classes we also look at defining methods 22 | for these. 23 | 24 | Ok, let's get started. 25 | 26 | Suppose we need to define a simple method that takes a number, adds the number 27 | `2` to it, and returns the result. Here's how we can do that: 28 | 29 | ```ruby 30 | def add_two(number) 31 | number + 2 32 | end 33 | ``` 34 | 35 | This *defines* a method. It does not *use* it, yet: You only build and place 36 | that vending machine, so it can be used (by yourself, or others) later. 37 | 38 | Let's walk through this method definition step by step: 39 | 40 | Ruby will start reading the code at the top, and find the keyword `def`. This 41 | tells Ruby that we're about to define a new method. 42 | 43 | Methods need a name, so Ruby looks for it next, and finds the word `add_two`. 44 | 45 | Ruby then checks if we define anything to "input" to the method (remember, this 46 | is optional). She finds the parentheses, and knows that we're about to define 47 | a list of things that can be given to the method. This list is called an 48 | *argument list*. 49 | 50 |

51 | An argument list defines names for objects passed to the method, enclosed by 52 | parentheses right after the method name. 53 |

54 | 55 | In our case the argument list has one single argument `number`, which means 56 | our method can accept one single thing (object). 57 | 58 | The next line is the block of code that our method has ("encapsulates"). This 59 | is also referred to as the *method body*. In our case that's just one single 60 | line because the operation that our method encapsulates is very simple. Other 61 | methods (think of `sort`, defined on Arrays) would require more code, and are 62 | longer. 63 | 64 | Inside the method body the arguments are known as local variables: You can see 65 | how the code in our method body uses the variable name `number`. 66 | 67 | Finally the keyword `end` tells Ruby that we're done with the method body, and 68 | the method definition. 69 | 70 | All we've done so far is defining the method, we haven't used it for anything, 71 | yet. We'll do that in the next chapter. 72 | 73 |

74 | On formatting: Note that the keywords def and end sit 75 | on the same level, while the method body is indented by two spaces. Also, there 76 | are no space before or inside the argument list, i.e. the () 77 | parentheses. 78 |

79 | -------------------------------------------------------------------------------- /source/10-writing_methods/03-usage.md: -------------------------------------------------------------------------------- 1 | # Using (calling) a method 2 | 3 | Once defined we can use our method. 4 | 5 | As programmers we usually say that we "call" a method. This means we ask Ruby 6 | to execute the code that the method body has, with some given arguments 7 | (input), if any. 8 | 9 | Here's how that looks: 10 | 11 | ```ruby 12 | def add_two(number) 13 | number + 2 14 | end 15 | 16 | puts add_two(3) 17 | ``` 18 | 19 | Let's inspect what's happening here under the microscope. If you don't 20 | understand each part of this just yet, don't worry, we'll get back to all of 21 | this. 22 | 23 | On the first three lines Ruby defines the method `add_two` as discussed above. 24 | 25 | * On the next line Ruby will look at the bit `add_two(3)` first. She recognizes 26 | that we are referring to a method defined earlier, and this will tell her we 27 | want to call (execute, use) this method. 28 | * In order to do so she first needs to look at what's inside the parantheses 29 | `()` so she can pass it on. She finds the `3` and creates a new object 30 | (number) for it. 31 | * Now Ruby is ready to actually call (execute, use) the method, passing the 32 | number `3`. 33 | 34 | So Ruby now deviates from the normal flow, which just goes from top to bottom 35 | in our file. Instead she now jumps into the method body. 36 | 37 | In this moment she now *assigns* the number `3` to a local variable `number` 38 | before she starts executing the method body. 39 | 40 | That's right. This is how method *arguments* work: 41 | 42 | When a method is called and objects are passed as arguments, then Ruby 43 | implicitely defines local variables with the argument names. She assigns the 44 | passed objects to the variable names that are in the argument list. These local 45 | variables are then available in the method body. 46 | 47 | In our case we have just one argument `number`. So we get one local variable 48 | `number` with the object `3` assigned (because that's the object passed when we 49 | called the method). 50 | 51 | You can imagine the method body now reads like this: 52 | 53 | ```ruby 54 | number = 3 55 | number + 2 56 | ``` 57 | 58 | Ok, so we're inside the method body now: 59 | 60 | * Ruby will now execute (evaluate, run) the method body (again, going from top 61 | to bottom), which in our case is just a single line with the expression 62 | `number + 2`. 63 | * Because `number` is assigned `3` this expression will evaluate to `5`. 64 | * Since this line of code is the last line in the method body, the value `5` 65 | also is the value returned from the method call. 66 | 67 | So Ruby now jumps back out of the method. The expression `add_two(3)` has just 68 | returned the object `5`. Imagine the last line now reads like this instead: 69 | 70 | ```ruby 71 | puts 5 72 | ``` 73 | 74 | And that will now print the number `5` to the screen. 75 | 76 | Let's have a closer look at that thing with the return value ("output"), as 77 | we just rushed over that a little. 78 | -------------------------------------------------------------------------------- /source/10-writing_methods/04-return_values.md: -------------------------------------------------------------------------------- 1 | # Return values 2 | 3 | In Ruby, a method always return exactly one single thing (an object). 4 | 5 | The returned object can be anything, but a method can only return one thing, 6 | and it also *always returns something*. 7 | 8 |

9 | Every method always returns exactly one object. 10 |

11 | 12 | The object returned could be the object `nil`, meaning "nothing", but it still 13 | is an object. Also, in order to return a bunch of things at once we could 14 | return an Array that holds the things that we are interested in, but the Array 15 | itself is just one object. 16 | 17 | Also note that in Ruby we do not have to use the statement `return`, as in 18 | other languages. In fact, most Ruby code does not use the keyword `return` at 19 | all. 20 | 21 | This is extremely convenient, but it is also something we need to learn: 22 | 23 | If we don't do anything else, then a method will return *the value that was 24 | returned from the last evaluated statement*. Most often, this is the last line 25 | in the method body. 26 | 27 | This is important to understand. Please read that sentence again: 28 | 29 |

30 | If we don't do anything else, then a method will return the return value of 31 | the last evaluated statement. 32 |

33 | 34 | In our example method ... 35 | 36 | ```ruby 37 | def add_two(number) 38 | number + 2 39 | end 40 | 41 | p add_two(3) 42 | ``` 43 | 44 | ... the last evaluated statement is the expression `number + 2`. Since in our 45 | example `number` is assigned `3` this expression returns the number `5`, and 46 | that is why the value returned by our method also is `5`. 47 | 48 | If, in certain cases, we do want to "return" from the method early, then we can 49 | still do this using the `return` statement. For now, you don't need to worry 50 | about this case. 51 | 52 | So, let's move on :) 53 | -------------------------------------------------------------------------------- /source/10-writing_methods/06-combining.md: -------------------------------------------------------------------------------- 1 | # Combining Methods 2 | 3 | We've discussed how to define a method, and how to call (use) it. 4 | 5 | What if one method is not enough? What if methods need to do more complicated 6 | things? 7 | 8 | Easy. We can call methods from other methods. 9 | 10 | For example, we could re-write ("re-implement") our method `add_two` using 11 | another method `add_one`, and simply call it twice: 12 | 13 | ```ruby 14 | def add_one(number) 15 | number + 1 16 | end 17 | 18 | def add_two(number) 19 | number = add_one(number) 20 | add_one(number) 21 | end 22 | 23 | puts add_two(3) 24 | ``` 25 | 26 | This would output `5` just like our previous examples. Do you understand how it 27 | works? 28 | 29 | Of course, in Ruby we could also just solve this whole thing with simply using 30 | the `+` operator. 31 | 32 | However, for the sake of the example, let's have a look how we could add a method 33 | that does the exact same thing as the `+` operator, too: 34 | 35 | ```ruby 36 | def sum(number, other) 37 | number + other 38 | end 39 | ``` 40 | 41 | We can now use that method like so: 42 | 43 | ```ruby 44 | puts sum(3, 2) 45 | ``` 46 | 47 | Which, again, would output `5`. 48 | 49 | Note that in this example our method `sum` now takes two arguments, and so, 50 | when we call (use) it, we also need to pass two numbers (i.e. add them inside 51 | the parentheses on the last line). 52 | 53 | Now, with this method in place we could change ("refactor") our previous methods 54 | to use it: 55 | 56 | ```ruby 57 | def sum(number, other) 58 | number + other 59 | end 60 | 61 | def add_one(number) 62 | sum(number, 1) 63 | end 64 | 65 | def add_two(number) 66 | sum(number, 2) 67 | end 68 | 69 | puts add_one(3) 70 | puts add_two(3) 71 | ``` 72 | 73 | Again, these examples are not super realistic, as we'd probably just use the 74 | `+` operator in the first place, in practice. 75 | 76 | However, we think this nicely demostrates how you can use one method from 77 | another ... and how different methods require different numbers of arguments. 78 | 79 | We'll look at a more realistic example in the next chapter. 80 | -------------------------------------------------------------------------------- /source/10-writing_methods/07-printing.md: -------------------------------------------------------------------------------- 1 | # Printing things 2 | 3 | Many of the exercises that you do while doing your first steps with Ruby 4 | basics include running a short Ruby program that outputs something to the 5 | terminal. 6 | 7 | So far, we have mostly used the method `puts` to do that. 8 | 9 | However, there's another method that is even more useful when we are trying 10 | to figure out what a program is doing, why it is doing it, and what a certain 11 | error might be about. 12 | 13 | This method is the method `p`. In order to understand `p` better, we want to 14 | look at another method first, which is the method `inspect`. 15 | 16 | The method `inspect` is available on any object in Ruby. It returns a string 17 | that is a representation of the object itself: a representation that is as 18 | close as possible to the code that you use to create the object. So `inspect` 19 | is useful to inspect objects, duh :) 20 | 21 | This becomes more clear when you try it in IRB: 22 | 23 | ```ruby 24 | $ irb 25 | > puts 5.inspect 26 | 5 27 | 28 | > puts "A string".inspect 29 | "A string" 30 | 31 | > puts [1, 2, 3].inspect 32 | [1, 2, 3] 33 | ``` 34 | 35 | As you can see the string returned from `inspect` is exactly the same as the 36 | Ruby code that we used to create the object. That is really convenient. 37 | 38 | However, typing `puts something.inspect` is quite a bit of work to do. That's 39 | 12 characters to type next to the object itself! 40 | 41 | Therefore Ruby has a method to make our lifes easier, and does this work for us. 42 | That's the method `p`. 43 | 44 | This method is implemented like so: 45 | 46 | ```ruby 47 | def p(object) 48 | puts object.inspect 49 | end 50 | ``` 51 | 52 | Whenever you are trying to figure out what a certain line of code does, what's 53 | assigned to a variable, or what a method call returns, we recommend to use `p` 54 | because it tells you exactly what the thing that you are looking at is. 55 | 56 | `puts` on the other hand tries to be smart. 57 | 58 | For example when you pass an array to `puts` then it will output each of the 59 | objects on a separate line: 60 | 61 | ```ruby 62 | $ irb 63 | > something = [1, 2, 3] 64 | > puts something 65 | 1 66 | 2 67 | 3 68 | ``` 69 | 70 | Also, the output for numbers and strings that contain numbers is exactly the 71 | same when you use `puts`: 72 | 73 | ```ruby 74 | $ irb 75 | > puts 123 76 | 123 77 | > puts "123" 78 | 123 79 | ``` 80 | 81 | From the output of `puts` it often is not clear whether the object that you are 82 | looking it is an array that contains numbers, or an array that contains 83 | strings, or just a long string that contains linebreaks. 84 | 85 | In short, `puts` is useful when you are writing a program that is supposed to 86 | actually output something to the screen. Like, this could be a command line 87 | tool that you write in order to make your own life easier at your job, and that 88 | is helpful at automating some repetitive work. Or it is useful in Ruby 89 | programming exercises :) 90 | 91 | On the other hand `p` is useful when you are trying to understand what your 92 | code does, e.g. when you are trying to figure out a certain error. 93 | 94 | -------------------------------------------------------------------------------- /source/11-writing_classes.md: -------------------------------------------------------------------------------- 1 | # Writing classes 2 | 3 | *Finally, our first own class* 4 | 5 | Since in Ruby "everything is an object", we have worked with objects quite a 6 | bit already. 7 | 8 | We've created numbers, Strings, and seen objects like `true`, `false`, and 9 | `nil`. We have also looked at their class names by calling, e.g. `"a 10 | string".class`, and we have explored some other methods that these objects 11 | have. We have also talked about how you can define your own methods, and how 12 | you can call them, passing arguments as required. 13 | 14 | That means we now have all the tools that we need to finally learn how to 15 | define and use your own classes. And this is where things suddenly become even 16 | more fun! 17 | 18 | You can think of objects as having two things: They know *stuff*, and they can 19 | *do something* with stuff (their own stuff, as well as the stuff that gets 20 | passed to them). 21 | 22 | Imagine you were an object that is an instance of the class `Person`. Well, in 23 | a certain way, you actually are :) 24 | 25 | Since you are a `Person`, you are able to remember your own name. And you are 26 | able to do something with it: When asked, you can tell your name to others, 27 | that is, you can return it to the "caller", to whoever asked. 28 | 29 | We'll define this exact class in just a few chapters. However, before we do 30 | that, we'll first look at how to define the method `add_two` to a class 31 | `Calculator` ... just because we can, and because you're already familiar with 32 | these methods. 33 | 34 | -------------------------------------------------------------------------------- /source/11-writing_classes/01-definition.md: -------------------------------------------------------------------------------- 1 | # Defining classes 2 | 3 | Let's start by creating a class `Calculator`, and adding some methods to it, 4 | step by step. 5 | 6 | In Ruby, you define a class like this: 7 | 8 | ```ruby 9 | class Calculator 10 | end 11 | ``` 12 | 13 | That's all. It's not a very useful class, since it's completely empty, but 14 | it's a class. 15 | 16 |

17 | A class is defined using the keyword class, a name, and the 18 | keyword end. 19 |

20 | 21 | Also, you see that the class has the name `Calculator`, which starts with an 22 | uppercase letter. In Ruby, this is required, and you'd get an error if you 23 | tried to define a class `calculator`. 24 | 25 | Also, for class names that consist of several words the Ruby community has the 26 | convention to separate these words by uppercase letters, as in 27 | `RubyStudyGroup`. This is called CamelCase, because of the humps. Whereas for 28 | variable and method names we use underscores, and keep everything lowercase: 29 | `local_variable` and `method_name`. This is called snake_case. 30 | 31 |

32 | Class names must start with an uppercase letter, and should use CamelCase. 33 | Variable and methods names should use snake_case. 34 |

35 | 36 | Ok, back to our class `Calculator`. 37 | 38 | Since we've defined a full, valid class, we can now already use it to create a 39 | new, concrete calculator instance, an object from it. 40 | 41 | You can think about the instance as the concrete calculator object that you can 42 | hold in your hands, and use to do actual calculations. The class on the other 43 | hand is more like the idea or concept of a calculator, like the idea of it that 44 | you have when you order a calculator online. 45 | 46 | Ok, here's how to do create a new, concrete instance from our class: 47 | 48 | ```ruby 49 | Calculator.new 50 | ``` 51 | 52 | That's right. `new` is a method, and it is defined on the class itself (which, 53 | as you might remember, is also an object, so it can have methods). This method 54 | creates a new instance of the class, and returns it. 55 | 56 |

57 | The method new is defined on every class, and returns a new 58 | instance of the class. 59 |

60 | 61 | Cool. Let's have a look at that object: 62 | 63 | ```ruby 64 | p Calculator.new 65 | ``` 66 | 67 | The output will seem a little bit weird, and technical at first: 68 | 69 | ``` 70 | # 71 | ``` 72 | 73 | The format `#<...>` tells you that this object is not a simple thing like a 74 | number, string, or array. Instead, it just tells you the name of the class, 75 | `Calculator`, and the internal id that Ruby has assigned to this object. 76 | 77 | Every object has its own, unique, internal object id, and when I ran this code 78 | on my computer, Ruby assigned the id `0x007fb2fbe50910`. If you run it, 79 | you'll get a different one. In practice, most of the time, you can simply ignore 80 | this id. 81 | 82 | Also, we can check that our new calculator instance indeed is an instance of 83 | the class `Calculator`: 84 | 85 | ```ruby 86 | $ irb 87 | > class Calculator 88 | > end 89 | > calculator = Calculator.new 90 | > calculator.class 91 | => Calculator 92 | > calculator.is_a?(Calculator) 93 | => true 94 | ``` 95 | -------------------------------------------------------------------------------- /source/11-writing_classes/03-initializers.md: -------------------------------------------------------------------------------- 1 | # Initializing objects 2 | 3 | *In the moment of birth* 4 | 5 | Let's start over, and define a new class. 6 | 7 | Remember how we said that objects can be thought of as two things: They know 8 | *stuff*, and they can *do things*. 9 | 10 | Let's define a class `Person`. People obviously also know things, and can do things. 11 | 12 | Here's how to define a shiny, new, empty class `Person`: 13 | 14 | ```ruby 15 | class Person 16 | end 17 | ``` 18 | 19 | Again, that's not a very useful class, but we can instantiate it, and create an 20 | actual, concrete person instance (object) from it: 21 | 22 | ```ruby 23 | p Person.new 24 | ``` 25 | 26 | Now, before we add any behaviour (methods) to our class, we want to be able to 27 | give it some initial data: In our case, we want the person to know its own 28 | name. 29 | 30 | We can do this like so: 31 | 32 | ```ruby 33 | class Person 34 | def initialize(name) 35 | end 36 | end 37 | ``` 38 | 39 | You see that we add a method called `initialize` to the class, and this method 40 | accepts a single argument, which is called `name`. At the moment, this method 41 | is still empty. We'll add some code to it in a bit. 42 | 43 | The important bit to learn for you is: the method `initialize` is a special 44 | method with a special meaning in Ruby: 45 | 46 | Whenever you call the method `new` on a class, as in `Person.new`, the class 47 | will create a new instance of itself. It will then, internally, call the method 48 | `initialize` on the new object. Doing so it will simply *pass all the 49 | arguments* that you passed to `new` *on to* the method `initialize`. 50 | 51 | So we can now create a new person instance by calling ... 52 | 53 | ```ruby 54 | Person.new("Ada") 55 | ``` 56 | 57 | ... and the string `"Ada"` will be passed on to our `initialize` method, and 58 | end up being assigned to the local variable `name`. 59 | 60 |

61 | The special method initialize is called under the hood when the 62 | object has been created by the class method new. 63 |

64 | 65 | Obviously, our `initialize` method does not *do* anything with the String 66 | passed, yet. That's right. We'll get to that in the next chapter. 67 | 68 | To recap, when you call `new` on the class `Person`, and pass the string 69 | `"Ada"` then the method `new` will create a new instance of the class, and call 70 | `initialize` on it, passing the same argument list, which in our case is the 71 | single string `"Ada"`. 72 | 73 | When we create a new instance of a class by the way of calling the method `new` 74 | on that class, we also say that we "instantiate" that object: By calling 75 | `Person.new` we instantiate a new person object. 76 | 77 | 78 | -------------------------------------------------------------------------------- /source/11-writing_classes/04-instance_variables.md: -------------------------------------------------------------------------------- 1 | # Instance variables 2 | 3 | *An object's own knowledge* 4 | 5 | Now that you understand how the string that we pass to the method `new` 6 | ends up being passed to the new object's `initialize` method, we can start 7 | improving `initialize`, so it does something with the string, and actually 8 | *initializes* our new object: 9 | 10 | ```ruby 11 | class Person 12 | def initialize(name) 13 | @name = name 14 | end 15 | end 16 | ``` 17 | 18 | This introduces another new concept: `@name` is a new type of variable, called 19 | an "instance variable". 20 | 21 | The body of the `initialize` method now does nothing else but assign the value 22 | of the local variable `name` to an instance variable `@name`. 23 | 24 | You remember how we said that each method has its own local scope, which is 25 | created when the method is called, and populated with local variables from the 26 | arguments list. You have also learned that this scope is erased, and thrown 27 | away when Ruby exits the method body and returns from the method. And that 28 | local variables that are visible in one method are not visible in other 29 | methods: that's why they are called local. 30 | 31 | Now, the thing is: Every *object* also has its own scope. 32 | 33 | An object's scope is populated with instance variables, in the moment we assign 34 | something to them. And they are visible *everywhere* in the object, that is, in 35 | every method that the object has. 36 | 37 |

38 | Instance variables live in, and are visible everywhere in the object's scope. 39 |

40 | 41 | You can think of the object's scope as your own knowledge, or memories. 42 | 43 | For example, you know your name, your email address, and your email password. 44 | You keep this knowledge around, and you can use it when you do things (such as 45 | responding to another person). Likewise, an object keeps its instance variables 46 | around, as long as the object exists. 47 | 48 | Ok, let's see how that works in practise. 49 | 50 | If you create, and output an instance of our class `Person`, you'll see that 51 | Ruby now prints out the instance variable, too: 52 | 53 | ```ruby 54 | person = Person.new("Ada") 55 | p person 56 | ``` 57 | 58 | The first line creates a new instance of the class `Person`, passing the string 59 | `"Ada"`, and assign this new object to the variable `person`. The second line 60 | will then print it out: 61 | 62 | ``` 63 | # 64 | ``` 65 | 66 | As you can see this includes the instance variable `@name` with the value 67 | `"Ada"`: This specific, concrete instance of the class `Person` knows their 68 | name. 69 | 70 | You can think of this as you, as a programmer, creating this new person, and 71 | in the moment of its creation, its birth, you also give it a name. Which kind 72 | of how it works with real people, too, isn't it? 73 | 74 | Hmm, well ... Yeah, sort of. Anyhow, let's move on. 75 | -------------------------------------------------------------------------------- /source/11-writing_classes/06-attribute_writers.md: -------------------------------------------------------------------------------- 1 | # Attribute writers 2 | 3 | *Setting information* 4 | 5 | Imagine in our application a person not only needs a name, but also a password. 6 | However, let's also imagine that, at the time of the creation of a new person 7 | instance, this password is not yet known. (Who would give a toddler an email 8 | password anyway?) 9 | 10 | Instead we want to be able to tell the person object about its email password 11 | later. 12 | 13 | We can do this like so: 14 | 15 | ```ruby 16 | class Person 17 | def initialize(name) 18 | @name = name 19 | end 20 | 21 | def name 22 | @name 23 | end 24 | 25 | def password=(password) 26 | @password = password 27 | end 28 | end 29 | ``` 30 | 31 | As you can see, the method `password=` does nothing else but take a single 32 | argument (called `password`) and assign the value of this local variable to the 33 | instance variable `@password`. 34 | 35 | This method's structure looks exactly the same as the method `initialize`, 36 | doesn't it? Execpt that `initialize` is called whenever you call `new` on the 37 | class. Our new method `password=` needs to be called on the object itself, 38 | once it has been created. 39 | 40 | Again, because this kind of method is used so often, there's another name for 41 | it: it's an attribute writer. (And again, we think it should have been called 42 | an "instance variable writer" instead.) 43 | 44 | Now, we can use the attribute writer like so: 45 | 46 | ```ruby 47 | person = Person.new("Ada") 48 | person.password=("super secret") 49 | p person 50 | ``` 51 | 52 | If you execute this, then it will print out: 53 | 54 | ``` 55 | # 56 | ``` 57 | 58 | So, yeah, we can see that, after calling `person.password=("super secret")` 59 | the object now has an instance variable defined, i.e., the person now knows 60 | their password, too. 61 | 62 |

63 | An attribute writer allows setting an instance variable. 64 |

65 | 66 | That method call looks a little odd though, doesn't it? 67 | 68 | Remember what we've said above about the syntax sugar that Ruby adds for the 69 | assignment operator `=`? 70 | 71 | Exactly the same works for attribute writers, that is, methods that end with an 72 | equals sign `=`. 73 | 74 | That's right. So we can also write this instead: 75 | 76 | ```ruby 77 | person = Person.new("Ada") 78 | person.password = "super secret" 79 | ``` 80 | 81 | And this reads just so much better, doesn't it? 82 | 83 | Just remember that, under the hood, when running your code, Ruby translates the 84 | line `person.password = "something"` to `person.password=("something")`, and 85 | this simply calls the method `password=`, passing the value on the right hand 86 | side as an argument: it's just another method :) 87 | 88 | We think this is pretty cool. 89 | -------------------------------------------------------------------------------- /source/11-writing_classes/07-state_and_behaviour.md: -------------------------------------------------------------------------------- 1 | # State and behaviour 2 | 3 | *Data and methods* 4 | 5 | Lets have another look our class definition for `Person`: 6 | 7 | ```ruby 8 | class Person 9 | def initialize(name) 10 | @name = name 11 | end 12 | 13 | def name 14 | @name 15 | end 16 | 17 | def password=(password) 18 | @password = password 19 | end 20 | end 21 | ``` 22 | 23 | Do you notice something? 24 | 25 | Our class demonstrates an important thing about objects: 26 | 27 | There's a way to ask a person for their `name`, but no way to set a new name. 28 | On the other hand there's a way to set a new password to the person, but no way 29 | to ask for it. 30 | 31 | If you think about it, that makes sense, doesn't it? 32 | 33 | If you join our Ruby beginners study group for the first time, and we ask you for 34 | your name, you'll happily tell it. But if we ask for your Gmail password, you 35 | will probably just laugh at us, or stare at us, or show some other error 36 | message. ;) In any case, you won't tell us your email password, because that's 37 | private information. 38 | 39 | The same is also true for objects. 40 | 41 | Every object has its own object scope that might hold a bunch of instance 42 | variables. These are private to the object. Our person object knows their 43 | password, once it has been given to them. But from then on, they won't tell 44 | anyone the password, because there's no method for that. On the other hand, 45 | there's a method `name`, which is an attribute reader, so we can ask our person 46 | object for their name. But there's no way for others to give a new name to the 47 | person, because there's no method for that, no attribute writer `name=`. 48 | 49 | This concept is called *encapsulation*, and it is one of the main motivations 50 | behind the whole paradigm of object-oriented programing: 51 | 52 | We can say that an object encapsulates state (data, knowledge), which is 53 | private to the object, and exposes behaviour by the way of having publicly 54 | accessible methods. 55 | 56 |

57 | Objects have state (instance variables) and behaviour (methods). 58 |

59 | 60 | So, we have now created our first little class, and it's one that you could 61 | actually see in real applications. 62 | 63 | -------------------------------------------------------------------------------- /source/13-blocks.md: -------------------------------------------------------------------------------- 1 | # Blocks 2 | 3 | *Like methods, but without a name* 4 | 5 | Blocks are one of the things programmers absolutely love about Ruby. They are 6 | an extremely powerful feature that allows us to write very flexible code. At 7 | the same time they read very well, and they are used all over the place. 8 | 9 | So, what is a block? 10 | 11 | A block, essentially, is the same thing as a method, except it does not have a 12 | name, and does not belong to an object. 13 | 14 | I.e. a block is an anonymous piece of code, it can accept input in form of 15 | arguments (if it needs any), and it will return a value, but it does not have a 16 | name. 17 | 18 | Moreover, blocks can only be created by the way of passing them to a method 19 | when the method is called. 20 | 21 |

22 | A block is a piece of code that accepts arguments, and returns a value. A block 23 | is always passed to a method call. 24 |

25 | 26 | Let's jump right in: 27 | 28 | ```ruby 29 | 5.times do 30 | puts "Oh, hello from inside a block!" 31 | end 32 | ``` 33 | 34 | As you can see `times` is a method that is defined on numbers: `5.times` calls 35 | the method `times` on the number `5`. 36 | 37 | Now, when this method is called the only thing passed is a block: that is the 38 | anonymous piece of code between `do` and `end`. There are no objects passed as 39 | arguments to the method `times`, instead it just passes a block. 40 | 41 | The method `times` is implemented in such a way that it simply calls (executes) 42 | the block 5 times, and thus, when you run the code, it will print out the 43 | message `"Oh, hello from inside a block!"` 5 times. 44 | 45 | The code almost reads like an English sentence *Five times do output this 46 | message*, right? It does, and that's one of the reasons why Rubyists love 47 | using blocks. 48 | 49 | One of the things that seem rather hard to grasp about blocks is that 50 | 51 | * First, they are anonymous chunks of code. 52 | * Second, they are passed to methods just like other objects. 53 | * And third, they still can be called (just like methods), from inside the 54 | method that it was passed to. 55 | 56 | Does that make sense? 57 | 58 | Imagine you are the object that represents the number `5`. You are a number and 59 | you do know your own value. 60 | 61 | Now I hand you a piece of paper saying: `Please print the following on the 62 | screen: "Oh, hello!"`, and I ask you to execute this instruction as many times 63 | as the value that you know. 64 | 65 | You'd go ahead and follow the instructions on the paper, and thus print out the 66 | message. You repeat this 5 times, because `5` is the value that you know. 67 | 68 | This is pretty much how the method `times` on numbers works, and how blocks 69 | work: `times` takes the block (the instructions), and runs it as many times 70 | as the value of the number. 71 | 72 | To summarize: Methods can not only accept input in the form of objects passed 73 | as arguments. They can also accept this one special piece of input, which is 74 | an anonymous block of code. And they can then call (execute) this block of code 75 | in order to do useful things with it. 76 | 77 | Let's look at some other aspects of how blocks work next. 78 | -------------------------------------------------------------------------------- /source/13-blocks/01-syntax.md: -------------------------------------------------------------------------------- 1 | # Alternative block syntaxes 2 | 3 | Next to the syntax shown before, using `do` and `end`, Ruby comes with an 4 | alternative syntax, which uses curly braces for defining a block. 5 | 6 | These two statements do exactly the same: 7 | 8 | ```ruby 9 | 5.times do 10 | puts "Oh, hello!" 11 | end 12 | 13 | 5.times { puts "hello!" } 14 | ``` 15 | 16 | Both statements define a block, which is passed to the method `times`. And 17 | both blocks contain a single line of code. 18 | 19 |

20 | Blocks can be defined enclosing code in do and end, 21 | or curly braces {}. 22 |

23 | 24 | So, when do you use one or the other syntax? 25 | 26 | In the Ruby community there's the convention to use curly braces if you have a 27 | single line block and it fits nicely on the same line (as, in our example, it 28 | does). 29 | 30 | Whenever you need to have more than one line in your block, then you use the 31 | syntax using `do` and `end`. Sometimes people also use the `do` and `end` 32 | syntax when they feel it makes the code more readable. 33 | 34 |

35 | Use curly braces {} for blocks, when the code fits on one line. 36 |

37 | 38 | -------------------------------------------------------------------------------- /source/13-blocks/02-arguments.md: -------------------------------------------------------------------------------- 1 | # Block arguments 2 | 3 | Blocks make a lot of sense on methods that are defined on collections like 4 | arrays and hashes. 5 | 6 | Let's have a look at some examples with arrays. 7 | 8 | In our previous example that used the method `times` our block did not accept 9 | an argument. A block that accepts an argument looks like this: 10 | 11 | ```ruby 12 | [1, 2, 3, 4, 5].each do |number| 13 | puts "#{number} was passed to the block" 14 | end 15 | ``` 16 | 17 | And, again, this is the same as: 18 | 19 | ```ruby 20 | [1, 2, 3, 4, 5].each { |number| puts "#{number} was passed to the block" } 21 | ``` 22 | 23 | It is unknown to us why Matz has chosen to not enclose the argument list of 24 | a block with round parentheses just like method argument lists. Instead, Ruby 25 | wants us to use vertical bars (we call them "pipes"). 26 | 27 | So, for blocks, `do |number|` is the same that is `def add_two (number)` for a 28 | method definition, except that the method wants a name while a block is 29 | anonymous: `|number|` and `(number)` both are argument lists. The first one 30 | is used for blocks, the second one for methods. 31 | 32 |

33 | Block arguments are listed between pipes |, instead of parentheses. 34 |

35 | 36 | Now, when you run the code example above, you'll see the message printed out 37 | for each of the numbers contained in the array. 38 | 39 | Does that make sense? Again, our code almost reads like an English sentence: 40 | 41 | *With this array for each of its elements, naming it `number`, output the 42 | following message.* 43 | 44 | The method `each` is defined on arrays, and it does just this: 45 | 46 | It takes each of the elements in the array and calls the block, passing the 47 | element as an argument. The block can then do whatever you want it to do with 48 | the element. In our case we interpolate it into a string and print it out to 49 | the screen. 50 | -------------------------------------------------------------------------------- /source/13-blocks/04-ioc.md: -------------------------------------------------------------------------------- 1 | # Inversion of control 2 | 3 | In Ruby there are a lot more methods that accept blocks, and they do very 4 | different things. However, they have one thing in common: 5 | 6 | By accepting a block, from you as a programmer, the method can pass control to 7 | you. 8 | 9 | This design is an example for the principle of *inversion of control*, and 10 | it's the real reason why Rubyists love blocks so much. 11 | 12 | What does that mean? 13 | 14 | In short it means that Matz, the creator of Ruby, put a tool in place that can 15 | be used to allow methods to pass control to its users (i.e. you as a 16 | programmer). 17 | 18 | "Control" in this context refers to the question who gets to decide how things 19 | work. 20 | 21 | In older languages, where there was no such tool, people either had to 22 | implement lots of very specific methods, and *guess* what users might need in 23 | the future. Or they'd decide to just not implement any of these methods at all. 24 | 25 | For example, in Ruby, we don't have to define lots of methods like `select_odd`, 26 | `select_even`, `select_lesser_than`, `select_greater_than` and so on, ... where 27 | each of these methods would be useful for one very specific usecase. 28 | 29 | Instead, the class `Array` only has to implement one single, very generic 30 | method for arrays: `select`. Since Ruby has blocks, the method can allow you 31 | (as a programmer) to specify the criterion yourself: by passing a piece of 32 | code, in the form of block to the method. 33 | 34 | That way Ruby lets you, as a programmer, take over control, and specify what is 35 | used as a criterion to select elements. 36 | 37 | One of the reasons we mention this is because we think this is a nice example 38 | of a pretty abstract principle applied to software design. There are lots of 39 | other principles like these, and they'll make more and more sense to you over 40 | time. Programming languages and code, from this perspective, is a subject of 41 | design, and thus art, as well as social and cultural questions ... much rather 42 | than strictly logical or technical ones. 43 | -------------------------------------------------------------------------------- /source/13-blocks/05-iterators.md: -------------------------------------------------------------------------------- 1 | # Iterators 2 | 3 | Methods on arrays and hashes that take a block are also called iterators. 4 | 5 | We say they *iterate over the array*, meaning that these methods take each 6 | element of the array and do something with it. 7 | 8 | In Ruby iterators are "chainable", adding functionality on top of each other. 9 | 10 | That means that, if you do not pass a block to an iterator method, such as 11 | `each`, `collect`, `select`, then you'll get an *iterator object* back. You can 12 | then call more methods on these iterator objects, and finally pass a block. 13 | Like so: 14 | 15 | ```ruby 16 | numbers = [1, 2, 3, 4, 5].collect.with_index do |number, index| 17 | number + index 18 | end 19 | p numbers 20 | ``` 21 | 22 | This will print out: 23 | 24 | ``` 25 | [1, 3, 5, 7, 9] 26 | ``` 27 | 28 | What's going on here? 29 | 30 | The method `with_index` can be called on any iterator object. All it does is 31 | pass the index of the element within the array to the block, as a second block 32 | argument, in addition to the element itself. 33 | 34 | Inside of the block we can then use it, and add the index to the number itself. 35 | 36 | So for the first iteration it will call the block with `1` and `0`, since `0` is 37 | the first "position", that is, index. It therefore returns `1`. For the second 38 | iteration it calls the block with `2` and `1`, and returns `3`, and so on. 39 | 40 | Therefore the method call eventually returns the array `[1, 3, 5, 7, 9]`. 41 | 42 |

43 | Iterators in Ruby are chainable. 44 |

45 | 46 | 47 | -------------------------------------------------------------------------------- /source/13-blocks/10-methods_using_blocks.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/13-blocks/10-methods_using_blocks.md -------------------------------------------------------------------------------- /source/14-conditionals.md: -------------------------------------------------------------------------------- 1 | # Conditionals 2 | 3 | *If this is true, then do that. Otherwise do something else.* 4 | 5 | Often we want to check for a certain condition, and then based on it, do 6 | either one thing or another. Or we want to do something only if a condition is 7 | true. 8 | 9 | All practical programming languages have some way of expressing this, and in 10 | Ruby it looks like so: 11 | 12 | ```ruby 13 | number = 5 14 | 15 | if number.between?(1, 10) 16 | puts "The number is between 1 and 10" 17 | elsif number.between?(11, 20) 18 | puts "The number is between 11 and 20" 19 | else 20 | puts "The number is bigger than 20" 21 | end 22 | ``` 23 | 24 | You can probably guess what it does, and how it works, can't you? 25 | 26 | Let's walk through it one by one: 27 | 28 | * If you run this code it will print out `The number is between 1 and 10`, 29 | because the number assigned to the variable `number` on the first line is 30 | `5`, and for this number the method call `number.between?(1, 10)` returns 31 | `true`. Ruby will therefore execute the code in the `if` branch: The `if` 32 | branch is the block of code that comes after the line with the `if` 33 | statement, and that is indented by two spaces. Once it is done executing 34 | the `if` branch Ruby will simply ignore the rest of the statement. 35 | 36 | * If you change the number `5` on the first line to `15`, and run the code 37 | again, then it will print out `The number is between 11 and 20`. In this 38 | case Ruby will, again, first check the first condition `number.between?(1, 39 | 10)`, but this time this method call returns `false`. Therefore, Ruby will 40 | ignore the `if` branch, and check the next condition on the `elsif` line: 41 | `number.between?(11, 20)`. Now, this method call returns true, because `5` is 42 | between `11` and `20`. Ruby will therefore execute the `elsif` branch, and 43 | print out this message. Again, once it is done executing the `elsif` branch 44 | Ruby will ignore the rest of the statement. 45 | 46 | * If you now change the number `15` to `25`, and run the code again, then it 47 | will print out `The number is bigger than 20`. Again, Ruby will first check 48 | the first condition, and find it returns `false`. It will check the second 49 | condition, which now also returns `false`. Therefore Ruby will then execute 50 | the `else` branch, and print out that message. 51 | 52 | The `elsif` and `else` statements and branches are optional: you don't need to 53 | have them. You can have an `if` statement without `elsif` or `else` branches, 54 | an `if` statement only with an `else`, or you could have an `if` statement with 55 | one or more `elsif` statements. Or combine all of them together: 56 | 57 | * There *must* be an `if` statement and branch. 58 | * There *can* be many `elsif` statements and branches. 59 | * There *can* be one `else` statement and branch. 60 | 61 | -------------------------------------------------------------------------------- /source/14-conditionals/01-shorthand.md: -------------------------------------------------------------------------------- 1 | # Shorthand syntax 2 | 3 | ## Trailing If 4 | 5 | One particularly nice feature in Ruby is that we can append the `if` statement 6 | to the code on the `if` branch if it's just a single line. So instead of this: 7 | 8 | ```ruby 9 | if number.odd? 10 | puts "The number is odd." 11 | end 12 | ``` 13 | 14 | we can also write this: 15 | 16 | ```ruby 17 | puts "The number is odd." if number.odd? 18 | ``` 19 | 20 | which not only saves us two lines, it also reads great! 21 | 22 | ## Unless 23 | 24 | Also, as well as `if` Ruby also knows a statement `unless` for when we want to do 25 | something if the condition does *not* apply (evaluate to true). And again, we 26 | can also append the `unless` statement to the end of the line, so these two are 27 | the same: 28 | 29 | ```ruby 30 | unless number.odd? 31 | puts "The number is not odd." 32 | end 33 | 34 | puts "The number is not odd." unless number.odd? 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /source/14-conditionals/02-return_values.md: -------------------------------------------------------------------------------- 1 | ## Conditionals return values 2 | 3 | Another extremely useful aspect about `if` and `unless` statements in Ruby is 4 | that they return values. 5 | 6 | Yes, that's right: 7 | 8 | An `if` statement, with all of its branches, as a whole, evaluates to the value 9 | returned by the statement that was last evaluated, just like a method does. 10 | 11 | For example, instead of this: 12 | 13 | ```ruby 14 | if number.even? 15 | puts "The number is even" 16 | else 17 | puts "The number is odd" 18 | end 19 | ``` 20 | 21 | we can also assign the return value of the `if` statement to a variable, and 22 | then output it: 23 | 24 | ```ruby 25 | message = if number.even? 26 | "The number is even" 27 | else 28 | "The number is odd" 29 | end 30 | 31 | puts message 32 | ``` 33 | 34 | Also, for the same reason, if we define a method that contains nothing but a 35 | single `if`/`else` statement, the method will, again, return the last statement 36 | evaluated: 37 | 38 | ```ruby 39 | def message(number) 40 | if number.even? 41 | "The number is even" 42 | else 43 | "The number is odd" 44 | end 45 | end 46 | 47 | puts message(2) 48 | puts message(3) 49 | ``` 50 | 51 | The first method call `message(2)` will output `The number is even`, and the 52 | second one `message(3)` will output `The number is odd`. 53 | -------------------------------------------------------------------------------- /source/15-operators.md: -------------------------------------------------------------------------------- 1 | # Operators 2 | 3 | We've already used some arithmetic operators (`+` and `*`) above. You have 4 | also learned that operators, under the hood, are just methods. Ruby just 5 | adds a little bit of syntax sugar on top of these methods, so they're sweeter 6 | to read, and write. 7 | 8 | There also are operators for comparing things, for logical calculations, and 9 | other operations. Let talk about some more of them. 10 | 11 | -------------------------------------------------------------------------------- /source/15-operators/01-arithmetical.md: -------------------------------------------------------------------------------- 1 | # Arithmetical operators 2 | 3 | For numbers, the operators `+` and `*` obviously mean the mathematical 4 | operations of adding and multiplying two numbers. Of course there are other 5 | arithmetical operators. Here's a full list: 6 | 7 | * `+` - addition 8 | * `-` - subtraction 9 | * `*` - multiplication 10 | * `/` - division 11 | * `**` - exponentiation 12 | * `%` - modulus (the rest of a division, e.g. `5 % 2` returns `1`) 13 | 14 | However, some of these methods are also defined on other objects, like strings 15 | and arrays. 16 | 17 | Try it yourself in IRB: 18 | 19 | ```ruby 20 | $ irb 21 | > "<3" + "!" 22 | => "<3!" 23 | 24 | > "<3" * 3 25 | => "<3<3<3" 26 | ``` 27 | 28 | As you can see, these operators mean something different for strings. But they 29 | also make sense, don't they? 30 | 31 | Adding one string to another just means that they will be concatenated into one 32 | longer string. And multiplying a string by a number means repeating it as many 33 | times. 34 | 35 | The same works for arrays: 36 | 37 | ```ruby 38 | $ irb 39 | > [1, 2] + [3, 4] 40 | => [1, 2, 3, 4] 41 | 42 | > [1, 2] * 3 43 | => [1, 2, 1, 2, 1, 2] 44 | ``` 45 | 46 | Again, adding two arrays means combining them into one big array. And 47 | multiplying an array with a number means getting a big array with the original 48 | elements repeated. 49 | -------------------------------------------------------------------------------- /source/15-operators/02-logical.md: -------------------------------------------------------------------------------- 1 | # Logical operators 2 | 3 | Logical operators are also, maybe more commonly, called boolean operators. 4 | 5 | The term "boolean" originates from the book "The Mathemetical Analysis of 6 | Logic" written by George Boole in 1847. Boolean logic has been fundamental in 7 | the development of computers, and programming, since at their core, computers 8 | are all about processing whether or not there is current flow: on vs off 9 | (true vs false). 10 | 11 | If you are curious, feel encouraged to google and read up on this online, but 12 | for now, we can simply look at the 3 fundamental boolean operators and 13 | what they do: `and`, `or`, and `not`. 14 | 15 | The operator `and` returns `true` if, and only if, both values also are `true`. 16 | So, only the expression `true and true` is also `true`. All of the expressions 17 | `true and false`, `false and true`, `false and false` evaluate to `false`. 18 | 19 | If you think about this, and come up with English sentences, then this will 20 | make a lot of sense: *At the restaurant I'll have a tomato soup IF it is vegan 21 | AND they still have some.* 22 | 23 | The operator `or` on the other hand returns `true` if at least one of the 24 | values is `true`. So, only if both values are `false`, the operator returns 25 | `false`. 26 | 27 | That's why it is logically correct to answer the question *Would you like tea 28 | or coffee for breakfeast?* with *Yes, please.* IF you'd like either tea, or 29 | coffee, or both. You'd only say *Hell, no!* if you'd like an orange juice 30 | instead :) 31 | 32 | The operator `not` simply returns the negated, opposite value. `not true` 33 | returns `false`, and `not false` returns `true`. Therefore, the following 34 | lines of code are the same: 35 | 36 | ```ruby 37 | puts "Always true" if not false 38 | puts "Always true" unless false 39 | ``` 40 | 41 | Each of these three operators comes in two versions: 42 | 43 | * `and` and `&&` 44 | * `or` and `||` 45 | * `not` and `!` 46 | 47 | The difference between them has to do with what is called "operator precedence". 48 | 49 | From math you know that `1 + 2 * 3` evaluates to `7`, not `9`. This is 50 | because the multiplication `*` operator binds stronger, and precedes the 51 | addition operator `+`. In other words `1 + 2 * 3` is same as `1 + (2 * 3)`, and 52 | not the same as `(1 + 2) * 3`. 53 | 54 | In Ruby, the operators `&&`, `||`, and `!` bind stronger than, and thus precede 55 | their fellows `and`, `or`, and `not`. 56 | -------------------------------------------------------------------------------- /source/15-operators/03-comparison.md: -------------------------------------------------------------------------------- 1 | # Comparison operators 2 | 3 | In order to compare things Ruby has a bunch of comparison operators. 4 | 5 | The operator `==` returns true if both objects can be considered the same. For 6 | example `1 == 1 * 1` will return `true`, because the numbers on both sides 7 | represent the same value. The expression `"A" == "A"` also returns `true` 8 | because both strings have the same value. 9 | 10 | Likewise, two arrays are equivalent when they contain the same elements, in the 11 | same order. For example `[1, 2] == [1, 2]` will return `true`, but `[1, 2] == 12 | [2, 3]` and `[1, 2] == [2, 1]` both will return `false`. 13 | 14 | Note that we say "considered the same" and "equivalent" because technically the 15 | two objects do not have to be (and most often, as in our examples) are not the 16 | same objects. E.g. while evaluating the expression `"A" == "A"` Ruby will 17 | actually create two different string objects which both contain a single 18 | character `A`. 19 | 20 | In practice this is almost always what you want. For the rare case when you 21 | actually need to check if two objects are the same object there's the method 22 | `equal?`. E.g., `"A".equal?("A")` returns `false`. 23 | 24 | Other comparison operators are: less than `<` , less than or equal `<=`, greater than `>`, 25 | and greater than or equal `>=`. They also work on numbers and strings, in the way 26 | you'll expect it. Open IRB and try a few combinations on numbers and strings. 27 | 28 | Comparison operators most often are used in order to formulate conditions 29 | in `if` statements. Like so: 30 | 31 | ```ruby 32 | number = 20 33 | puts "#{number} is greater than 10." if number > 10 34 | ``` 35 | 36 | The most funny operator in Ruby is `<=>`, because it's called the spaceship 37 | operator. No kidding :) It is rather rarely used, and it is useful for 38 | implementing custom ways of sorting things. 39 | -------------------------------------------------------------------------------- /source/15-operators/04-methods.md: -------------------------------------------------------------------------------- 1 | # Operators are methods 2 | 3 | As you have seen a number has methods named like the arithmetic operators `+`, 4 | `-`, `*`, and `/`. That's right! Interesting. 5 | 6 | If you think about this, it makes sense: If everything is an object then 7 | numbers are objects. If "doing things" means operating with methods by the way 8 | of calling them, then what would `+` be? A method of course. 9 | 10 | But if we call methods on objects using that dot `.` notation, then where are 11 | the dots in `2 + 3 * 4`? 12 | 13 | The trick is: Ruby adds them for you, silently. If you write the following code: 14 | 15 | ```ruby 16 | number = 2 + 3 * 4 17 | ``` 18 | 19 | Then Ruby will translate this to the following: 20 | 21 | ```ruby 22 | number = 2.+(3.*(4)) 23 | ``` 24 | 25 | Fun, isn't it? These operators are all methods on numbers, and they can be 26 | called just like any other method. (The same is true for lots of other 27 | operators, as you can see in IRB, when you run `1.methods.sort`.) The code 28 | above is valid Ruby code, and both lines do exactly the same. 29 | 30 | Ruby just adds a little bit of syntax in order to make it easier to read and 31 | write for us: It allows us to write `number = 2 + 3 * 4` instead of `number = 32 | 2.+(3.*(4))`, which is a pretty nasty thing to type. 33 | 34 | This is something called "syntax sugar", because it makes the language more 35 | sweet (no kidding). 36 | 37 | By the way, this works the same way for other things too. 38 | 39 | For example, you have learned about the array and hash syntax that uses square 40 | brackets `[]` for reading and writing. 41 | 42 | Ruby translates this code: 43 | 44 | ```ruby 45 | array = [1, 2, 3] 46 | array[3] = 4 47 | puts array[3] 48 | 49 | hash = { :one => 'eins', :two => 'zwei' } 50 | hash[:three] = 'drei' 51 | puts hash[:three] 52 | ``` 53 | 54 | to these method calls: 55 | 56 | ```ruby 57 | array = [1, 2, 3] 58 | array.[]=(3, 4) 59 | puts(array.[](3)) 60 | 61 | hash = { :one => 'eins', :two => 'zwei' } 62 | hash.[]=(:three, 'drei') 63 | puts(hash.[](:three)) 64 | ``` 65 | 66 | Knowing this can be useful when you want to write classes that look and feel 67 | similar to arrays or hashes, but behave differently. 68 | -------------------------------------------------------------------------------- /source/70-bonus.md: -------------------------------------------------------------------------------- 1 | # Bonus Chapters 2 | -------------------------------------------------------------------------------- /source/70-bonus/01-top_level.md: -------------------------------------------------------------------------------- 1 | # Top-level object 2 | 3 | So, we've learned that objects come with lots of methods attached to them. And 4 | we've seen how we can use them to do interesting things with the object, by the 5 | way of calling these methods. 6 | 7 | Now, if you pop into `irb`, or if you write the following code into an otherwise 8 | empty Ruby file, this works: 9 | 10 | ```ruby 11 | $ irb 12 | > is_a?(Object) 13 | true 14 | > methods 15 | [:to_s, :inspect, ... ] 16 | ``` 17 | 18 | So, as you can see we can use some methods in Ruby without even defining an 19 | object at all: They're already there. And the method `is_a?` tells us that this 20 | already is an object ... whatever "this" is. 21 | 22 | What's up with that? 23 | 24 | This is something that pretty much blows some people's minds, confuses the heck 25 | out of others, while yet others don't even notice or think about it at all. It 26 | is also something, that, when you ask around on your local Ruby meetup, very 27 | few people can explain in a satisfying, and clear way. And neither do we, yet. 28 | 29 | Let's still try. 30 | 31 | Whenever you open IRB and it presents that prompt to you, or when you create a 32 | new, empty Ruby file ... Ruby will not only execute your code: Before she does 33 | she first creates an empty, anonymous Object, and kind of places you inside of 34 | it. 35 | 36 | This empty Object kind of seems invisible, and it seems that those methods 37 | that we can define in this scope (space) were somehow "standing alone". In fact 38 | they aren't. They're defined on this empty, anonymous Object that we usually 39 | don't see. 40 | 41 | This Object often is referred to as the *top-level scope* in Ruby. 42 | 43 |

44 | The top-level scope is an empty, anonymous object. All Ruby code starts in here. 45 |

46 | 47 | Confusing? Yeah. Don't worry. This is something many Ruby programmers do and 48 | use everyday without ever asking the question why it works, exactly. 49 | 50 | Hopefully some of this will become more clear once you've written your 51 | own methods and classes. 52 | -------------------------------------------------------------------------------- /source/70-bonus/03-other_methods.md: -------------------------------------------------------------------------------- 1 | # Lots of other methods 2 | 3 | If you look at the methods that are defined on strings and arrays (e.g. run 4 | `[].methods.sort` or `{}.methods.sort` in IRB), then you'll find quite a bunch 5 | of method names that look like they are doing exactly what their names 6 | describe. 7 | 8 | For example, some of the things you can do with strings are: 9 | 10 | * `"a string".capitalize` returns `"A string"`, with the first letter uppercased. 11 | * `"a string".length` returns `8`, which is the length of the string. 12 | * `"a string".start_with?("a")` returns `true`, because the string starts with an `"a"`. 13 | * `"a string".include?("s")` returns `true`, because the string contains the character `"s"`. 14 | 15 | Some examples for useful methods on arrays are: 16 | 17 | * `[5, 1, 3].sort` returns another array, with the elements sorted: `[1, 3, 5]`. 18 | * `[5, 1, 3].size` returns `3`, the number of elements in the array. 19 | * `[1, 1, 1, 2].uniq` returns a new array with duplicate elements removed: `[1, 2]`. 20 | * `[1, 2, 3].join(", ")` returns a string `"1, 2, 3"`. 21 | * `[1, 2, 3].include?(2)` returns true because the array contains the number `2`. 22 | 23 | How do you find all these methods? 24 | 25 | The quickest way to find a certain method for an object often is to just ask 26 | Google: "ruby array sort". That will point you to the Ruby documentation. 27 | Another way is to read through all the methods for the class on the respective 28 | Ruby documentation page. And of course, you can also read through the method 29 | names returned by `[1, 2, 3].methods.sort`. 30 | -------------------------------------------------------------------------------- /source/70-bonus/04-questions_commands.md: -------------------------------------------------------------------------------- 1 | # Questions and commands 2 | 3 | Generally speaking, methods play one of two roles: They either answer a question, 4 | or they perform a command. 5 | 6 | For example, if we have a `user` object, and users have a name, then we would 7 | ask the user object for its name by calling the method `name`: `user.name` 8 | would return the user's name. Arrays know their size (how many elements they 9 | have), so we can ask an array: `[1, 2, 3].size` will return `3`. 10 | 11 | Another example is the method `sort` on arrays. This method does not actually 12 | sort the array that it is called on. Instead it returns a *new* array with the 13 | same values, but in a sorted order: 14 | 15 | ```ruby 16 | array = [3, 2, 1] 17 | p array.sort 18 | p array 19 | ``` 20 | 21 | This will output `[1, 2, 3]` and then `[3, 2, 1]`. So we see that the original 22 | array is still the same. 23 | 24 | Often questions need another bit of information passed. E.g., we can ask 25 | a string *Do you start with this character?*, and we'll need to pass the 26 | character that we are talking about: `"a string".start_with?("a")`. The answer 27 | to this question will be `true`. Or if we ask an array if it includes a certain 28 | element, then of course we need to pass that element, as in `[1, 2, 29 | 3].include?(1)` 30 | 31 | The other role that a method can play is being responsible for executing a 32 | certain command. 33 | 34 | For example in Rails objects that can be stored to the database have a method 35 | `save`. Of course, the purpose of this method is to save the object to the 36 | database. E.g. `user.save` would save some changes that we've made to the user 37 | before, like, maybe we have given them a new password. 38 | 39 | Another example is the method `sort!` on arrays. Different from the method 40 | `sort` (without an exclamation mark), this method tells the array to sort 41 | itself: 42 | 43 | ```ruby 44 | array = [3, 2, 1] 45 | array.sort! 46 | p array 47 | ``` 48 | 49 | If you run this code, then it will print out `[1, 2, 3]`: the array is now 50 | sorted. 51 | 52 | Another example for a method that is a command is the method `puts`. All it 53 | does is print something to the screen, and it always returns `nil`. 54 | 55 | Whenever you think about adding a new method to your code it makes sense to 56 | think about the role the method should have. Is it a question? Or a command? 57 | -------------------------------------------------------------------------------- /source/70-bonus/12-write_new_method.md: -------------------------------------------------------------------------------- 1 | # Writing a new method 2 | 3 | As programmers we like to split up our tasks, and do one thing after another. 4 | This allows us to focus on one small task, and once we've solved it, we move on 5 | to the next one. 6 | 7 | When you need to add some new functionality to your program you'll often 8 | find yourself thinking "I should add a method for this": methods add behaviour. 9 | 10 | Now, the first thing you should ask yourself is: "What is it that this method 11 | should do?" The answer to this gives you a hint for a good method name. 12 | 13 | Let's say you are working on an application that deals with emails, and the 14 | thing you are trying to accomplish is formatting an email. So your method name 15 | can be `format_email`. 16 | 17 | With this first task solved, knowing the method name, you can already go ahead 18 | and write down the method definition: 19 | 20 | ```ruby 21 | def format_email 22 | end 23 | ``` 24 | 25 | While this is a pretty useless method, since it does nothing at all, it already 26 | *is* a valid method. So that's good progress already: you've made the first 27 | step. 28 | 29 | The next question to ask is: "Does this method need to be given any information 30 | in order to do its thing?" The answer to this question specifies the method's 31 | arguments list. 32 | 33 | In our example, the answer probably is that, in order to format an email, it 34 | needs the email. 35 | 36 | So you can now add the argument list to the method: 37 | 38 | ```ruby 39 | def format_email(email) 40 | end 41 | ``` 42 | 43 | With these two things solved you can now start thinking about implementing 44 | the method body. How can you transform the email into some formatted text? 45 | 46 | You'd add a new line between `def` and `end`, and make sure it's indented by 47 | two spaces (hit `tab`, unless your editor does it for you), and start focussing 48 | on the code that makes up the method body: 49 | 50 | ```ruby 51 | def format_email(email) 52 | ... 53 | end 54 | ``` 55 | 56 | You see how we've split up the task of writing a new method into three smaller 57 | tasks, and worked on each one of them after another. 58 | 59 | Even though this might seem trivial at first, we recommend you get into this 60 | habit, too. 61 | 62 | Btw, good editors help you format things. For example, when you are on a 63 | line that starts with `def something`, and at the end of the line hit return, 64 | Sublime will already indent the next line for you by 2 spaces. If you now type 65 | `end`, then Sublime will notice that you are closing the method, and outdent 66 | it again, so `def` and `end` sit on the same level. Smart, isn't it? 67 | 68 | Also, you'll notice that when you type an opening parenthesis `(`, then Sublime 69 | will add a closing one `)` too, but keep your cursor placed between them, so 70 | you can type the argument list where it belongs. 71 | 72 | 73 | Exercises: Now would be a good time to do some of the [exercises on 74 | methods](/exercises/methods_1.html). 75 | -------------------------------------------------------------------------------- /source/70-bonus/13-arguments_parameters.md: -------------------------------------------------------------------------------- 1 | # Terminology: Arguments vs Parameters 2 | 3 | We should mention that we are slightly simplifying terminology here. We 4 | conflate two terms that normally would be defined separately: We simply use the 5 | term "argument" for both the variable names that are defined in the arguments 6 | list of the method definition, and the value that is passed as part of the 7 | method call. 8 | 9 | In programming, normally the "argument list" is called a "parameter list" 10 | instead, and a single name on it is called a "parameter". On the other hand, 11 | only the objects passed when calling the method are referred to as "arguments". 12 | 13 | E.g. in the code ... 14 | 15 | ```ruby 16 | def add_two(number) 17 | number + 2 18 | end 19 | 20 | puts add_two(3) 21 | ``` 22 | 23 | ... the word `number` in the first line is a "parameter", whereas `3` in the 24 | last line is an "argument". 25 | 26 | We found making this distinction in our beginners classes unnecessarily 27 | confusing, and thus ignore it. We simply call both these things "arguments", 28 | and point out that they create a local variable inside of the method body. 29 | 30 | So, now you know :) 31 | -------------------------------------------------------------------------------- /source/80-advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced Topics 2 | -------------------------------------------------------------------------------- /source/80-advanced/03-private_methods.md: -------------------------------------------------------------------------------- 1 | # Private methods 2 | 3 | Remember how we said that instance variables store data that is "private" to 4 | the object? Instance variables are only made accessible to the outside world 5 | (we say "it is exposed") if we add attribute accessors to the class. 6 | 7 | In the same way classes sometimes want to keep certain methods private: methods 8 | that aren't supposed to be called from outside of the object. Only the object 9 | itself is supposed to use them internally, from other methods. 10 | 11 | Imagine I am an instance of a class `ItalianRestaurant`, and I have a method 12 | `pizza`, which is supposed to return an instance of the class `Pizza`. 13 | 14 | When you approach me, and call the method `pizza` (i.e. ask me to bring a 15 | pizza) then I'll know what to do, and how to do it. I'll get some prepared 16 | pizza dough from somewhere, some tomato sauce, vegetables and other stuff from 17 | somewhere else, prepare the pizza, bake it, put it on a nice plate, and finally 18 | give (return) it to you. 19 | 20 | However, you don't really care about any of these details. You are hungry, and 21 | just want the pizza. All the exact steps involved are something that I keep 22 | private to me, and maybe they've been our family's best kept secret for 23 | generations. 24 | 25 | This is pretty much how objects work, too. The Italian restaurant object 26 | exposes some stuff to the outer world (you), and keeps other things private. 27 | They'll let you order a pizza, and other things. But they won't tell you the 28 | exact ingredients of their tomato sauce, or how they manage to make this damn 29 | great pizza dough. 30 | 31 | In our `Person` example it makes sense to make the method `encrypt` private. 32 | 33 | Currently, if you run the following code it will execute just fine, even though 34 | it makes little sense: 35 | 36 | ```ruby 37 | person = Person.new("Ada") 38 | p person.encrypt("some other secret") 39 | ``` 40 | 41 | Why would a person encrypt some arbitrary string for someone else, and return 42 | it? This is something that the person object should keep private. The 43 | restaurant wouldn't turn flour, water, olive oil and other ingredients into 44 | pizza dough for everyone else either. 45 | 46 | We can make the method `encrypt` private like so: 47 | 48 | ```ruby 49 | module Encryption 50 | private 51 | 52 | def encrypt(string) 53 | Digest::SHA2.hexdigest(string) 54 | end 55 | end 56 | ``` 57 | 58 | The keyword `private` tells Ruby that all methods defined from now on, are 59 | supposed to be private. They can be called from within the object (from other 60 | methods that the class defines), but not from outside. 61 | 62 | If you now try to call the method it will raise an error: 63 | 64 | ```ruby 65 | person = Person.new("Ada") 66 | p person.encrypt("super secret") 67 | ``` 68 | 69 | This will print the error message: 70 | 71 | ``` 72 | private method `encrypt' called for # 73 | ``` 74 | 75 | Nice. Does this make sense? 76 | -------------------------------------------------------------------------------- /source/80-advanced/04-procs.md: -------------------------------------------------------------------------------- 1 | # Procs 2 | 3 | TBD 4 | -------------------------------------------------------------------------------- /source/80-advanced/05-yield.md: -------------------------------------------------------------------------------- 1 | # Yield 2 | 3 | TBD 4 | -------------------------------------------------------------------------------- /source/90-exercises.md: -------------------------------------------------------------------------------- 1 | # Exercises (old) 2 | 3 | These exercises are an outdated set of exercises that need to be ported and 4 | adjusted in order to reflect the current order of topics in our book. 5 | 6 | Many of them at the moment assume things early on that only are explained 7 | later in the book. 8 | -------------------------------------------------------------------------------- /source/90-exercises/01-numbers.md: -------------------------------------------------------------------------------- 1 | # Working with Numbers 2 | 3 | In order to start `irb` open your terminal and type `irb`, then hit the 4 | `return` key (`enter`). In order to quit `irb` again (and get back to your 5 | system shell prompt) you can type `exit` or press `ctrl-d`, which does the 6 | same. 7 | 8 | ## Exercise 1.1 9 | 10 | In `irb`, calculate: 11 | 12 | * How many hours are in a year. 13 | * How many minutes are in a decade? 14 | * How many seconds old are you? 15 | 16 | ## Exercise 1.2 17 | 18 | What do you think happens when you combine the following floats and integers? 19 | 20 | Try computing these in `irb`: 21 | 22 | * `3.0 / 2` 23 | * `3 / 2.0` 24 | * `4 ** 2.0` 25 | * `4.1 % 2` 26 | 27 | Is the result a float or an integer? 28 | 29 | ## Exercise 1.3 30 | 31 | Methods are a way of "doing something with an object". E.g. in Ruby, numbers 32 | have two methods that allow you to check whether the number is odd or even. 33 | 34 | Look through the documentation for integer 35 | numbers (called `Fixnum`) and find the methods that tell if a number is odd or 36 | even. 37 | 38 | ## Exercise 1.4 39 | 40 | In `irb`, use these methods to find out if certain numbers are odd or even. 41 | Numbers like `0, 1, 2, 99, -502` etc. 42 | 43 |

44 | You can use a method by appending a dot . and then the method name 45 | to the object. E.g. -99.abs uses (we also say: "calls") the method 46 | abs on the number -99. 47 |

48 | 49 | Try for yourself what it does, and google for "ruby abs" to find 50 | [the documentation](http://ruby-doc.org/core-2.0/Numeric.html#method-i-abs) 51 | for this method. 52 | -------------------------------------------------------------------------------- /source/90-exercises/02-strings.md: -------------------------------------------------------------------------------- 1 | # Working with Strings 2 | 3 | In order to start `irb` open your terminal and type `irb`, then hit the 4 | `return` key (`enter`). In order to quit `irb` again (and get back to your 5 | system shell prompt) you can type `exit` or press `ctrl-d`, which does the 6 | same. 7 | 8 | ## Exercise 2.1 9 | 10 | What do you think this will do? 11 | 12 | ```ruby 13 | $ irb 14 | > "hello".length + "world".length 15 | ``` 16 | 17 | Try it yourself. 18 | 19 | ## Exercise 2.3 20 | 21 | Skim through the [documentation for strings](http://www.ruby-doc.org/core-2.1.4/String.html) 22 | in the Ruby documentation, and look for a method that prepends one string 23 | to another string. 24 | 25 | Using that method prepend the string `"Learning "` to the string `"Ruby"` 26 | 27 | Show solution 28 | 29 | ## Exercise 2.4 30 | 31 | Skim through the [documentation for strings](http://www.ruby-doc.org/core-2.1.4/String.html) 32 | in the Ruby documentation, and look for a method that removes characters 33 | from a string. 34 | 35 | Using that method turn the string `"Learning Ruby"` into the string `"Lrnng Rb"`. 36 | 37 | Show solution 38 | 39 | ## Exercise 2.5 40 | 41 | Find out how to convert the string `"1.23"` into the number `1.23`. 42 | 43 | You can either, again, skim the documentation page for strings, or google for 44 | "ruby convert string to number". 45 | 46 | Then also find out what method can be used to turn the string `"1"` into the 47 | number `1` (remember floats and integers are different kinds of numbers). 48 | 49 | Confirm that you have found the right methods by trying them in `irb`. 50 | 51 | Show solution 52 | 53 | ## Exercise 2.6 54 | 55 | There is a method that allows to justify a string, and padding it with another 56 | string. 57 | 58 | Find that method and use it on the string `"Ruby"` together with `"<3"` so that 59 | you get the following string back: 60 | 61 | ```ruby 62 | "Ruby<3<3<3" 63 | ``` 64 | 65 | We'll admit that this is a rather creative usage of this method. Normally you'd 66 | use it to align strings to columns (e.g. so that they line up nicely when you 67 | format a table). You'll use this method in other exercises later on. 68 | 69 | Show solution 70 | 71 | -------------------------------------------------------------------------------- /source/90-exercises/03-arrays_1.md: -------------------------------------------------------------------------------- 1 | # Working with Arrays (1) 2 | 3 | Before you get started, make sure you have your text editor and terminal open, 4 | and you have navigated to your exercises directory in the terminal. E.g. `cd 5 | ~/ruby-for-beginners/exercises`. 6 | 7 | ## Exercise 3.1 8 | 9 | Create a new, empty file. Save it as `arrays_1-1.rb`. Fill in the following 10 | line: 11 | 12 | ```ruby 13 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 14 | # your code goes here 15 | ``` 16 | 17 | So that, when you run your code (run `ruby arrays_1-1.rb`), you get the 18 | following output: 19 | 20 | ```ruby 21 | 5 22 | ``` 23 | 24 | Show solution 25 | 26 | ## Exercise 3.2 27 | 28 | Copy your file to a new file: `cp arrays_1-1.rb arrays_1-2.rb`, then open this 29 | new file. 30 | 31 | Add another line *before the line that you just added*, so that, when you run 32 | your code, you get the following output: 33 | 34 | ```ruby 35 | 99 36 | ``` 37 | 38 | Show solution 39 | 40 | ## Exercise 3.3 41 | 42 | Make a new file `arrays_1-3.rb`, and fill in the following line: 43 | 44 | ```ruby 45 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 46 | # your code goes here 47 | p numbers 48 | ``` 49 | 50 | So that you get the following output: 51 | 52 | ```ruby 53 | [2, 4, 6, 8, 10] 54 | ``` 55 | 56 |

57 | Read the documentation for the method select that you can use on arrays 58 | on the Ruby documentation 59 |

60 | 61 | Show solution 62 | 63 | ## Exercise 3.4 64 | 65 | Again, copy your last file to a new file: `cp arrays_1-3.rb arrays_1-4.rb`, 66 | then open this new file. 67 | 68 | Now change the code that you just added so that you get the following output: 69 | 70 | ```ruby 71 | [10, 8, 6, 4, 2] 72 | ``` 73 | 74 |

75 | The method select that you used in the last exercise returns an array. 76 | On this array (the return value) you can use another method, by, again, just 77 | appending a dot . and the method name to it, i.e., to the end of the line. 78 |

79 | 80 |

81 | There is another method that reverses the order of the array. You can 82 | find it by googling for "ruby array reverse". 83 |

84 | 85 | Show solution 86 | 87 | ## Exercise 3.5 88 | 89 | Again, copy your last file to a new file: `cp arrays_1-4.rb arrays_1-5.rb`, 90 | then open this new file. 91 | 92 | Now change your code so that you get the following output: 93 | 94 | ```ruby 95 | [10, 8, 4, 2] 96 | ``` 97 | 98 | Bonus: Find at least three different solutions for this last change. 99 | 100 | Show solution 101 | 102 | 103 | -------------------------------------------------------------------------------- /source/90-exercises/04-hashes_1.md: -------------------------------------------------------------------------------- 1 | # Working with Hashes (1) 2 | 3 | Before you get started, make sure you have your text editor and terminal open, 4 | and you have navigated to your exercises directory in the terminal. E.g. `cd 5 | ~/ruby-for-beginners/exercises`. 6 | 7 | 8 | ## Exercise 4.1 9 | 10 | Make a new file `hashes_1-1.rb`, and fill in the following line: 11 | 12 | ```ruby 13 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 14 | # your code goes here 15 | ``` 16 | 17 | So that it prints out `dos`. 18 | 19 | Show solution 20 | 21 | 22 | ## Exercise 4.2 23 | 24 | Make a new file `hashes_1-2.rb`, and fill in the following line: 25 | 26 | ```ruby 27 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 28 | # your code goes here 29 | puts dictionary[:four] 30 | ``` 31 | 32 | So that it prints out `cuatro`. 33 | 34 | Show solution 35 | 36 | 37 | ## Exercise 4.3 38 | 39 | Copy that file to a new file `cp hashes_1-2.rb hashes_1-3.rb`, and change your 40 | code so that it prints out the following. 41 | 42 | ``` 43 | Cuatro 44 | ``` 45 | 46 |

47 | There's a method that upcases the first letter of a string. Find it by 48 | googling for "ruby string upcase first letter". 49 |

50 | 51 | Show solution 52 | 53 | 54 | ## Exercise 4.4 55 | 56 | There is a method on hashes that allows to check if a certain key is defined on 57 | the hash. Find that method by googling for "ruby hash key defined". 58 | 59 | Try this method in `irb` by creating a hash like the one above, calling the 60 | method and passing keys like `:one`, `:two`, `:four`, and `:ten`. 61 | 62 | Show solution 63 | 64 | 65 | ## Exercise 4.5 66 | 67 | There is a method on hashes that flips keys and values. Find that method on the 68 | [Ruby documentation about hashes](http://www.ruby-doc.org/core-2.2.0/Hash.html) 69 | 70 | Make a new file `hashes_1-5.rb`, and fill in the following line using that 71 | method: 72 | 73 | ```ruby 74 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 75 | # your code goes here 76 | ``` 77 | 78 | This should then output: 79 | 80 | ``` 81 | { 'uno' => :one, 'dos' => :two, 'tres' => :three } 82 | ``` 83 | 84 | Show solution 85 | -------------------------------------------------------------------------------- /source/90-exercises/05-methods_1.md: -------------------------------------------------------------------------------- 1 | # Defining methods 2 | 3 | ## Exercise 5.1 4 | 5 | Write a method `greet` that takes a name, prepends `"Hello "`, and appends an 6 | exclamation mark `"!"`: 7 | 8 | ```ruby 9 | def greet(name) 10 | # your code goes here 11 | end 12 | 13 | puts greet("Ada") 14 | ``` 15 | 16 | This should print out `Hello Ada!`. 17 | 18 | Show solution 19 | 20 | 21 | ## Exercise 5.2 22 | 23 | Once you've implemented the method this should print out: `Hello Ada!`. 24 | 25 | Now change your method so that instead of always using `"Hello "` it picks a 26 | random string from the array `["Hello", "Hi", "Ohai", "ZOMG"]`. 27 | 28 | Every time you run the program it should print out either `"Hello Ada!"`, `"Hi 29 | Ada!"`, `"Ohai Ada!"`, or `"ZOMG Ada!"`. 30 | 31 |

32 | The method shuffle on arrays does, well, shuffle the array :) That 33 | means it changes the order of the elements in the array in a random way. 34 |

35 | 36 | Show solution 37 | 38 | 39 | ## Exercise 5.3 40 | 41 | Write a method that converts a distance (a number) from miles to kilometers: 42 | 43 | ```ruby 44 | def miles_to_kilometers(miles) 45 | # your code goes here 46 | end 47 | 48 | puts miles_to_kilometers(25) 49 | ``` 50 | 51 | This should print out: 52 | 53 | ``` 54 | 40.2336 55 | ``` 56 | 57 | Show solution 58 | 59 | 60 | ## Exercise 5.4 61 | 62 | Write a method `leap_year?` that takes a year (a number), and calculates if it is a leap year. 63 | 64 | ```ruby 65 | def leap_year?(year) 66 | # your code goes here 67 | end 68 | 69 | p leap_year?(2012) 70 | p leap_year?(2015) 71 | ``` 72 | 73 | This should print out: 74 | 75 | ``` 76 | true 77 | false 78 | ``` 79 | 80 | Hint: The operator `%` returns the rest of a division. E.g. `14 % 3` returns `2`. 81 | 82 | Bonus: Also make it so that the method returns `true` for the year `2000` and 83 | `false` for `1900` ... because that's really the definition of leap years. 84 | 85 | Show solution 86 | 87 | -------------------------------------------------------------------------------- /source/90-exercises/07-nested_arrays.md: -------------------------------------------------------------------------------- 1 | # Working with Nested Arrays 2 | 3 | Note: At the moment these exercises require reading up until "Block return values". 4 | You should know how to use the method `collect` on arrays. 5 | 6 | Before you get started, make sure you have your text editor and terminal open, and 7 | you have navigated to your exercises directory in the terminal. E.g. `cd 8 | ~/ruby-for-beginners/exercises`. 9 | 10 | ## Exercise 7.1 11 | 12 | Make a new file `nested_arrays-1.rb`, and fill in the following line: 13 | 14 | ```ruby 15 | numbers = [ 16 | [1, 2, 3], 17 | [4, 5, 6], 18 | [7, 8, 9] 19 | ] 20 | # your code goes here 21 | p sums 22 | ``` 23 | 24 | So that you get the following output: 25 | 26 | ```ruby 27 | [6, 15, 24] 28 | ``` 29 | 30 | Show solution 31 | 32 | 33 | ## Exercise 7.2 34 | 35 | Make a new file `nested_arrays-2.rb`, and fill in the following line: 36 | 37 | Fill in the following line 38 | 39 | ```ruby 40 | numbers = [ 41 | [1, 2, 3], 42 | [2, 2, 2], 43 | [3, 2, 1] 44 | ] 45 | # your code goes here 46 | lines.each { |line| puts line } 47 | ``` 48 | 49 | So that you get the following output: 50 | 51 | ``` 52 | * ** *** 53 | ** ** ** 54 | *** ** * 55 | ``` 56 | 57 | Show solution 58 | 59 | -------------------------------------------------------------------------------- /source/90-exercises/08-hashes_2.md: -------------------------------------------------------------------------------- 1 | # Working with Hashes (2) 2 | 3 | ## Exercise 8.1 4 | 5 | Make a new file `hashes_2-1.rb`, and dd the following lines: 6 | 7 | ```ruby 8 | languages = { 9 | :de => 'German', 10 | :en => 'English', 11 | :es => 'Spanish', 12 | } 13 | dictionary = { 14 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 15 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 16 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 17 | } 18 | ``` 19 | 20 | Now, at the end of the file, add code that prints out the following: 21 | 22 | ``` 23 | In German, eins means one, zwei means two, drei means three. 24 | In Spanish, uno means one, duo means two, tres means three. 25 | ``` 26 | 27 | Show solution 28 | 29 | 30 | ## Exercise 8.2 31 | 32 | Now, in a new file `hashes_2-2.rb`, with the same hashes from above, add code 33 | that prints out the following table: 34 | 35 | ``` 36 | de eins zwei drei 37 | en one two three 38 | es uno dos tres 39 | ``` 40 | 41 | Show solution 42 | 43 | 44 | ## Exercise 8.3 45 | 46 | Copy your file to a new file `cp hashes_2-2.rb hashes_2-3.rb` and change your 47 | code so that it aligns the table columns: 48 | 49 | ``` 50 | de eins zwei drei 51 | en one two three 52 | es uno dos tres 53 | ``` 54 | 55 | Show solution 56 | 57 | 58 | ## Exercise 8.4 59 | 60 | Copy your file to a new file `cp hashes_2-3.rb hashes_2-4.rb` and change your 61 | code so that it adds delimiters: 62 | 63 | ``` 64 | | de | eins | zwei | drei | 65 | | en | one | two | three | 66 | | es | uno | dos | tres | 67 | ``` 68 | 69 | Show solution 70 | 71 | -------------------------------------------------------------------------------- /source/90-exercises/10-email.md: -------------------------------------------------------------------------------- 1 | # The Email Class 2 | 3 | ## Exercise 10.1 4 | 5 | In a new file `email_1.rb` write a class `Email` that has a `subject`, 6 | `date`, and `from` attribute. Make it so that these attributes can be populated 7 | through `new` and `initialize`. 8 | 9 | The following code 10 | 11 | ```ruby 12 | class Email 13 | # fill in this class body 14 | end 15 | 16 | email = Email.new("Homework this week", "2014-12-01", "Ferdous") 17 | 18 | puts "Date: #{email.date}" 19 | puts "From: #{email.from}" 20 | puts "Subject: #{email.subject}" 21 | ``` 22 | 23 | should then output the following: 24 | 25 | ``` 26 | Date: 2014-12-01 27 | From: Ferdous 28 | Subject: Homework this week 29 | ``` 30 | 31 | Show solution 32 | 33 | ## Exercise 10.2 34 | 35 | Once you have this class, copy your file to `email_2.rb`. 36 | 37 | Change your class so that the `initialize` method now takes a `subject` string, 38 | and a `headers` hash. This is then more in line with how actual emails are 39 | stored in the real world: the values `date` and `from` are stored on a hash, 40 | which is called the "email headers". 41 | 42 | Doing so, in the code above the only line you should change is the one that 43 | instantiates the email object, which should now read: 44 | 45 | ```ruby 46 | email = Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Ferdous" }) 47 | ``` 48 | 49 | Your program should now still produce the same output. 50 | 51 | Show solution 52 | -------------------------------------------------------------------------------- /source/90-exercises/11-mailbox.md: -------------------------------------------------------------------------------- 1 | # The Mailbox Class 2 | 3 | ## Exercise 11.1 4 | 5 | In a new file `mailbox-1.rb` Write a class that has a `name` and `emails` 6 | attribute. Make it so that the these attributes can be populated through the 7 | `initialize` method (`name` being a string, and `emails` being an array of 8 | `Email` instances). 9 | 10 | The following code 11 | 12 | ```ruby 13 | class Email 14 | # your class from the last exercise 15 | end 16 | 17 | class Mailbox 18 | # fill in this class body 19 | end 20 | 21 | emails = [ 22 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 23 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 24 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 25 | ] 26 | mailbox = Mailbox.new("Ruby Study Group", emails) 27 | 28 | mailbox.emails.each do |email| 29 | puts "Date: #{email.date}" 30 | puts "From: #{email.from}" 31 | puts "Subject: #{email.subject}" 32 | puts 33 | end 34 | ``` 35 | 36 | should then output the following: 37 | 38 | ``` 39 | Date: 2014-12-01 40 | From: Ferdous 41 | Subject: Homework this week 42 | 43 | Date: 2014-12-01 44 | From: Dajana 45 | Subject: Keep on coding! :) 46 | 47 | Date: 2014-12-02 48 | From: Arianne 49 | Subject: Re: Homework this week 50 | ``` 51 | 52 | Show solution 53 | -------------------------------------------------------------------------------- /source/90-exercises/12-mailbox_text.md: -------------------------------------------------------------------------------- 1 | # The Mailbox Text Formatter 2 | 3 | The mailbox text formatter exercise is a milestone in the beginners group 4 | curriculum, and it may take a little bit longer to complete it. That is fine, 5 | and, to some extent, the point :) 6 | 7 | ## Exercise 12.1 8 | 9 | Make a new file `mailbox_text-1.rb`. Fill in and complete the following 10 | class definitions: 11 | 12 | ```ruby 13 | class Email 14 | # your class from the last exercise 15 | end 16 | 17 | class Mailbox 18 | # your class from the last exercise 19 | end 20 | 21 | class MailboxTextFormatter 22 | # fill in this class body 23 | end 24 | 25 | emails = [ 26 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 27 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 28 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 29 | ] 30 | mailbox = Mailbox.new("Ruby Study Group", emails) 31 | formatter = MailboxTextFormatter.new(mailbox) 32 | 33 | puts formatter.format 34 | ``` 35 | 36 | Your goal is to complete the code in a way so it outputs the following: 37 | 38 | ``` 39 | Mailbox: Ruby Study Group 40 | 41 | +------------+---------+------------------------+ 42 | | Date | From | Subject | 43 | +------------+---------+------------------------+ 44 | | 2014-12-01 | Ferdous | Homework this week | 45 | | 2014-12-01 | Dajana | Keep on coding! :) | 46 | | 2014-12-02 | Ariane | Re: Homework this week | 47 | +------------+---------+------------------------+ 48 | ``` 49 | 50 | You are allowed to add as many methods to the classes `Email`, `Mailbox` and 51 | `MailboxTextFormatter` as you deem useful. In your final solution you are not 52 | allowed to change any of the code outside (after) the class definitions. For 53 | debugging purposes, you can, of course, change all the code you want :) 54 | 55 | If you do this exercise in one of our study groups then best do it together 56 | with someone else. We found pairs to work best, and a three person team would 57 | be better than doing it on your own. 58 | 59 | Try to come up with a working solution first. It doesn't matter how elegant, 60 | generic, or pretty it is. Whatever produces the required output is fine for a 61 | first solution. 62 | 63 | Then, later, look at your code, and try to improve it by cleaning up everything 64 | that you don't like, or deam ugly. 65 | 66 | Eventually, one goal to aim for would be: Adding another column to the table 67 | only requires mimimal changes, e.g. changes to one or two places in your 68 | formatter class. 69 | 70 | Show solution 71 | -------------------------------------------------------------------------------- /source/90-exercises/14-mailbox_file.md: -------------------------------------------------------------------------------- 1 | # Storing our HTML to a File 2 | 3 | ## Exercise 14.1 4 | 5 | In this exercise our goal is to store the generated HTML for our mailbox to a 6 | file, so that we can finally view it in an actual browser. 7 | 8 | Your objective is to write a file (using Ruby) that contains all the HTML 9 | from the last exercise. 10 | 11 | Building on the last exercise, copy your file to a new file `cp mailbox_html-1.rb 12 | mailbox_file-1.rb` and change the last line: 13 | 14 | Instead of passing the HTML to `puts` you should be passing it to a different 15 | method, so that running your program writes the HTML file that we are after. 16 | In order to find that method try googling for "ruby write file". Call this 17 | file `emails.html`. 18 | 19 | When you open this file in a text editor you should see the same HTML from 20 | the last exercise. When you open it in your browser it should look like this: 21 | 22 | ![image](https://cloud.githubusercontent.com/assets/2208/5602614/5ac2058c-935a-11e4-9c01-7ff9ec3b66cb.png) 23 | 24 | Show solution 25 | -------------------------------------------------------------------------------- /source/91-exercises_2.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | If you haven't done the [try ruby](http://tryruby.org) course already, we 4 | recommend you do at least a couple of lessons over there too. 5 | 6 | Whenever you sit down to do some of the exercises in this book make sure you 7 | have your text editor and terminal open. In your terminal: 8 | 9 | * Create a new directory where you want to keep Ruby files for our exercises. For 10 | example: `~/ruby-for-beginners/exercises`. In 11 | order to create that directory in the terminal you can run: `mkdir 12 | ~/ruby-for-beginners/exercises`. 13 | 14 | * Then navigate to this directory: `cd ~/ruby-for-beginners/exercises` 15 | 16 | * In order to check if you are in the right directory you can always run: 17 | `pwd`. (That's short for *print working directory*, meaning, it prints the name 18 | of the current directory.) 19 | 20 | * In order to see all the files and subdirectories contained in the current 21 | directory you can run `ls`. 22 | 23 |

24 | Credits: The exercises 1.1, 1.2, and 2.1 are taken from 25 | Introduction to Ruby. 26 |

27 | -------------------------------------------------------------------------------- /source/91-exercises_2/01-numbers.md: -------------------------------------------------------------------------------- 1 | # Working with Numbers 2 | 3 | In order to do these exercises you should have read at least the chapter about 4 | [built_in_classes/numbers.html]. 5 | 6 | ## Exercise 1.1 7 | 8 | In `irb`, calcluate: 9 | 10 | * How many hours are in a year. 11 | * How many minutes are in a decade? 12 | * How many seconds old are you? 13 | 14 |

15 | To start irb open your terminal and type irb, then 16 | hit the return key (enter). To quit it (and get back 17 | to your system shell prompt) type exit or press ctrl-d. 18 |

19 | 20 | ## Exercise 1.2 21 | 22 | What do you think happens when you combine floats and integers the following 23 | in the following calculations? 24 | 25 | Try computing these in `irb`: 26 | 27 | * `3 / 2` 28 | * `3.0 / 2` 29 | * `3 / 2.0` 30 | * `4 * 2.0` 31 | * `0 + 1` 32 | 33 | Is the result a float or an integer? 34 | 35 | ## Exercise 1.3 36 | 37 | Try finding out what "modulo" means by asking Google. 38 | 39 | In Ruby (and many other languages) the operator for modulo is `%`. 40 | 41 | Try the following in `irb`: 42 | 43 | * `5 % 2` 44 | * `15 % 2` 45 | * `505 % 2` 46 | 47 | And: 48 | 49 | * `8 % 5` 50 | * `9 % 5` 51 | * `10 % 5` 52 | * `11 % 5` 53 | 54 | ## Exercise 1.4 55 | 56 | Methods are a way of "doing something with an object", and you'll learn a lot 57 | more about them in a few chapters. 58 | 59 | In Ruby, numbers have methods that allow you to check whether the number is odd 60 | or even. 61 | 62 | Look through the documentation for integer 63 | numbers and find the methods that tell if a number is odd or even. 64 | 65 | Look at the examples for some of the other methods on that page. 66 | 67 |

68 | You can use a method by appending a dot . and then the method name 69 | to the object. E.g. -99.abs uses (we also say: "calls") the method 70 | abs on the number -99. 71 |

72 | 73 | ## Exercise 1.5 74 | 75 | In `irb`, use these methods to find out if certain numbers are odd or even. 76 | 77 | Try a bunch of numbers like `0, 1, 2, 99, -502` etc. 78 | 79 | Try for yourself what it does, and google for "ruby number odd even" to find 80 | [the documentation](http://ruby-doc.org/core-2.2.2/Integer.html#method-i-odd-3F) 81 | for these methods. 82 | 83 | Show solution 84 | -------------------------------------------------------------------------------- /source/91-exercises_2/02-strings.md: -------------------------------------------------------------------------------- 1 | # Working with Strings 2 | 3 | In order to do these exercises you should have read at least the chapter about 4 | [built_in_classes/strings.html]. 5 | 6 | ## Exercise 2.1 7 | 8 | In `irb` try figuring out how to produce the following String: `"Ruby<3<3<3"`. 9 | 10 | Try using no more than two other String objects. 11 | 12 | Show solution 13 | 14 |

15 | To start irb open your terminal and type irb, then 16 | hit the return key (enter). To quit it (and get back 17 | to your system shell prompt) type exit or press ctrl-d. 18 |

19 | 20 | ## Exercise 2.2 21 | 22 | Look through the documentation for Strings. 23 | 24 | Do you spot any interesting methods? 25 | 26 | ## Exercise 2.3 27 | 28 | What do you think this will do? 29 | 30 | ```ruby 31 | $ irb 32 | > "hello".length + "world".length 33 | ``` 34 | 35 | Try it yourself in `irb`. 36 | 37 | Show solution 38 | 39 | ## Exercise 2.4 40 | 41 | Look through the documentation for Strings, 42 | and find out how to convert the string `"1.23"` into the number `1.23`. 43 | 44 | If you can't find it, google for "ruby convert string to float". 45 | 46 | Then also find out what method can be used to turn the string `"1"` into the 47 | number `1` (remember floats and integers are different kinds of numbers). 48 | 49 | Confirm that you have found the right methods by trying them in `irb`. 50 | 51 | Show solution 52 | 53 | -------------------------------------------------------------------------------- /source/91-exercises_2/04-hashes_1.md: -------------------------------------------------------------------------------- 1 | # Working with Hashes (1) 2 | 3 | In order to do these exercises you should have read at least the chapter about 4 | [built_in_classes/hashes.html]. 5 | 6 |

7 | Make sure you have your text editor and terminal open, and you have navigated 8 | to your exercises directory in the terminal. E.g. cd ~/ruby-for-beginners/exercises. 9 |

10 | 11 | ## Exercise 4.1 12 | 13 | Make a new file `hashes_1-1.rb`, and fill in the following line: 14 | 15 | ```ruby 16 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 17 | # your code goes here 18 | ``` 19 | 20 | ... so that it prints out `dos`. 21 | 22 | Show solution 23 | 24 | 25 | ## Exercise 4.2 26 | 27 | Make a new file `hashes_1-2.rb`, and fill in the following line: 28 | 29 | ```ruby 30 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 31 | # your code goes here 32 | puts dictionary[:four] 33 | ``` 34 | 35 | ... so that it prints out `cuatro`. 36 | 37 | Show solution 38 | 39 | 40 | ## Exercise 4.3 41 | 42 | Copy that file to a new file `cp hashes_1-2.rb hashes_1-3.rb`, and change your 43 | code so that it prints out the following. 44 | 45 | ``` 46 | Cuatro 47 | ``` 48 | 49 |

50 | There's a method that upcases the first letter of a string. Find it by 51 | googling for "ruby string upcase first letter". 52 |

53 | 54 | Show solution 55 | 56 | 57 | ## Exercise 4.4 58 | 59 | There is a method on hashes that allows to check if a certain key is defined on 60 | the hash. Find that method by googling for "ruby hash key defined". 61 | 62 | Try this method in `irb` by creating a hash like the one above, calling the 63 | method and passing keys like `:one`, `:two`, `:four`, and `:ten`. 64 | 65 | Show solution 66 | 67 | 68 | ## Exercise 4.5 69 | 70 | There is a method on hashes that flips keys and values. Find that method on the 71 | [Ruby documentation about Hashes](http://www.ruby-doc.org/core-2.2.0/Hash.html) 72 | 73 | Make a new file `hashes_1-5.rb`, and fill in the following line using that 74 | method: 75 | 76 | ```ruby 77 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 78 | # your code goes here 79 | ``` 80 | 81 | This should then output: 82 | 83 | ``` 84 | { 'uno' => :one, 'dos' => :two, 'tres' => :three } 85 | ``` 86 | 87 | Show solution 88 | -------------------------------------------------------------------------------- /source/91-exercises_2/99-notes.md: -------------------------------------------------------------------------------- 1 | # Working with Strings 2 | 3 | ## Exercise 2.3 4 | 5 | Skim through the [documentation for strings](http://www.ruby-doc.org/core-2.1.4/String.html) 6 | in the Ruby documentation, and look for a method that prepends one string 7 | to another string. 8 | 9 | Using that method prepend the string `"Learning "` to the string `"Ruby"` 10 | 11 | Show solution 12 | 13 | ## Exercise 2.4 14 | 15 | Skim through the [documentation for strings](http://www.ruby-doc.org/core-2.1.4/String.html) 16 | in the Ruby documentation, and look for a method that removes characters 17 | from a string. 18 | 19 | Using that method turn the string `"Learning Ruby"` into the string `"Lrnng Rb"`. 20 | 21 | Show solution 22 | 23 | ## Exercise 2.6 24 | 25 | There is a method that allows to justify a string, and padding it with another 26 | string. 27 | 28 | Find that method and use it on the string `"Ruby"` together with `"<3"` so that 29 | you get the following string back: 30 | 31 | ```ruby 32 | "Ruby<3<3<3" 33 | ``` 34 | 35 | We'll admit that this is a rather creative usage of this method. Normally you'd 36 | use it to align strings to columns (e.g. so that they line up nicely when you 37 | format a table). You'll use this method in other exercises later on. 38 | 39 | Show solution 40 | 41 | 42 | # Working with Arrays (1) 43 | 44 | ## Exercise 3.3 45 | 46 | Make a new file `arrays_1-3.rb`, and fill in the following line: 47 | 48 | ```ruby 49 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 50 | # your code goes here 51 | p numbers 52 | ``` 53 | 54 | So that you get the following output: 55 | 56 | ```ruby 57 | [2, 4, 6, 8, 10] 58 | ``` 59 | 60 |

61 | Read the documentation for the method select that you can use on arrays 62 | on the Ruby documentation 63 |

64 | 65 | Show solution 66 | 67 | ## Exercise 3.5 68 | 69 | Again, copy your last file to a new file: `cp arrays_1-4.rb arrays_1-5.rb`, 70 | then open this new file. 71 | 72 | Now change your code so that you get the following output: 73 | 74 | ```ruby 75 | [10, 8, 4, 2] 76 | ``` 77 | 78 | Bonus: Find at least three different solutions for this last change. 79 | 80 | Show solution 81 | 82 | 83 | -------------------------------------------------------------------------------- /source/CNAME: -------------------------------------------------------------------------------- 1 | ruby-for-beginners.rubymonstas.org 2 | -------------------------------------------------------------------------------- /source/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /source/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /source/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /source/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /source/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubymonsters/ruby-for-beginners/a4ded425e25a4f809267d41a644b3f944177548e/source/assets/images/favicon.png -------------------------------------------------------------------------------- /source/assets/stylesheets/_fonts.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'FontAwesome'; 3 | src: url('../fonts/fontawesome-webfont.eot'); 4 | src: url('../fonts/fontawesome-webfont.eot') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff') format('woff'), url('../fonts/fontawesome-webfont.ttf') format('truetype'), url('../fonts/fontawesome-webfont.svg') format('svg'); 5 | font-weight: normal; 6 | font-style: normal; 7 | } 8 | -------------------------------------------------------------------------------- /source/assets/stylesheets/_html5-reset.css: -------------------------------------------------------------------------------- 1 | /* 2 | html5doctor.com Reset Stylesheet 3 | v1.6.1 4 | Last Updated: 2010-09-17 5 | Author: Richard Clark - http://richclarkdesign.com 6 | Twitter: @rich_clark 7 | */ 8 | 9 | html, body, div, span, object, iframe, 10 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 11 | abbr, address, cite, code, 12 | del, dfn, em, img, ins, kbd, q, samp, 13 | small, strong, sub, sup, var, 14 | b, i, 15 | dl, dt, dd, ol, ul, li, 16 | fieldset, form, label, legend, 17 | table, caption, tbody, tfoot, thead, tr, th, td, 18 | article, aside, canvas, details, figcaption, figure, 19 | footer, header, hgroup, menu, nav, section, summary, 20 | time, mark, audio, video { 21 | margin:0; 22 | padding:0; 23 | border:0; 24 | outline:0; 25 | // font-size:100%; 26 | vertical-align:baseline; 27 | background:transparent; 28 | } 29 | 30 | body { 31 | line-height:1; 32 | } 33 | 34 | article,aside,details,figcaption,figure, 35 | footer,header,hgroup,menu,nav,section { 36 | display:block; 37 | } 38 | 39 | nav ul { 40 | list-style:none; 41 | } 42 | 43 | blockquote, q { 44 | quotes:none; 45 | } 46 | 47 | blockquote:before, blockquote:after, 48 | q:before, q:after { 49 | content:''; 50 | content:none; 51 | } 52 | 53 | a { 54 | margin:0; 55 | padding:0; 56 | font-size:100%; 57 | vertical-align:baseline; 58 | background:transparent; 59 | } 60 | 61 | /* change colours to suit your needs */ 62 | ins { 63 | background-color:#ff9; 64 | color:#000; 65 | text-decoration:none; 66 | } 67 | 68 | /* change colours to suit your needs */ 69 | mark { 70 | background-color:#ff9; 71 | color:#000; 72 | font-style:italic; 73 | font-weight:bold; 74 | } 75 | 76 | del { 77 | text-decoration: line-through; 78 | } 79 | 80 | abbr[title], dfn[title] { 81 | border-bottom:1px dotted; 82 | cursor:help; 83 | } 84 | 85 | table { 86 | border-collapse:collapse; 87 | border-spacing:0; 88 | } 89 | 90 | /* change border colour to suit your needs */ 91 | hr { 92 | display:block; 93 | height:1px; 94 | border:0; 95 | border-top:1px solid #cccccc; 96 | margin:1em 0; 97 | padding:0; 98 | } 99 | 100 | input, select { 101 | vertical-align:middle; 102 | } 103 | -------------------------------------------------------------------------------- /source/assets/stylesheets/_print.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | nav, .menu { 3 | display: none !important; 4 | } 5 | @page { 6 | margin: 4em; 7 | } 8 | body { 9 | font-size: 1em; 10 | } 11 | body > div { 12 | display: block; 13 | margin: 0; 14 | max-width: 100%; 15 | padding: 0; 16 | } 17 | article { 18 | max-width: 100%; 19 | } 20 | h1 { 21 | /* page-break-before: always; */ 22 | } 23 | h1, h2, h3, h4 { 24 | page-break-after: avoid; 25 | } 26 | article { 27 | page-break-before: always; 28 | } 29 | pre { 30 | page-break-inside: avoid; 31 | } 32 | ul, ol { 33 | page-break-inside: avoid; 34 | } 35 | body > div { 36 | padding: 0; 37 | } 38 | body, a { 39 | color: #000; 40 | } 41 | a { 42 | word-wrap: break-word; 43 | } 44 | a[href^="http://"]:after, a[href^="https://"]:after { 45 | content: " (" attr(href) ")"; 46 | font-size: 90%; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/assets/stylesheets/_response.scss: -------------------------------------------------------------------------------- 1 | $tablet-1: 700px; 2 | $tablet-2: 900px; 3 | $tablet-3: 1024px; 4 | $desktop: 1200px; 5 | 6 | $font-s: 0.875rem; // 14px 7 | $font-m: 1rem; // 16px 8 | $font-l: 1.125rem; // 18px 9 | 10 | html { 11 | font-size: $font-s; 12 | @media screen and (min-width: $tablet-2) { font-size: $font-s; } 13 | @media screen and (min-width: $tablet-3) { font-size: $font-m; } 14 | @media screen and (min-width: $desktop) { font-size: $font-l; } 15 | } 16 | 17 | @media screen and (max-width: $tablet-2) { 18 | body > .page-nav { display: none; } 19 | article > .page-nav { display: block; } 20 | body > div { padding: 3rem 3rem 6.250rem 3rem; } 21 | .menu { display: block; } 22 | .toc { display: none; } 23 | } 24 | 25 | @media screen and (min-width: $tablet-2) { 26 | article > .page-nav { display: none; } 27 | .menu { display: none; } 28 | } 29 | -------------------------------------------------------------------------------- /source/assets/stylesheets/monstas.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | $light-gray: #F2F2F2; 4 | $red: #ee4444; 5 | 6 | @import "fonts"; 7 | @import "html5-reset"; 8 | @import "syntax"; 9 | @import "layout"; 10 | @import "style"; 11 | @import "response"; 12 | @import "print"; 13 | -------------------------------------------------------------------------------- /source/assets/stylesheets/syntax.css.erb: -------------------------------------------------------------------------------- 1 | <%# A good day theme, though not that colorful %> 2 | <%= Rouge::Themes::Github.render(:scope => '.highlight') %> 3 | 4 | <%# A good day theme, and is very colorful %> 5 | <%#= Rouge::Themes::Base16.render(:scope => '.highlight') %> 6 | 7 | <%# That good sublime text night theme%> 8 | <%#= Rouge::Themes::Base16::Monokai.render(:scope => '.highlight') %> 9 | -------------------------------------------------------------------------------- /source/index.md: -------------------------------------------------------------------------------- 1 | # Ruby For Beginners 2 | 3 | *[Ruby Monday Study Group](http://rubymonstas.org) curriculum for beginners* 4 | 5 | This book has been written after we have run 4 beginners groups at our [Ruby 6 | Monstas](http://rubymonstas.org) groups in Berlin, and it outlines the current 7 | state of our beginner groups curriculum. 8 | 9 | After completing this curriculum you'll be able to read, understand, and write 10 | basic Ruby code yourself: 11 | 12 | You can use this knowledge to create small tools that might help you in your 13 | day-to-day work (such as converting numbers, or extracting data from files) 14 | that normally would cost a lot of manual work. You'll be able to jump into 15 | other tutorials, and have a much easier time understanding what they're 16 | talking about. And you can start working on our next curriculum which will 17 | walk you through the basics of building an actual 18 | [web application](http://webapps-for-beginners.rubymonstas.org). 19 | 20 | If you'd like to print this book, or export it as a PDF try using [this page](/print.html), 21 | which is a single-page version of the entire book. 22 | 23 | You can find the [source code of this book here](https://github.com/rubymonsters/ruby-for-beginners). 24 | -------------------------------------------------------------------------------- /source/layouts/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <%= discover_page_title %> 14 | 15 | <%= stylesheet_link_tag "/assets/stylesheets/monstas" %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 28 |
29 | 30 | <%= yield %> 31 | 32 | 33 |
34 |
35 | 36 | 49 | 50 | <%= javascript_include_tag "/assets/javascripts/modernizr" %> 51 | <%= javascript_include_tag "/assets/javascripts/monstas" %> 52 | 53 | 54 | -------------------------------------------------------------------------------- /source/print.erb: -------------------------------------------------------------------------------- 1 | <% all_pages.each do |page| %> 2 | <%= page.render %> 3 | <% end %> 4 | 5 | -------------------------------------------------------------------------------- /source/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /source/sitemap.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% sitemap.resources.each do |page| %> 5 | <% if page.path.include? ".html" %> 6 | 7 | <%= data.book.domain + page.url %> 8 | 0.5 9 | 10 | <% end %> 11 | <% end %> 12 | 13 | -------------------------------------------------------------------------------- /source/solutions/02-strings-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "Ruby".prepend("Learning ") 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions/02-strings-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "Learning Ruby".delete('aeiuy') 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions/02-strings-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "1.23".to_f 3 | "1".to_i 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions/02-strings-6.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "Ruby".ljust(10, '<3') 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions/03-arrays_1-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | p numbers[4] 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions/03-arrays_1-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | numbers[4] = 99 4 | p numbers[4] 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/03-arrays_1-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | numbers = numbers.select { |number| number.even? } 4 | p numbers 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/03-arrays_1-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | numbers = numbers.select { |number| number.even? }.reverse 4 | p numbers 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/03-arrays_1-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | numbers = numbers.select { |number| number.even? }.reverse 4 | numbers.delete(6) 5 | p numbers 6 | 7 | # alternative solutions: 8 | # 9 | # numbers = numbers.slice(0, 2) + numbers.slice(-2, 2) 10 | # numbers = numbers.select { |number| number != 6 } 11 | # numbers = numbers.reject { |number| number == 6 } 12 | ``` 13 | -------------------------------------------------------------------------------- /source/solutions/04-hashes_1-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | puts dictionary[:two] 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions/04-hashes_1-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | dictionary[:four] = 'cuatro' 4 | puts dictionary[:four] 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/04-hashes_1-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | dictionary[:four] = 'cuatro' 4 | puts dictionary[:four].capitalize 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/04-hashes_1-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | p dictionary.key?(:one) 4 | 5 | # There's also the method has_key?, but key? is more common: 6 | # p dictionary.key?(:one) 7 | ``` 8 | -------------------------------------------------------------------------------- /source/solutions/04-hashes_1-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | puts dictionary.invert 4 | ``` -------------------------------------------------------------------------------- /source/solutions/05-methods-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def greet(name) 3 | "Hello #{name}!" 4 | end 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/05-methods-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def greet(name) 3 | greetings = ["Hello", "Hi", "Ohai", "ZOMG"].shuffle 4 | "#{greetings.first} #{name}!" 5 | end 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions/05-methods-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def miles_to_kilometers(miles) 3 | miles * 1.60934 4 | end 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions/05-methods-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def leap_year?(year) 3 | year % 4 == 0 4 | end 5 | 6 | # Bonus: 7 | 8 | def leap_year?(year) 9 | year % 400 == 0 or year % 100 != 0 and year % 4 == 0 10 | end 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words.delete("two") 4 | words.delete("four") 5 | 6 | # better solutions: 7 | # words = words.reject { |word| word == "two" or word == "four" } 8 | # words = words.select.with_index { |word, ix| ix.even? } 9 | 10 | p words 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | p words 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | words = words.map { |word| "#{word} <3" } 6 | p words 7 | ``` 8 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix * 2 + 1) 8 | "#{word} #{hearts}" 9 | end 10 | p words 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix + 1) 8 | "#{word} #{hearts}" 9 | end 10 | 11 | puts words.join(", ") 12 | ``` 13 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-6.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix + 1) 8 | "#{word} #{hearts}" 9 | end 10 | 11 | puts words.join("\n") 12 | ``` 13 | -------------------------------------------------------------------------------- /source/solutions/06-arrays_2-7.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | word = words.max_by { |word| word.length } 7 | width = word.length 8 | 9 | words = words.map.with_index do |word, ix| 10 | hearts = "<3" * (ix + 1) 11 | "#{word.ljust(width)} #{hearts}" 12 | end 13 | 14 | puts words.join("\n") 15 | ``` 16 | -------------------------------------------------------------------------------- /source/solutions/07-nested_arrays-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [ 3 | [1, 2, 3], 4 | [4, 5, 6], 5 | [7, 8, 9] 6 | ] 7 | 8 | sums = numbers.map do |row| 9 | sum = 0 10 | row.each { |number| sum = sum + number } 11 | sum 12 | end 13 | 14 | p sums 15 | 16 | # Using inject is a better, because more succinct: 17 | # 18 | # sums = numbers.map do |row| 19 | # row.inject(0) { |sum, number| sum + number } 20 | # end 21 | # 22 | # And you can even do: 23 | # 24 | # sums = numbers.map { |row| row.inject(&:+) } 25 | # 26 | # This last solution is pretty "magical", and hard to explain at this point. 27 | # But we thought we'd show it :) 28 | ``` 29 | -------------------------------------------------------------------------------- /source/solutions/07-nested_arrays-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [ 3 | [1, 2, 3], 4 | [2, 2, 2], 5 | [3, 2, 1] 6 | ] 7 | 8 | lines = numbers.map do |row| 9 | row.map { |number| "*" * number }.join(" ") 10 | end 11 | 12 | lines.each { |line| puts line } 13 | ``` 14 | -------------------------------------------------------------------------------- /source/solutions/08-hashes_2-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | selected = languages.select do |key, words| 14 | key == :de or key == :es 15 | end 16 | 17 | lines = selected.map do |key, name| 18 | words = dictionary[key] 19 | parts = words.map { |key, word| "#{key} means #{word}" } 20 | "In #{name}, #{parts.join(", ")}." 21 | end 22 | 23 | puts lines.join("\n") 24 | ``` 25 | -------------------------------------------------------------------------------- /source/solutions/08-hashes_2-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | lines = languages.keys.map do |key| 14 | words = dictionary[key].values 15 | "#{key} #{words.join(' ')}" 16 | end 17 | 18 | puts lines.join("\n") 19 | ``` 20 | -------------------------------------------------------------------------------- /source/solutions/08-hashes_2-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | columns = dictionary.values.map { |words| words.values }.transpose 14 | widths = columns.map do |column| 15 | column.max_by { |word| word.length }.length 16 | end 17 | 18 | lines = languages.keys.map do |key| 19 | words = dictionary[key].values 20 | words = words.map.with_index { |word, ix| word.ljust(widths[ix]) } 21 | "#{key} #{words.join(' ')}" 22 | end 23 | 24 | puts lines.join("\n") 25 | ``` 26 | -------------------------------------------------------------------------------- /source/solutions/08-hashes_2-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | columns = dictionary.values.map { |words| words.values }.transpose 14 | widths = columns.map do |column| 15 | column.max_by { |word| word.length }.length 16 | end 17 | 18 | lines = languages.keys.map do |key| 19 | words = dictionary[key].values 20 | words = words.map.with_index { |word, ix| word.ljust(widths[ix]) } 21 | "#{key} | #{words.join(' | ')}" 22 | end 23 | lines = lines.map { |line| "| #{line} |" } 24 | 25 | puts lines.join("\n") 26 | ``` 27 | -------------------------------------------------------------------------------- /source/solutions/09-truthiness-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | objects = [true, false, 0, 1, "", [], Object.new, Class.new, Module.new] 3 | 4 | rows = objects.map do |object| 5 | representation = object.inspect.sub(/:.*>/, ">").ljust(9) 6 | value = !!object 7 | [representation, value].join(" | ") 8 | end 9 | 10 | puts rows.join("\n") 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions/09-truthiness-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | objects = [true, false, 0, 1, "", [], Object.new, Class.new, Module.new] 3 | 4 | rows = objects.map do |left| 5 | objects.map { |right| [left, right] } 6 | end 7 | 8 | headers = [""] + objects.map { |object| object.inspect.sub(/:.*>/, ">") } 9 | width = headers.max_by { |header| header.length }.length 10 | headers = headers.map { |header| header.ljust(width) } 11 | 12 | lines = rows.map.with_index do |row, ix| 13 | cells = row.map do |cell| 14 | value = cell[0] == cell[1] 15 | value.inspect.ljust(width) 16 | end 17 | cells = [headers[ix + 1]] + cells 18 | cells.join(" | ") 19 | end 20 | lines = [headers.join(" | ")] + lines 21 | 22 | puts lines.join("\n") 23 | ``` 24 | -------------------------------------------------------------------------------- /source/solutions/10-email-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, date, from) 4 | @subject = subject 5 | @date = date 6 | @from = from 7 | end 8 | 9 | def subject 10 | @subject 11 | end 12 | 13 | def date 14 | @date 15 | end 16 | 17 | def from 18 | @from 19 | end 20 | end 21 | 22 | email = Email.new("Homework this week", "2014-12-01", "Ferdous") 23 | 24 | puts "Date: #{email.date}" 25 | puts "From: #{email.from}" 26 | puts "Subject: #{email.subject}" 27 | ``` 28 | -------------------------------------------------------------------------------- /source/solutions/10-email-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | email = Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Ferdous" }) 22 | 23 | puts "Date: #{email.date}" 24 | puts "From: #{email.from}" 25 | puts "Subject: #{email.subject}" 26 | ``` 27 | -------------------------------------------------------------------------------- /source/solutions/11-mailbox-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | class Mailbox 22 | def initialize(name, emails) 23 | @name = name 24 | @emails = emails 25 | end 26 | 27 | def name 28 | @name 29 | end 30 | 31 | def emails 32 | @emails 33 | end 34 | end 35 | 36 | emails = [ 37 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 38 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 39 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 40 | ] 41 | mailbox = Mailbox.new("Ruby Study Group", emails) 42 | 43 | mailbox.emails.each do |email| 44 | puts "Date: #{email.date}" 45 | puts "From: #{email.from}" 46 | puts "Subject: #{email.subject}" 47 | puts 48 | end 49 | ``` 50 | -------------------------------------------------------------------------------- /source/solutions/12-mailbox_text-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | # You will probably have found a solution that looks a little bit different 3 | # from the one below. 4 | # 5 | # Here is a step by step refactoring from one solution from our groups: 6 | # https://gist.github.com/svenfuchs/308e5f7f32b6e4920619 7 | # 8 | # Try understand each of the steps, and how it leads to the solution below. 9 | 10 | class Email 11 | def initialize(subject, headers) 12 | @subject = subject 13 | @headers = headers 14 | end 15 | 16 | def subject 17 | @subject 18 | end 19 | 20 | def date 21 | @headers[:date] 22 | end 23 | 24 | def from 25 | @headers[:from] 26 | end 27 | end 28 | 29 | class Mailbox 30 | def initialize(name, emails) 31 | @name = name 32 | @emails = emails 33 | end 34 | 35 | def name 36 | @name 37 | end 38 | 39 | def emails 40 | @emails 41 | end 42 | end 43 | 44 | class MailboxTextFormatter 45 | def initialize(mailbox) 46 | @mailbox = mailbox 47 | end 48 | 49 | def format 50 | lines = [ 51 | separator, 52 | format_row(["Date", "From", "Subject"]), 53 | separator, 54 | rows.collect { |row| format_row(row) }, 55 | separator, 56 | ] 57 | lines.join("\n") 58 | end 59 | 60 | def separator 61 | "+#{column_widths.map { |width| '-' * (width + 2) }.join("+")}+" 62 | end 63 | 64 | def format_row(row) 65 | cells = 0.upto(row.length - 1).map do |ix| 66 | row[ix].ljust(column_widths[ix]) 67 | end 68 | "| #{cells.join(" | ")} |" 69 | end 70 | 71 | def emails 72 | @mailbox.emails 73 | end 74 | 75 | def column_widths 76 | columns.map { |column| column.max_by { |cell| cell.length }.length } 77 | end 78 | 79 | def columns 80 | rows.transpose 81 | end 82 | 83 | def rows 84 | emails.collect { |email| [email.date, email.from, email.subject] } 85 | end 86 | end 87 | 88 | emails = [ 89 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 90 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 91 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 92 | ] 93 | mailbox = Mailbox.new("Ruby Study Group", emails) 94 | formatter = MailboxTextFormatter.new(mailbox) 95 | 96 | puts formatter.format 97 | ``` 98 | -------------------------------------------------------------------------------- /source/solutions/13-mailbox_html-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | class Mailbox 22 | def initialize(name, emails) 23 | @name = name 24 | @emails = emails 25 | end 26 | 27 | def name 28 | @name 29 | end 30 | 31 | def emails 32 | @emails 33 | end 34 | end 35 | 36 | class MailboxHtmlFormatter 37 | def initialize(mailbox) 38 | @mailbox = mailbox 39 | end 40 | 41 | def format 42 | tag(:html, [head, body].join("\n")) 43 | end 44 | 45 | def body 46 | tag(:body, [headline, table].join("\n")) 47 | end 48 | 49 | def headline 50 | tag(:h1, @mailbox.name) 51 | end 52 | 53 | def table 54 | tag(:table, [thead, tbody].join("\n")) 55 | end 56 | 57 | def thead 58 | tag(:thead, ths.join("\n")) 59 | end 60 | 61 | def ths 62 | headers = ["Date", "From", "Subject"] 63 | headers.map { |content| tag(:th, content) } 64 | end 65 | 66 | def tbody 67 | tag(:tbody, trs.join("\n")) 68 | end 69 | 70 | def trs 71 | rows.map { |row| tr(row) } 72 | end 73 | 74 | def tr(row) 75 | tag(:tr, tds(row).join("\n")) 76 | end 77 | 78 | def tds(row) 79 | row.collect { |content| tag(:td, content) } 80 | end 81 | 82 | def tag(name, content) 83 | content = "\n#{content}\n" unless [:h1, :td, :th].include?(name) 84 | html = "<#{name}>#{content}" 85 | html = indent(html) unless name == :html 86 | html 87 | end 88 | 89 | def indent(html) 90 | lines = html.split("\n") 91 | lines = lines.map { |line| " " * 2 + line } 92 | lines.join("\n") 93 | end 94 | 95 | def rows 96 | @mailbox.emails.collect do |email| 97 | [email.date, email.from, email.subject] 98 | end 99 | end 100 | 101 | def head 102 | " 103 | 112 | " 113 | end 114 | end 115 | 116 | emails = [ 117 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 118 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 119 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 120 | ] 121 | mailbox = Mailbox.new("Ruby Study Group", emails) 122 | formatter = MailboxHtmlFormatter.new(mailbox) 123 | 124 | puts formatter.format 125 | ``` 126 | -------------------------------------------------------------------------------- /source/solutions/14-mailbox_file-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | File.write("emails.html", formatter.format) 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions/15-mailbox_csv-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class EmailsCsvStore 3 | def initialize(filename) 4 | @csv = CSV.new(File.read(filename), headers: true) 5 | end 6 | 7 | def read 8 | @csv.map do |row| 9 | row = row.to_hash 10 | Email.new(row['Subject'], { from: row['From'], date: row['Date'] }) 11 | end 12 | end 13 | end 14 | ``` 15 | -------------------------------------------------------------------------------- /source/solutions_2/01-numbers-5.html: -------------------------------------------------------------------------------- 1 | ```ruby 2 | 0.odd? 3 | 0.even? 4 | 99.odd? 5 | 99.even? 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions_2/02-strings-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "Ruby" + "<3" * 3 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions_2/02-strings-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | 10 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions_2/02-strings-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | "1.23".to_f 3 | "1".to_i 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | 3 3 | 4 | # Remember the position of the first element in Arrays is referred 5 | # to as 0, not 1. 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | [1, 2, 3, 4, 5].index(4) 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | [1, 2, 3, 4, 5].length 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | p numbers[4] 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 | numbers[4] = 99 4 | p numbers[4] 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-6.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6] 3 | numbers = [numbers[1], numbers[3], numbers[5]] 4 | p numbers 5 | 6 | # This solution isn't terribly elegant, but at least it shows how 7 | # you can use the value returned from an array lookup, and create 8 | # a new array from it right away. 9 | # 10 | # Also possible, but not much better: 11 | 12 | second = numbers[1] 13 | fourth = numbers[3] 14 | sixth = numbers[4] 15 | numbers = [second, fourth, sixth] 16 | 17 | # In case you're curious, a way more elegant (and better) solution 18 | # to this would be the following: 19 | 20 | numbers = numbers.select { |number| number.even? } 21 | 22 | # However, we'll need to learn more about methods and blocks before 23 | # we'll be able to fully understand it. 24 | ``` 25 | -------------------------------------------------------------------------------- /source/solutions_2/03-arrays_1-7.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [1, 2, 3, 4, 5, 6] 3 | numbers = [numbers[1], numbers[3], numbers[5]] 4 | numbers = numbers.reverse 5 | p numbers 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions_2/04-hashes_1-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | puts dictionary[:two] 4 | ``` 5 | -------------------------------------------------------------------------------- /source/solutions_2/04-hashes_1-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | dictionary[:four] = 'cuatro' 4 | puts dictionary[:four] 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions_2/04-hashes_1-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | dictionary[:four] = 'cuatro' 4 | puts dictionary[:four].capitalize 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions_2/04-hashes_1-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | p dictionary.key?(:one) 4 | 5 | # There's also the method has_key?, but key? is more common: 6 | 7 | p dictionary.has_key?(:one) 8 | ``` 9 | -------------------------------------------------------------------------------- /source/solutions_2/04-hashes_1-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | dictionary = { :one => 'uno', :two => 'dos', :three => 'tres' } 3 | puts dictionary.invert 4 | ``` -------------------------------------------------------------------------------- /source/solutions_2/05-methods-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def greet(name) 3 | "Hello #{name}!" 4 | end 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions_2/05-methods-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def greet(name) 3 | greetings = ["Hello", "Hi", "Ohai", "ZOMG"].shuffle 4 | "#{greetings.first} #{name}!" 5 | end 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions_2/05-methods-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def miles_to_kilometers(miles) 3 | miles * 1.60934 4 | end 5 | ``` 6 | -------------------------------------------------------------------------------- /source/solutions_2/05-methods-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | def leap_year?(year) 3 | year % 4 == 0 4 | end 5 | 6 | # Bonus: 7 | 8 | def leap_year?(year) 9 | year % 400 == 0 or year % 100 != 0 and year % 4 == 0 10 | end 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words.delete("two") 4 | words.delete("four") 5 | 6 | # better solutions: 7 | # words = words.reject { |word| word == "two" or word == "four" } 8 | # words = words.select.with_index { |word, ix| ix.even? } 9 | 10 | p words 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | p words 6 | ``` 7 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | words = words.map { |word| "#{word} <3" } 6 | p words 7 | ``` 8 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix * 2 + 1) 8 | "#{word} #{hearts}" 9 | end 10 | p words 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-5.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix + 1) 8 | "#{word} #{hearts}" 9 | end 10 | 11 | puts words.join(", ") 12 | ``` 13 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-6.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | words = words.map.with_index do |word, ix| 7 | hearts = "<3" * (ix + 1) 8 | "#{word} #{hearts}" 9 | end 10 | 11 | puts words.join("\n") 12 | ``` 13 | -------------------------------------------------------------------------------- /source/solutions_2/06-arrays_2-7.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | words = ["one", "two", "three", "four", "five"] 3 | words = words.select.with_index { |word, ix| ix.even? } 4 | words = words.map { |word| word.capitalize } 5 | 6 | word = words.max_by { |word| word.length } 7 | width = word.length 8 | 9 | words = words.map.with_index do |word, ix| 10 | hearts = "<3" * (ix + 1) 11 | "#{word.ljust(width)} #{hearts}" 12 | end 13 | 14 | puts words.join("\n") 15 | ``` 16 | -------------------------------------------------------------------------------- /source/solutions_2/07-nested_arrays-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [ 3 | [1, 2, 3], 4 | [4, 5, 6], 5 | [7, 8, 9] 6 | ] 7 | 8 | sums = numbers.map do |row| 9 | sum = 0 10 | row.each { |number| sum = sum + number } 11 | sum 12 | end 13 | 14 | p sums 15 | 16 | # Using inject is a better, because more succinct: 17 | # 18 | # sums = numbers.map do |row| 19 | # row.inject(0) { |sum, number| sum + number } 20 | # end 21 | # 22 | # And you can even do: 23 | # 24 | # sums = numbers.map { |row| row.inject(&:+) } 25 | # 26 | # This last solution is pretty "magical", and hard to explain at this point. 27 | # But we thought we'd show it :) 28 | ``` 29 | -------------------------------------------------------------------------------- /source/solutions_2/07-nested_arrays-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | numbers = [ 3 | [1, 2, 3], 4 | [2, 2, 2], 5 | [3, 2, 1] 6 | ] 7 | 8 | lines = numbers.map do |row| 9 | row.map { |number| "*" * number }.join(" ") 10 | end 11 | 12 | lines.each { |line| puts line } 13 | ``` 14 | -------------------------------------------------------------------------------- /source/solutions_2/08-hashes_2-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | selected = languages.select do |key, words| 14 | key == :de or key == :es 15 | end 16 | 17 | lines = selected.map do |key, name| 18 | words = dictionary[key] 19 | parts = words.map { |key, word| "#{key} means #{word}" } 20 | "In #{name}, #{parts.join(", ")}." 21 | end 22 | 23 | puts lines.join("\n") 24 | ``` 25 | -------------------------------------------------------------------------------- /source/solutions_2/08-hashes_2-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | lines = languages.keys.map do |key| 14 | words = dictionary[key].values 15 | "#{key} #{words.join(' ')}" 16 | end 17 | 18 | puts lines.join("\n") 19 | ``` 20 | -------------------------------------------------------------------------------- /source/solutions_2/08-hashes_2-3.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | columns = dictionary.values.map { |words| words.values }.transpose 14 | widths = columns.map do |column| 15 | column.max_by { |word| word.length }.length 16 | end 17 | 18 | lines = languages.keys.map do |key| 19 | words = dictionary[key].values 20 | words = words.map.with_index { |word, ix| word.ljust(widths[ix]) } 21 | "#{key} #{words.join(' ')}" 22 | end 23 | 24 | puts lines.join("\n") 25 | ``` 26 | -------------------------------------------------------------------------------- /source/solutions_2/08-hashes_2-4.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | languages = { 3 | :de => 'German', 4 | :en => 'English', 5 | :es => 'Spanish', 6 | } 7 | dictionary = { 8 | :de => { :one => 'eins', :two => 'zwei', :three => 'drei' }, 9 | :en => { :one => 'one', :two => 'two', :three => 'three' }, 10 | :es => { :one => 'uno', :two => 'dos', :three => 'tres' } 11 | } 12 | 13 | columns = dictionary.values.map { |words| words.values }.transpose 14 | widths = columns.map do |column| 15 | column.max_by { |word| word.length }.length 16 | end 17 | 18 | lines = languages.keys.map do |key| 19 | words = dictionary[key].values 20 | words = words.map.with_index { |word, ix| word.ljust(widths[ix]) } 21 | "#{key} #{words.join(' | ')}" 22 | end 23 | lines = lines.map { |line| "| #{line} |" } 24 | 25 | puts lines.join("\n") 26 | ``` 27 | -------------------------------------------------------------------------------- /source/solutions_2/09-truthiness-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | objects = [true, false, 0, 1, "", [], Object.new, Class.new, Module.new] 3 | 4 | rows = objects.map do |object| 5 | representation = object.inspect.sub(/:.*>/, ">").ljust(9) 6 | value = !!object 7 | [representation, value].join(" | ") 8 | end 9 | 10 | puts rows.join("\n") 11 | ``` 12 | -------------------------------------------------------------------------------- /source/solutions_2/09-truthiness-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | objects = [true, false, 0, 1, "", [], Object.new, Class.new, Module.new] 3 | 4 | rows = objects.map do |left| 5 | objects.map { |right| [left, right] } 6 | end 7 | 8 | headers = [""] + objects.map { |object| object.inspect.sub(/:.*>/, ">") } 9 | width = headers.max_by { |header| header.length }.length 10 | headers = headers.map { |header| header.ljust(width) } 11 | 12 | lines = rows.map.with_index do |row, ix| 13 | cells = row.map do |cell| 14 | value = cell[0] == cell[1] 15 | value.inspect.ljust(width) 16 | end 17 | cells = [headers[ix + 1]] + cells 18 | cells.join(" | ") 19 | end 20 | lines = [headers.join(" | ")] + lines 21 | 22 | puts lines.join("\n") 23 | ``` 24 | -------------------------------------------------------------------------------- /source/solutions_2/10-email-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, date, from) 4 | @subject = subject 5 | @date = date 6 | @from = from 7 | end 8 | 9 | def subject 10 | @subject 11 | end 12 | 13 | def date 14 | @date 15 | end 16 | 17 | def from 18 | @from 19 | end 20 | end 21 | 22 | email = Email.new("Homework this week", "2014-12-01", "Ferdous") 23 | 24 | puts "Date: #{email.date}" 25 | puts "From: #{email.from}" 26 | puts "Subject: #{email.subject}" 27 | ``` 28 | -------------------------------------------------------------------------------- /source/solutions_2/10-email-2.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | email = Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Ferdous" }) 22 | 23 | puts "Date: #{email.date}" 24 | puts "From: #{email.from}" 25 | puts "Subject: #{email.subject}" 26 | ``` 27 | -------------------------------------------------------------------------------- /source/solutions_2/11-mailbox-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | class Mailbox 22 | def initialize(name, emails) 23 | @name = name 24 | @emails = emails 25 | end 26 | 27 | def name 28 | @name 29 | end 30 | 31 | def emails 32 | @emails 33 | end 34 | end 35 | 36 | emails = [ 37 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 38 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 39 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 40 | ] 41 | mailbox = Mailbox.new("Ruby Study Group", emails) 42 | 43 | mailbox.emails.each do |email| 44 | puts "Date: #{email.date}" 45 | puts "From: #{email.from}" 46 | puts "Subject: #{email.subject}" 47 | puts 48 | end 49 | ``` 50 | -------------------------------------------------------------------------------- /source/solutions_2/12-mailbox_text-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | # You will probably have found a solution that looks a little bit different 3 | # from the one below. 4 | # 5 | # Here is a step by step refactoring from one solution from our groups: 6 | # https://gist.github.com/svenfuchs/308e5f7f32b6e4920619 7 | # 8 | # Try understand each of the steps, and how it leads to the solution below. 9 | 10 | class Email 11 | def initialize(subject, headers) 12 | @subject = subject 13 | @headers = headers 14 | end 15 | 16 | def subject 17 | @subject 18 | end 19 | 20 | def date 21 | @headers[:date] 22 | end 23 | 24 | def from 25 | @headers[:from] 26 | end 27 | end 28 | 29 | class Mailbox 30 | def initialize(name, emails) 31 | @name = name 32 | @emails = emails 33 | end 34 | 35 | def name 36 | @name 37 | end 38 | 39 | def emails 40 | @emails 41 | end 42 | end 43 | 44 | class MailboxTextFormatter 45 | def initialize(mailbox) 46 | @mailbox = mailbox 47 | end 48 | 49 | def format 50 | lines = [ 51 | separator, 52 | format_row(["Date", "From", "Subject"]), 53 | separator, 54 | rows.collect { |row| format_row(row) }, 55 | separator, 56 | ] 57 | lines.join("\n") 58 | end 59 | 60 | def separator 61 | "+#{column_widths.map { |width| '-' * (width + 2) }.join("+")}+" 62 | end 63 | 64 | def format_row(row) 65 | cells = 0.upto(row.length - 1).map do |ix| 66 | row[ix].ljust(column_widths[ix]) 67 | end 68 | "| #{cells.join(" | ")} |" 69 | end 70 | 71 | def emails 72 | @mailbox.emails 73 | end 74 | 75 | def column_widths 76 | columns.map { |column| column.max_by { |cell| cell.length }.length } 77 | end 78 | 79 | def columns 80 | rows.transpose 81 | end 82 | 83 | def rows 84 | emails.collect { |email| [email.date, email.from, email.subject] } 85 | end 86 | end 87 | 88 | emails = [ 89 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 90 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 91 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 92 | ] 93 | mailbox = Mailbox.new("Ruby Study Group", emails) 94 | formatter = MailboxTextFormatter.new(mailbox) 95 | 96 | puts formatter.format 97 | ``` 98 | -------------------------------------------------------------------------------- /source/solutions_2/13-mailbox_html-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class Email 3 | def initialize(subject, headers) 4 | @subject = subject 5 | @headers = headers 6 | end 7 | 8 | def subject 9 | @subject 10 | end 11 | 12 | def date 13 | @headers[:date] 14 | end 15 | 16 | def from 17 | @headers[:from] 18 | end 19 | end 20 | 21 | class Mailbox 22 | def initialize(name, emails) 23 | @name = name 24 | @emails = emails 25 | end 26 | 27 | def name 28 | @name 29 | end 30 | 31 | def emails 32 | @emails 33 | end 34 | end 35 | 36 | class MailboxHtmlFormatter 37 | def initialize(mailbox) 38 | @mailbox = mailbox 39 | end 40 | 41 | def format 42 | tag(:html, [head, body].join("\n")) 43 | end 44 | 45 | def body 46 | tag(:body, [headline, table].join("\n")) 47 | end 48 | 49 | def headline 50 | tag(:h1, @mailbox.name) 51 | end 52 | 53 | def table 54 | tag(:table, [thead, tbody].join("\n")) 55 | end 56 | 57 | def thead 58 | tag(:thead, ths.join("\n")) 59 | end 60 | 61 | def ths 62 | headers = ["Date", "From", "Subject"] 63 | headers.map { |content| tag(:th, content) } 64 | end 65 | 66 | def tbody 67 | tag(:tbody, trs.join("\n")) 68 | end 69 | 70 | def trs 71 | rows.map { |row| tr(row) } 72 | end 73 | 74 | def tr(row) 75 | tag(:tr, tds(row).join("\n")) 76 | end 77 | 78 | def tds(row) 79 | row.collect { |content| tag(:td, content) } 80 | end 81 | 82 | def tag(name, content) 83 | content = "\n#{content}\n" unless [:h1, :td, :th].include?(name) 84 | html = "<#{name}>#{content}" 85 | html = indent(html) unless name == :html 86 | html 87 | end 88 | 89 | def indent(html) 90 | lines = html.split("\n") 91 | lines = lines.map { |line| " " * 2 + line } 92 | lines.join("\n") 93 | end 94 | 95 | def rows 96 | @mailbox.emails.collect do |email| 97 | [email.date, email.from, email.subject] 98 | end 99 | end 100 | 101 | def head 102 | " 103 | 112 | " 113 | end 114 | end 115 | 116 | emails = [ 117 | Email.new("Homework this week", { :date => "2014-12-01", :from => "Ferdous" }), 118 | Email.new("Keep on coding! :)", { :date => "2014-12-01", :from => "Dajana" }), 119 | Email.new("Re: Homework this week", { :date => "2014-12-02", :from => "Ariane" }) 120 | ] 121 | mailbox = Mailbox.new("Ruby Study Group", emails) 122 | formatter = MailboxHtmlFormatter.new(mailbox) 123 | 124 | puts formatter.format 125 | ``` 126 | -------------------------------------------------------------------------------- /source/solutions_2/14-mailbox_file-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | File.write("emails.html", formatter.format) 3 | ``` 4 | -------------------------------------------------------------------------------- /source/solutions_2/15-mailbox_csv-1.html.md: -------------------------------------------------------------------------------- 1 | ```ruby 2 | class EmailsCsvStore 3 | def initialize(filename) 4 | @csv = CSV.new(File.read(filename), headers: true) 5 | end 6 | 7 | def read 8 | @csv.map do |row| 9 | row = row.to_hash 10 | Email.new(row['Subject'], { from: row['From'], date: row['Date'] }) 11 | end 12 | end 13 | end 14 | ``` 15 | --------------------------------------------------------------------------------