Skip to content

Instantly share code, notes, and snippets.

@jnv
Last active December 30, 2015 00:08
Show Gist options
  • Save jnv/7746982 to your computer and use it in GitHub Desktop.
Save jnv/7746982 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": ""
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Refinements"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"> Due to Ruby\u2019s open classes you can redefine or add functionality to existing classes. This is called a **monkey patch**. Unfortunately the scope of such changes is global. All users of the monkey-patched class see the same changes. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"> Refinements are designed to reduce the impact of monkey patching on other users of the monkey-patched class. Refinements provide a way to extend a class locally."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"```ruby\n",
"module TimeExtensions\n",
" refine Fixnum do\n",
" def minutes; self * 60; end\n",
" end\n",
"end\n",
"\n",
"class MyApp\n",
" using TimeExtensions\n",
"\n",
" def initialize\n",
" p 2.minutes\n",
" end\n",
"end\n",
"\n",
"MyApp.new # => 120\n",
"p 2.minutes # => NoMethodError\n",
"```"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"module JSONGenerator\n",
" refine String do\n",
" def to_json; inspect end\n",
" end\n",
" \n",
" refine Fixnum do\n",
" def to_json; to_s end\n",
" end\n",
" \n",
" refine Array do\n",
" def to_json\n",
" # Refinements can see one another, so we can use String#to_json and\n",
" # Fixnum#to_json as part of the definition of Array#to_json.\n",
" \"[\" + map { |x| x.to_json }.join(',') + \"]\"\n",
" end\n",
" end\n",
"end"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 15,
"text": [
"#<refinement:Array@JSONGenerator>"
]
}
],
"prompt_number": 15
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"using JSONGenerator\n",
"['lorem', 'ipsum', 3].to_json"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 14,
"text": [
"\"[\\\"lorem\\\",\\\"ipsum\\\",3]\""
]
}
],
"prompt_number": 14
},
{
"cell_type": "heading",
"level": 3,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Subclasses & Reopening"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```ruby\n",
"class ApplicationController\n",
" using JSONGenerator\n",
"end\n",
"\n",
"# Somewhere else:\n",
"class ApplicationController\n",
" p 123.to_json # Still works\n",
"end\n",
"\n",
"class UsersController < ApplicationController\n",
" p 123.to_json # Still works\n",
"end\n",
"```"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"DCI: Data Context Interaction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```ruby\n",
"module Runner\n",
" refine User do\n",
" def run\n",
" puts \"I'm running!\"\n",
" end\n",
" end\n",
"end\n",
"\n",
"class UserRunsContext\n",
" using Runner\n",
"\n",
" def self.call\n",
" User.new.run \n",
" end\n",
"end\n",
"```"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Still not ready :("
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"module TimeExtensions\n",
" refine Fixnum do\n",
" def minutes; self * 60; end\n",
" end\n",
"end"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"output_type": "stream",
"stream": "stderr",
"text": [
"<main>:1: warning: Refinements are experimental, and the behavior may change in future versions of Ruby!\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 1,
"text": [
"#<refinement:Fixnum@TimeExtensions>"
]
}
],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"class Hour\n",
" using TimeExtensions\n",
" def seconds\n",
" 60.minutes\n",
" end\n",
"end\n",
"Hour.new.seconds"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"ename": "NoMethodError",
"evalue": "undefined method `using' for Hour:Class",
"output_type": "pyerr",
"traceback": [
"\u001b[31mNoMethodError\u001b[0m: undefined method `using' for Hour:Class",
"\u001b[37m<main>:1:in `<class:Hour>'\u001b[0m",
"\u001b[37m<main>:6:in `<main>'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/backend.rb:8:in `eval'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/backend.rb:8:in `eval'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/kernel.rb:105:in `execute_request'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/kernel.rb:57:in `run'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/command.rb:28:in `run_kernel'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/lib/iruby/command.rb:15:in `run'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/iruby-0.1.7/bin/iruby:6:in `<top (required)>'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/bin/iruby:23:in `load'\u001b[0m",
"\u001b[37m/home/j/.rbenv/versions/2.0.0-p247/bin/iruby:23:in `<main>'\u001b[0m"
]
}
],
"prompt_number": 3
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"eval <<-EOF\n",
" using TimeExtensions\n",
" \n",
" class Hour\n",
" def seconds\n",
" 60.minutes\n",
" end\n",
" end\n",
" puts Hour.new.seconds\n",
"EOF"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"3600\n"
]
}
],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"http://bugs.ruby-lang.org/projects/ruby-200/repository/entry/test/ruby/test_refinement.rb"
],
"language": "ruby",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Resources\n",
"\n",
"* http://timelessrepo.com/refinements-in-ruby\n",
"* http://www.ruby-doc.org/core-2.0.0/doc/syntax/refinements_rdoc.html\n",
"* http://mikepackdev.com/blog_posts/35-dci-with-ruby-refinements"
]
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment