Jekyll2018-02-04T11:48:03-08:00https://ronniemlr.com/Ronnie Miller is a software developer located in Portland, OR. He uses Ruby, Rails and React to deliver powerful web applications and small business software tools.Ronnie Millerme@ronniemlr.comhttp://ronniemlr.comHTTPS For Your Static S3 Website2018-01-22T00:00:00-08:002018-01-22T00:00:00-08:00https://ronniemlr.com/2018/01/22/https-for-your-static-s3-website<h2 id="why-https">Why HTTPS?</h2>
<p>This is just a static blog and personal website. Why? The better question is – why not?</p>
<p>In the wake of security disclosures like
Meltdown and Spectre we should all be thinking of ways to improve security,
whenever and wherever possible. That said, there are <a href="https://https.cio.gov/everything/">many reasons to use HTTPS
everywhere</a> not to mention the fact that with
initiatives like <a href="http://letsencrypt.org">Let’s Encrypt</a> it’s becoming easier
(and cheaper) than ever. If you aren’t going to do it now, when will you?</p>
<h2 id="getting-started">Getting Started</h2>
<p>Anyway, having thoroughly convinced you this is necessary, you may remember back
in May of 2016 that I moved my website from <a href="/2016/05/19/static-website-hosting-on-amazon-s3/">GitHub pages to Amazon
S3</a>. That’s still its home,
and probably will be for the foreseeable future.</p>
<p>Due to that it made sense to go with AWS Certificate Manager, rather than Let’s
Encrypt for the certificate. I’ll walk through each of the steps I took to setup
HTTPS for this website by using <a href="https://aws.amazon.com/certificate-manager/">AWS Certificate
Manager</a> and
<a href="https://aws.amazon.com/cloudfront/">CloudFront</a>.</p>
<p>The main steps are:</p>
<ol>
<li>Use AWS Certificate Manager to obtain an SSL Certificate</li>
<li>Verify ownership of your domain name by adding a CNAME entry</li>
<li>Setup a CloudFront Distribution to point to your website’s S3 bucket</li>
<li>Update your DNS to point to the CloudFront distribution URL</li>
</ol>
<p>The steps above assume you already have a <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html#tutorial-contents">static S3 website with
a custom domain</a>
and that you can manage your domain name’s DNS records.</p>
<h2 id="obtain-the-ssl-certificate">Obtain the SSL Certificate</h2>
<p>AWS Certificate Manager makes it easy to obtain an SSL certificate from a
Certified Authority, in this case Amazon. They also take care to auto-renew the
certificate and it’s free when you use it with other Amazon products.</p>
<p>To get started visit <a href="https://console.aws.amazon.com/acm/home?region=us-east-1#/">AWS Certificate
Manager</a> in the
<code class="highlighter-rouge">us-east-1</code> (N. Virginia) region:exclamation: Region selection becomes important when we later setup
CloudFront, since it can only use Certificate Manager certificates you created in <code class="highlighter-rouge">us-east-1</code>. I ended
up having to redo this step when I got further along.</p>
<p>Click “Request a certificate” and you’ll be prompted to enter the domain name(s)
for your certificate. For this website I used <code class="highlighter-rouge">ronniemlr.com</code> and
<code class="highlighter-rouge">www.ronniemlr.com</code>.</p>
<p><img src="/images/posts/amazon-cert-add-domains.png" alt="Add Domain Names" class="image center" /></p>
<h2 id="verify-domain-ownership">Verify Domain Ownership</h2>
<p>Next you have to prove to Amazon you own the domain name. You can do this with
DNS verification or Email verification. I chose DNS verification, but email is
probably even easier. With DNS verification Amazon gives you CNAME entries to
add for each domain name variant you requested for the certificate.</p>
<p><img src="/images/posts/amazon-cert-verification.png" alt="Certificate Verification" class="image center" /></p>
<p>After you add the CNAME records to your domain name and wait a few minutes, the
validation status will change from “Pending validation” to “Success” and you can
proceed with the next step.</p>
<h2 id="setup-cloudfront-distribution">Setup CloudFront Distribution</h2>
<p>The next thing you need to do is create a CloudFront web distribution. This will
give you a CloudFront URL that will respond and serve traffic over
HTTPS, once you configure it to do so.</p>
<p>Head over to the <a href="https://console.aws.amazon.com/cloudfront/home">CloudFront
dashboard</a> and click “Create
Distribution” then “Get Started” under the “Web” distribution type.</p>
<p>You’ll be prompted to select the <em>Origin Domain Name</em>. If you click the field it will
list your available S3 buckets.:exclamation: Don’t do this. This is another trap that I fell into.
Because you’ve configured the bucket to be a static website host, S3 provides
you with another URL. This is the URL you’ve probably used to point your
custom domain name to your S3 website.</p>
<p><img src="/images/posts/amazon-cert-s3-properties.png" alt="S3 Bucket URL" class="image center" /></p>
<p>To find your S3 static website host URL, go to your website’s bucket in the S3 dashboard
then click the properties tab, then the “Static website hosting” box.</p>
<p>If you just use the standard S3 URL suggested for you, your settings for
“Index document” and “Error document” and others won’t be honored.</p>
<h3 id="distribution-settings">Distribution settings</h3>
<p>Back in the CloudFront dashboard, you can leave the other settings for origin
and caching as-is or set them up to your liking (I personally like to redirect
non HTTP to HTTPS using “Viewer Protocol Policy”).</p>
<p>The most important distribution settings in particular are the SSL certificate
and the SSL Client Support settings. Choose the “Custom SSL Certificate” option,
then you should be able to choose the certificate manager certificate you
created previously.</p>
<p><img src="/images/posts/amazon-cert-cloudfront-distribution.png" alt="Distribution Settings" class="image center" /></p>
<p>Be sure to choose the <em>Only Clients That Support Server Name Indication (SNI)</em>
option. This is the free one. You can <a href="https://https.cio.gov/sni/">read more about
SNI</a> and its limitations, but essentially older
clients (Internet Explorer on XP, Android 2.3 for example) won’t work.</p>
<p>When the alternative costs $600/mo for a dedicated IP address, I think I can
live with those clients not viewing my website. If you run a mission critical
operation or one with paying customers, you’re probably reading the wrong blog post!</p>
<p>Review your settings and click “Create Distribution”. After a few minutes your
distribution will be ready to use. However, you should already see the domain name
assigned to you. It will look something like this: <code class="highlighter-rouge">dln7ue9x0nvom.cloudfront.net</code></p>
<h2 id="pointing-your-domain-to-cloudfront">Pointing your Domain to CloudFront</h2>
<p>The final step is to point your custom domain name to the CloudFront
distribution URL. The way I do this is by using an ALIAS record on my domain,
swapping out the S3 static website URL for the CloudFront distribution URL:</p>
<p><img src="/images/posts/amazon-cert-dns-alias.png" alt="Distribution Settings" class="image center" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>I’ve been wanting to try Amazon Certificate Manager for a while now and I’m glad
I got around to doing so. I was pleasantly surprised with how easy it was to
setup and get an SSL certificate issued. I didn’t touch a single openssl command
and not a single certificate signing request was harmed in the making of this
blog post. Feels like a win.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://serverfault.com/a/776143">What can happen if you don’t use the correct S3 URL</a></li>
<li><a href="https://knightlab.northwestern.edu/2015/05/21/implementing-ssl-on-amazon-s3-static-websites/">Implementing SSL on Amazon S3 Static Websites</a></li>
<li><a href="https://https.cio.gov/sni/">What is Server Name Indication (SNI)</a></li>
</ul>
Why HTTPS?Static Website Hosting on Amazon S32016-05-19T00:00:00-07:002016-05-19T00:00:00-07:00https://ronniemlr.com/2016/05/19/static-website-hosting-on-amazon-s3<h2 id="goodbye-github-pages">Goodbye GitHub Pages</h2>
<p>For a little over 3½ years this website was hosted on
<a href="https://pages.github.com/">GitHub Pages</a>. It used <a href="http://jekyllrb.com">Jekyll</a>
(and still does) to compile the layouts, content, styles, etc. into this static
website you’re reading now. For me, the greatest thing about static site generators
like Jekyll is their simplicity. You write the code, you run a command, it spits
out a website. The output you receive is all of the files necessary to view your
website, assets all compiled and ready to go. The problem for me was that using
GitHub Pages sometimes removed that key piece of simplicity.</p>
<p><a href="https://github.com/blog/2100-github-pages-now-faster-and-simpler-with-jekyll-3-0">New versions of Jekyll</a>
or other <a href="https://github.com/blog/2151-github-pages-drops-support-for-rdiscount-redcarpet-and-redcloth-textile-markup-engines">updates</a>
would be rolled out that either dropped or changed support for certain features.
If you weren’t on top of these announcements you may have had to do some leg work to
get your local build behaving exactly like GitHub Pages before knowing your
deploys were working as expected.</p>
<p>While this didn’t cause me issues frequently, it was an annoyance that ruined
the simplicity of static site generators. It also inevitably means that you’re
bound to the constraints that GitHub Pages imposes and can’t use custom
plugins or parsers as freely as you might like.</p>
<h2 id="hello-amazon-s3">Hello Amazon S3</h2>
<p><a href="https://aws.amazon.com/s3">Amazon S3</a> provides static website hosting using a
standard S3 bucket and some minor configuration. What drew me to move to S3 was
the simplicity that mirrors a typical static site generator. Essentially with S3,
you have some files (i.e. your static website files), you upload them to S3, you
tell S3 you want the bucket to be a website, your website is online.</p>
<p>The added benefit is that because you’re responsible for putting the files in a
bucket that will ultimately become your website, there is no limit to how
you can use or configure your static site generator; you take the files it
outputs locally and you put them on S3. Done. The site you previewed locally is
the site that shows online. No compatibility concerns with whatever is building
the site when you deploy, as with GitHub Pages.</p>
<h2 id="configuring-s3-as-a-static-website-host">Configuring S3 as a Static Website Host</h2>
<p>Amazon has pretty good documentation on <a href="http://docs.aws.amazon.com/gettingstarted/latest/swh/website-hosting-intro.html#tutorial-contents">configuring a bucket to be a static
website host</a>.
The steps are essentially what I followed for this website: create a bucket,
configure the bucket, deploy the website. You can follow along the docs and use
the AWS Management Console to create and configure the bucket.</p>
<p>We can also do it with code, so why not?</p>
<p>The <a href="https://github.com/aws/aws-sdk-ruby"><code class="highlighter-rouge">aws-sdk</code></a> gem supports managing
pretty much any resource on AWS including website buckets. The following code
configures the SDK gem and creates a bucket, then configures it for
static website hosting by creating a policy that allows access to all files in
the bucket. If you had more specific access requirements you could extend the
policy.</p>
<figure>
<figcaption>~/.aws/credentials</figcaption>
{% highlight ruby %}
[default]
aws_access_key_id = <your default="" access="" key="">
aws_secret_access_key = <your default="" secret="" key="">
{% endhighlight %}
&lt;/figure&gt;
<figure>
<figcaption>Gemfile</figcaption>
{% highlight ruby %}
source 'https://rubygems.org'
gem 'aws-sdk'
gem 'dotenv' # we'll use this later
{% endhighlight %}
</figure>
<figure>
<figcaption>Rakefile</figcaption>
{% highlight ruby linenos=table %}
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError =&gt; e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'aws-sdk'
desc "Create website bucket on s3"
task :create do
# Perform operations in us-west-2 (or change to your preferred region)
client = Aws::S3::Client.new(region: 'us-west-2')
client.create_bucket(bucket: ENV['bucket'])
client.put_bucket_website({
bucket: ENV['bucket'],
website_configuration: {
index_document: {
suffix: "index.html",
}
}
})
client.put_bucket_policy({
bucket: ENV['bucket'],
policy: {
"Version" =&gt; "2012-10-17",
"Statement" =&gt; [
{
"Sid" =&gt; "AddPerm",
"Effect" =&gt; "Allow",
"Principal" =&gt; "*",
"Action" =&gt; "s3:GetObject",
"Resource" =&gt; "arn:aws:s3:::#{ENV['bucket']}/*"
}
]
}.to_json
})
client.put_object({
bucket: ENV['bucket'],
key: "index.html",
body: "Hello World!"
})
end
{% endhighlight %}
</figure>
This small Rake task will put an accessible "Hello World" web page on S3.
The first 10-15 lines are just boilerplate gem loading. By default, the
`aws-sdk` gem will load credentials stored in your user's home folder in
`.aws/credentials`.
The task can be invoked with `rake create bucket=foobarbazbaz.com` where `bucket`
is the domain name of the website. This isn't the standard rake task argument syntax,
but I find using the environment variable syntax is more readable and easier to type.
* Line 17 sets up the SDK client and defines which region the bucket will be in.
* Line 19 creates the bucket with the name given.
* Lines 21-28 sets the basic configuration for a bucket website. This is
effectively the same thing as clicking the "Enable website hosting" radio
button in the management console and providing an index page value.
* Lines 30-44 create an access policy for the bucket. Since this is a static
website bucket and all files need to be accessible by the browser, the policy
allows access to all files using a wildcard.
* Lines 46-50 just puts a "Hello World!" index.html page in the bucket, to
greet visitors.
The web page `http://<your-bucket-name>.s3-website-<region>.amazonaws.com`
will become available after running the Rake task. In this case, running the
task created [http://foobarbazbaz.com.s3-website-us-west-2.amazonaws.com](http://foobarbazbaz.com.s3-website-us-west-2.amazonaws.com/).
Of course, this step only needs to be done a single time. You should make sure
whichever domain name you own that you want to host using S3 becomes the name
of your bucket. This will be important when configuring a custom domain name.
## Deploying the Website to S3
With the bucket created and configured, all that's left to do to make this a
real website is put the files from the static site build into the bucket.
With Jekyll the built site is placed into a `_site` directory. The following Rake
task will iterate through each file in that directory, uploading each file to
the same path on S3.
<figure>
<figcaption>Rakefile</figcaption>
{% highlight ruby linenos=table %}
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError =&gt; e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
# Load region and S3 credentials from .env file, ex:
# REGION: us-west-2
# BUCKET: ronniemlr.com
# ACCESS_KEY_ID: <your access="" key="">
# SECRET_ACCESS_KEY: <your secret="" key="">
require 'dotenv'
Dotenv.load
desc "Deploy website to S3"
task :deploy do
puts "Building website"
`jekyll build`
puts "Deploying website"
require 'aws-sdk'
s3 = Aws::S3::Resource.new(region: ENV['REGION'],
access_key_id: ENV['ACCESS_KEY_ID'],
secret_access_key: ENV['SECRET_ACCESS_KEY'])
build = Pathname.new("_site")
Dir.glob("_site/**/*.*").each do |file|
# Glob can still pick up directories
next if File.directory?(file)
source = Pathname.new(file)
destination = source.relative_path_from(build)
object = s3.bucket(ENV['BUCKET']).object(destination.to_s)
object.upload_file(source)
end
puts "Website deployed"
end
{% endhighlight %}
&lt;/figure&gt;
This script could be improved to only deploy changed files since the last
commit, but for a small site like this one, deploying the whole thing doesn't
take very long. Running the task with `rake deploy` puts the site online in
under a minute. Right now, I just run this command manually when I want to deploy
changes, but this could easily be called by a git hook.
&gt; Update: 2/4/17: I've since switched to using the excellent
&gt; [s3_website](https://github.com/laurilehmijoki/s3_website) tool.
&gt; It handles diffing for you and even asks you if you want to clean up old files on the s3 bucket.
## Using a Custom Domain
The S3 documentation of course recommends that you use Amazon's Route 53 service
to route your custom domain to your S3 bucket. The difference in the documented
setup and the one I landed on is that I don't use Route 53. I also don't create
a second bucket strictly for the `www` variant of this site, which Amazon
recommends you create in order to redirect that traffic to the main bucket.
Instead I use DNSimple <small>([shameless referral link](https://dnsimple.com/r/d21999fd16e53e))</small>
which I've used to manage my domains for many years. Personally, I love DNSimple
because they make it push-button easy to point your domain at S3. Assuming your
bucket is named correctly it "Just Works&trade;."
<img alt="DNSimple S3 Service" src="/images/posts/dns-simple-s3.png" width="625" height="160" class="center" />
DNSimple will create an [ALIAS record](https://support.dnsimple.com/articles/alias-record/)
for you that makes your domain map to the S3 bucket. For the `www` redirect,
I just use a [URL record](https://support.dnsimple.com/articles/url-record/).
Both of these are actually special DNS record types [created by
DNSimple](https://blog.dnsimple.com/2011/11/introducing-alias-record/) to make
this stuff a no-brainer. One caveat is that URL redirect records don't work for
HTTPS requests.
## Conclusion
It might look like a lot of configuration, but it doesn't take long. Overall I'm
happy with this setup and glad to have more flexibility by having full control
over the final build and deploy. Bye GH Pages! :wave:
## Resources
* [Technical reasons behind the ALIAS record](https://blog.dnsimple.com/2014/01/why-alias-record/)
* [Hosting a Static Website on Amazon Web Services](http://docs.aws.amazon.com/gettingstarted/latest/swh/website-hosting-intro.html)
* [AWS SDK Documentation - put_bucket_website](http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html#put_bucket_website-instance_method)
* [AWS SDK Documentation - put_bucket_policy](http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html#put_bucket_policy-instance_method)
</your></your></figure></region></your-bucket-name></your></your></figure>
Goodbye GitHub PagesRuby 2.3 – A Gift for You and Me2015-12-26T00:00:00-08:002015-12-26T00:00:00-08:00https://ronniemlr.com/2015/12/26/ruby-2.3-a-gift-for-you-and-me<p>It’s beginning to feel like tradition –– for the <a href="http://ronniemlr.com/2014/12/25/trying-out-new-features-of-ruby-2.2/">second year
now</a>,
the Ruby core team has shipped a minor point update on Christmas Day.
This year gifting the world Ruby 2.3.</p>
<p>After reading up on this release, it feels more more like a major
point update, just for the sheer amount of work that must have been involved.
Part of the release seems to be <a href="https://wyeworks.com/blog/2015/12/1/immutable-strings-in-ruby-2-dot-3">paving the way</a>
for <a href="https://bugs.ruby-lang.org/issues/11473">immutability in Ruby 3.0</a>
and a large chunk of the release provided new syntax and language features.</p>
<p>Let’s take a look at a few of the new features.
I was most looking forward to both the <code class="highlighter-rouge">Hash#dig</code> and <code class="highlighter-rouge">Array#dig</code> methods.
The “safe navigation” operator was added, which apparently is a
<a href="https://blog.heroku.com/archives/2015/12/25/ruby-2-3-0-on-heroku-with-matz#do-you-have-any-favorite-features-coming-in-ruby-2-3">favorite of Matz</a>.
Additionally, the <a href="https://bugs.ruby-lang.org/issues/11252">did_you_mean gem</a> is
now built into the language, a breakthrough for typo-driven-development of which
I am so fond.</p>
<h2 id="install-ruby-230">Install Ruby 2.3.0</h2>
<p>Using <a href="https://github.com/postmodern/ruby-install">ruby-install</a> we can quickly
grab the latest version of ruby.</p>
<p>{% highlight bash %}
ruby-install -M https://ftp.ruby-lang.org/pub/ruby ruby 2.3.0
{% endhighlight %}</p>
<p>Once that’s done downloading and compiling, I like
<a href="https://github.com/postmodern/chruby">chruby</a> for switching to the new version.</p>
<p>{% highlight bash %}
chruby 2.3
{% endhighlight %}</p>
<h2 id="hashdig-and-arraydig">Hash#dig and Array#dig</h2>
<p>Both of these methods are great if you’re dealing with unwieldy data sets that you
may or may not have control over. Think complex API responses, legacy system interfaces,
etc. They allow you to safely traverse into the data structure without raising
an error if a single node in the lookup doesn’t exist.</p>
<h3 id="hashdig"><code class="highlighter-rouge">Hash#dig</code></h3>
<p>Given a hash that looks like this:</p>
<p>{% highlight ruby %}
data = {
people: [
{
first_name: “Jamie”,
last_name: “Jackson”,
team: “Blue Squad”,
qualifications: [
{
skill: “Oil Change”,
type: “primary”
},
{
skill: “Alignment”,
type: “advanced”
}
]
},
{
first_name: “Jordan”,
last_name: “Jacobs”,
team: “Red Squad”,
qualifications: []
}
]
}
{% endhighlight %}</p>
<p>The dig method allows you to pull out data or simply get <code class="highlighter-rouge">nil</code> if the piece
of data doesn’t exist at the path you’ve “dug” to. For instance:</p>
<p>{% highlight ruby %}
data[:people][0][:qualifications][0][:skill]</p>
<h1 id="-oil-change">=&gt; Oil Change</h1>
<p>data[:people][1][:qualifications][0][:skill]</p>
<h1 id="-nomethoderror-undefined-method--for-nilnilclass">=&gt; NoMethodError: undefined method `[]’ for nil:NilClass</h1>
<h1 id="thrown-since-datapeople1qualifications0-is-nil">thrown since data[:people][1][:qualifications][0] is nil</h1>
<p>data.dig(:people, 0, :qualifications, 0, :skill)</p>
<h1 id="-oil-change-1">=&gt; Oil Change</h1>
<p>data.dig(:people, 1, :qualifications, 0, :skill)</p>
<h1 id="-nil">=&gt; nil</h1>
<p>{% endhighlight %}</p>
<h3 id="arraydig"><code class="highlighter-rouge">Array#dig</code></h3>
<p>The dig method for Array works similarly:</p>
<p>{% highlight ruby %}
data = [
[10, [10, 4]],
[15, [15, 3]],
[20]
]</p>
<p>data[0][1][0]</p>
<h1 id="-10">=&gt; 10</h1>
<p>data[1][1][0]</p>
<h1 id="-15">=&gt; 15</h1>
<p>data[2][1][0]</p>
<h1 id="-nomethoderror-undefined-method--for-nilnilclass-1">=&gt; NoMethodError: undefined method `[]’ for nil:NilClass</h1>
<p>data.dig(0, 1, 0)</p>
<h1 id="-10-1">=&gt; 10</h1>
<p>data.dig(1, 1, 0)</p>
<h1 id="-15-1">=&gt; 15</h1>
<p>data.dig(2, 1, 0)</p>
<h1 id="-nil-1">=&gt; nil</h1>
<p>{% endhighlight %}</p>
<h2 id="safe-navigation-operator">Safe Navigation Operator</h2>
<p>This is something that I hope people will use sparingly since it seems like it
would be a smell otherwise. The “safe navigation” operator allows you to call
a method on a variable that might be nil. Other languages, such as C# and Swift,
have a similar feature.</p>
<p>In Ruby it looks like this: <code class="highlighter-rouge">&amp;.</code></p>
<p>{% highlight ruby %}</p>
<h1 id="assume-nilly-is-nil-because-user-was-not-found">assume nilly is nil because User was not found</h1>
<p>nilly = User.find_by(username: “nilly”)</p>
<h1 id="before-ruby-23">Before Ruby 2.3</h1>
<p>nilly.notify! # NoMethodError error because nilly is nil!
nilly &amp;&amp; nilly.notify! # avoid NoMethodError using short-circuit conditional</p>
<h1 id="after-ruby-23">After Ruby 2.3</h1>
<p>nilly&amp;.notify! # will only call notify! if nilly is not nil</p>
<h1 id="-nil-----------------just-returns-nil-when-object-is-nil">=&gt; nil # just returns nil when object is nil</h1>
<p>{% endhighlight %}</p>
<p>I can see this coming in handy in some situations, but I would consider using a
<a href="https://www.google.com/search?q=null+object+pattern">null object pattern</a>
if I frequently found myself calling methods on something that is possibly nil.</p>
<h2 id="did_you_mean">did_you_mean</h2>
<p>The <a href="https://github.com/yuki24/did_you_mean">did_you_mean gem</a> is a nice little
tool that provides helpful suggestions when you mispell or tpyo a word. It’s
also nice when you might have a good idea of a method name, but need a
little help.</p>
<p>Check it out:</p>
<p>{% highlight ruby %}
data = [1, 2, 3, 4]</p>
<p>data.puhs(5)</p>
<h1 id="nomethoderror-undefined-method-puhs-for-1-2-3-4array">NoMethodError: undefined method `puhs’ for [1, 2, 3, 4]:Array</h1>
<h1 id="did-you-mean--push">Did you mean? push</h1>
<h1 id="puts">puts</h1>
<p>data.push(5)</p>
<p>data.sliver(2,5)</p>
<h1 id="nomethoderror-undefined-method-sliver-for-1-2-3-4-5array">NoMethodError: undefined method `sliver’ for [1, 2, 3, 4, 5]:Array</h1>
<h1 id="did-you-mean--slice">Did you mean? slice</h1>
<h1 id="slice">slice!</h1>
<p>data.slice(2,5)</p>
<h1 id="-3-4-5">=&gt; [3, 4, 5]</h1>
<p>{% endhighlight %}</p>
<p>What do you like about this release?</p>
<h2 id="sources-and-info">Sources and Info</h2>
<ul>
<li><a href="https://www.ruby-lang.org/en/news/2015/12/25/ruby-2-3-0-released/">Ruby 2.3 release announcement</a></li>
<li>Hash#dig (<a href="http://ruby-doc.org/core-2.3.0/Hash.html#method-i-dig">docs</a>),
Array#dig (<a href="http://ruby-doc.org/core-2.3.0/Array.html#method-i-dig">docs</a>) –
<a href="https://bugs.ruby-lang.org/issues/11643">discussion</a></li>
<li>Safe Navigation Operator –
<a href="https://bugs.ruby-lang.org/issues/11537">discussion</a>,
<a href="https://github.com/ruby/ruby/commit/a356fe1c3550892902103f66928426ac8279e072">commit</a></li>
<li>did_you_mean –
<a href="https://bugs.ruby-lang.org/issues/11252">discussion</a>,
<a href="https://github.com/yuki24/did_you_mean">gem homepage</a></li>
</ul>
It’s beginning to feel like tradition –– for the second yearnow,the Ruby core team has shipped a minor point update on Christmas Day.This year gifting the world Ruby 2.3.Slack bot for API Documentation2015-09-05T00:00:00-07:002015-09-05T00:00:00-07:00https://ronniemlr.com/2015/09/05/slack-bot-api-documentation<p>The number of Slack bots, Slack plug-ins and other integrations available
recently seems to have skyrocketed. There are plug-ins to help facilitate <a href="https://twitter.com/moriogawa/status/629485213849157632/">live
blogging</a>, bots that
can <a href="https://www.roomino.com/">book rooms for your team</a> (hopefully better than
your company’s travel department), there’s even a Slack bot that will
<a href="http://christinac.github.io/ellie-slack/">listen to all of your frustrations</a>.</p>
<p>At work we switched to Slack recently and there are many things I’ve been
wanting to try. One being to create a bot that will give API documentation
and example responses.</p>
<p>In order to do this we’ll take a simple JSON schema, parse it a bit,
then provide a sample response based on the schema. To handle this I’m
leaning heavily on both <a href="http://github.com/interagent/prmd">Prmd</a>,
a tool for managing JSON schemas and generating documentation from them and
for the Slack integration, the excellent
<a href="http://github.com/dblock/slack-ruby-bot">slack-ruby-bot</a>.</p>
<h2 id="creating-a-json-schema">Creating a JSON Schema</h2>
<p>I just recently started diving into JSON schema, so I’m still fairly new myself.
I found the online book <em><a href="http://spacetelescope.github.io/understanding-json-schema/">Understanding JSON
Schema</a></em> to be a
great resource. For the purposes of this blog post I’m going to use a generic “person”
schema. This person object will have a first name, last name, and an email address.</p>
<p>We’ll use Prmd to combine a <code class="highlighter-rouge">meta.yml</code> and a <code class="highlighter-rouge">person.yml</code> file into our
<code class="highlighter-rouge">schema.json</code> file.</p>
<figure>
<figcaption>meta.yml</figcaption>
{% highlight yaml %}
id: "person-api"
description: "Person Example API"
title: "Person Example API"
links:
- href: "https://api.example.com"
rel: "self"
definitions:
identity:
"$ref": "#/definitions/id"
id:
description: "Unique identifier of a resource."
example: "1dc3567e-acd4-4819-afd5-21d0ef677dcd"
readOnly: true
format: "uuid"
type: "string"
{% endhighlight %}
</figure>
<figure>
<figcaption>person.yml</figcaption>
{% highlight yaml %}
id: "person"
title: "Person"
properties:
id:
"$ref": "#/definitions/id"
first_name:
description: "The person's first name."
example: "Jean-Luc"
type: "string"
last_name:
description: "The person's last name."
example: "Picard"
type: "string"
email_address:
description: "The person's email address."
example: "locutus@borg.hive"
format: "email"
type: "string"
definitions:
person:
description: "A single person"
properties:
id:
"$ref": "#/id"
first_name:
"$ref": "#/first_name"
last_name:
"$ref": "#/last_name"
email_address:
"$ref": "#/email_address"
type: "object"
links:
- title: "Person details"
description: "Get the details of a person"
method: GET
href: "/person/{#/definitions/identity}"
targetSchema:
"$ref": "#/person"
{% endhighlight %}
</figure>
<p>Combining these into a single schema file is easy:</p>
<p>{% highlight bash %}
prmd combine –meta meta.yml person.yml &gt; schema.json
{% endhighlight %}</p>
<p>This produces:</p>
<figure>
<figcaption>schema.json</figcaption>
{% highlight javascript %}
{
"$schema": "http://interagent.github.io/interagent-hyper-schema",
"type": [
"object"
],
"definitions": {
"identity": {
"$ref": "#/definitions/id"
},
"id": {
"description": "Unique identifier of a resource.",
"example": "1dc3567e-acd4-4819-afd5-21d0ef677dcd",
"readOnly": true,
"format": "uuid",
"type": [
"string"
]
},
"person": {
"title": "Person",
"properties": {
"id": {
"$ref": "#/definitions/id"
},
"first_name": {
"description": "The person's first name.",
"example": "Jean-Luc",
"type": [
"string"
]
},
"last_name": {
"description": "The person's last name.",
"example": "Picard",
"type": [
"string"
]
},
"email_address": {
"description": "The person's email address.",
"example": "locutus@borg.hive",
"format": "email",
"type": [
"string"
]
}
},
"definitions": {
"person": {
"description": "A single person",
"properties": {
"id": {
"$ref": "#/definitions/id"
},
"first_name": {
"$ref": "#/definitions/first_name"
},
"last_name": {
"$ref": "#/definitions/last_name"
},
"email_address": {
"$ref": "#/definitions/email_address"
}
},
"type": [
"object"
]
}
},
"links": [
{
"title": "Person details",
"description": "Get the details of a person",
"method": "GET",
"href": "/person/{#/definitions/identity}",
"targetSchema": {
"$ref": "#/definitions/person"
}
}
]
}
},
"properties": {
"person": {
"$ref": "#/definitions/person"
}
},
"id": "person-api",
"description": "Person Example API",
"title": "Person Example API",
"links": [
{
"href": "https://api.example.com",
"rel": "self"
}
]
}
{% endhighlight %}
</figure>
<h2 id="creating-a-basic-slack-bot">Creating a basic Slack bot</h2>
<p>We will need the following files:</p>
<p>{% highlight bash %}
docutron/
response.rb
docutron.rb
Gemfile
schema.json # the generated output from above
{% endhighlight %}</p>
<h3 id="gemfile">Gemfile</h3>
<p>{% highlight ruby %}
source ‘http://rubygems.org’</p>
<p>gem ‘slack-ruby-bot’
gem ‘prmd’
{% endhighlight %}</p>
<h2 id="docutronrb">docutron.rb</h2>
<p>This will be the main entry point into the bot when a webhook payload is received.</p>
<p>{% highlight ruby linenos=table %}
require ‘slack-ruby-bot’
require_relative ‘docutron/response’</p>
<p>module Docutron
class App &lt; SlackRubyBot::App
end</p>
<p>class SlackBot &lt; SlackRubyBot::Commands::Base
DOC_REQUEST = /^(?<request_method>\w*) (?<resource>\w*)$/</resource></request_method></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>match DOC_REQUEST do |client, data, match|
method, resource = match[:request_method], match[:resource]
response = Docutron::Response.new(method, resource)
response.send(client, data.channel)
end end end
</code></pre></div></div>
<p>Docutron::App.instance.run
{% endhighlight %}</p>
<p>We match against the incoming message using the SlackRubyBot’s <code class="highlighter-rouge">.match</code> method.
We’re looking for a message in the form of <code class="highlighter-rouge">[request method] [resource name]</code>,
For instance:</p>
<blockquote>
<p>GET person</p>
</blockquote>
<p>When a message is received, we create a new <code class="highlighter-rouge">Docutron::Response</code> instance and pass
it the request method and the resource. We then call <code class="highlighter-rouge">#send</code> to respond in the
Slack channel the message was sent from.</p>
<h2 id="docutronresponserb">docutron/response.rb</h2>
<p>This is where we’ll do the bulk of the work of loading and parsing the schema,
choosing the correct schema link for the requested resource, then returning the
appropriate response.</p>
<p>{% highlight ruby linenos=table %}
require ‘prmd’</p>
<p>module Docutron
class Response
UnknownResponse = “Sorry, I don’t know about that resource.”.freeze</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def initialize(method, resource)
@method = method.upcase
@resource = resource
@schemata = "#/definitions/#{@resource}"
@schema = Prmd::Schema.new(JSON.parse(File.read('schema.json')))
end
def link
@schema['definitions'][@resource]['links'].detect do |link|
link['method'] == @method
end or raise UnknownResponse
end
def json_example
if link['rel'] == 'empty'
elsif link.has_key?('targetSchema')
JSON.pretty_generate(@schema.schema_example(link['targetSchema']))
elsif link['rel'] == 'instances'
JSON.pretty_generate([@schema.schemata_example(@schemata)])
else
JSON.pretty_generate(@schema.schemata_example(@schemata))
end
end
def message
"```#{json_example}```"
end
def send(client, channel)
client.message text: message, channel: channel
end end end {% endhighlight %}
</code></pre></div></div>
<p>The initializer of the <code class="highlighter-rouge">Docutron::Response</code> class sets up some instance variables
and creates a new <code class="highlighter-rouge">Prmd::Schema</code> instance using the schema.json data.</p>
<p>The <code class="highlighter-rouge">#link</code> method finds the schema’s link definition for the resource and the request
method of the incoming Slack message. Our basic person schema defines one link:</p>
<p>{% highlight json %}
{
“title”: “Person details”,
“description”: “Get the details of a person”,
“method”: “GET”,
“href”: “/person/{#/definitions/identity}”,
“targetSchema”: {
“$ref”: “#/definitions/person”
}
}
{% endhighlight %}</p>
<p>The <code class="highlighter-rouge">#json_example</code> method uses the link to generate a JSON example either using
the <code class="highlighter-rouge">targetSchema</code> of the link if it exists, or by using a default json
reference for the resource, in this case <code class="highlighter-rouge">#/definitions/person</code>. If the link has
a rel of “instances”, it wraps the response in an array. This
method is adapted from <a href="https://github.com/interagent/prmd/blob/master/lib/prmd/templates/schemata/link.md.erb">Prmd’s link.md.erb
template</a>.</p>
<p>The important bit here is the <code class="highlighter-rouge">@schema.schemata_example(@schemata)</code> which
returns a JSON object based on the properties defined for a given “schemata” and
the example values defined in the schema. For person it looks like this:</p>
<p>{% highlight json %}
{
“id”: “1dc3567e-acd4-4819-afd5-21d0ef677dcd”,
“first_name”: “Jean-Luc”,
“last_name”: “Picard”,
“email_address”: “locutus@borg.hive”
}
{% endhighlight %}</p>
<p>Finally, the <code class="highlighter-rouge">#message</code> method wraps the JSON example in triple back ticks so
that Slack will format the message as preformatted text. The <code class="highlighter-rouge">#send</code> method as
you recall is what our Slack bot actually calls to send the message.</p>
<h2 id="configure-slack">Configure Slack</h2>
<p>The final step to run this basic Slack bot is to configure your team’s
integrations. First create a new bot by going to the <a href="https://slack.com/services/new/bot">Add Bot</a>
page. Create a new bot and obtain the bot’s API token. You’ll need this to start the bot.
Next, from the <a href="https://slack.com/services/new/outgoing-webhook">Add Outgoing Webhooks</a>
page create a new outgoing webhook and choose a specific
channel for your bot to monitor.</p>
<p>To run the bot use the command:</p>
<p>{% highlight text %}
SLACK_API_TOKEN=bot_api_token_here ruby docutron.rb
{% endhighlight %}</p>
<p><img src="/images/posts/slackbot-docutron.gif" alt="Slackbot: Docutron" class="console-image" /></p>
<h2 id="just-the-beginning">Just the beginning</h2>
<p>For the documentation to be truly useful, you’ll of course want more
information. Maybe some details about each property, for example.
Prmd has templates to handle generating that which could be
adapted for docutron, but I leave that as an exercise for the reader.</p>
<p>Happy Slacking!</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="http://github.com/mlr/docutron">Docutron source code</a></li>
<li><a href="http://spacetelescope.github.io/understanding-json-schema/">Understanding JSON Schema</a></li>
<li><a href="https://github.com/dblock/slack-ruby-bot">slack-ruby-bot</a></li>
<li><a href="https://github.com/interagent/prmd">Prmd</a></li>
</ul>
The number of Slack bots, Slack plug-ins and other integrations availablerecently seems to have skyrocketed. There are plug-ins to help facilitate liveblogging, bots thatcan book rooms for your team (hopefully better thanyour company’s travel department), there’s even a Slack bot that willlisten to all of your frustrations.Vim plugins lost in the Vundle2015-07-12T00:00:00-07:002015-07-12T00:00:00-07:00https://ronniemlr.com/2015/07/12/vim-plugins-lost-in-the-vundle<p>Once in a while I try to audit my vim configuration, plugins, etc. Although I try
to be vigilant against it, I inevitably end up with plugins that go unused for
months and cause some minor wtf moments when I see them later and can’t remember
what they’re for.</p>
<p>Even worse: it’s a vicious, time consuming cycle. I find myself having a
hard time parting from some of these plugins after I rediscover their purpose.
So I end up refreshing my knowledge of their mappings, using them for a few
days, and forgetting about them again because for whatever reason they just aren’t
sticking as part of my workflow.</p>
<p>In an effort to document some this discovery and once and for all expunge
some of these plugins from my Vundle, I decided it’s worth writing up a quick
review of a few plugins I have tried, but just haven’t stuck.</p>
<h2 id="vundle-organization">Vundle organization</h2>
<p>As you’ve probably inferred, I currently use
<a href="https://github.com/VundleVim/Vundle.vim"><em>Vundle</em></a> to manage my vim bundle.
I’ve considered trying <a href="https://github.com/tpope/vim-pathogen">Tim Pope’s <em>Pathogen</em></a>,
but that’s a topic for another post. Anyway, my
<a href="https://github.com/mlr/dotfiles/blob/master/vimrc.bundles"><code class="highlighter-rouge">.vimrc.bundles</code></a>
file looks like this:</p>
<p>{% highlight vim linenos=table %}
set nocompatible
filetype off</p>
<p>set rtp+=~/.vim/bundle/vundle/
call vundle#rc()</p>
<p>” Let Vundle manage Vundle
“ Define bundles via Github repos
Bundle ‘gmarik/vundle’</p>
<p>” Core enhancements
Bundle ‘chriskempson/base16-vim’
Bundle ‘danro/rename.vim’
Bundle ‘itchyny/lightline.vim’
Bundle ‘vim-scripts/ShowTrailingWhitespace’
Bundle ‘vim-scripts/DeleteTrailingWhitespace’
Bundle ‘tpope/vim-unimpaired’</p>
<p>” IDE-like enhancements
Bundle ‘scrooloose/nerdtree’
Bundle ‘scrooloose/syntastic’
Bundle ‘kien/ctrlp.vim’
Bundle ‘rking/ag.vim’
Bundle ‘nathanaelkane/vim-indent-guides’
Bundle ‘tpope/vim-fugitive’
Bundle ‘gregsexton/gitv’
Bundle ‘benmills/vimux’
Bundle ‘jgdavey/vim-turbux’</p>
<p>” Code editing enhancements
Bundle ‘tmhedberg/vim-matchit’
Bundle ‘tpope/vim-endwise’
Bundle ‘tpope/vim-surround’
Bundle ‘vim-scripts/tComment’
Bundle ‘godlygeek/tabular’
Bundle ‘goldfeld/vim-seek’
Bundle ‘PeterRincker/vim-argumentative’
Bundle ‘tommcdo/vim-exchange.git’
Bundle ‘terryma/vim-expand-region’
Bundle ‘kana/vim-textobj-user’
Bundle ‘nelstrom/vim-textobj-rubyblock’
Bundle ‘jgdavey/vim-blockle’</p>
<p>” File type handlers
Bundle ‘xenoterracide/html.vim’
Bundle ‘vim-ruby/vim-ruby’
Bundle ‘tpope/vim-bundler’
Bundle ‘tpope/vim-rails’
Bundle ‘tpope/vim-haml’
Bundle ‘tpope/vim-markdown’
Bundle ‘mustache/vim-mustache-handlebars’
Bundle ‘kchmck/vim-coffee-script’
Bundle ‘slim-template/vim-slim.git’</p>
<p>” Snippets
Bundle ‘MarcWeber/vim-addon-mw-utils’
Bundle ‘tomtom/tlib_vim’
Bundle ‘garbas/vim-snipmate’</p>
<p>filetype on
{% endhighlight %}</p>
<p>As you can see it’s organized into 5 semi-defined sections:</p>
<ol>
<li>Core enhancements</li>
<li>IDE-like enhancements</li>
<li>Code editing enhancements</li>
<li>File type handlers</li>
<li>Snippets</li>
</ol>
<p>Sections 1, 4 and 5 tend to be fairly unchanging. Sections 2 and 3 are where the
most churn tends to happens. There’s four plugins in particular that I wanted to
review for removal:</p>
<p>{% highlight vim %}
Bundle ‘goldfeld/vim-seek’
Bundle ‘PeterRincker/vim-argumentative’
Bundle ‘tommcdo/vim-exchange.git’
Bundle ‘terryma/vim-expand-region’
{% endhighlight %}</p>
<h2 id="vim-seek">Vim seek</h2>
<p><a href="https://github.com/goldfeld/vim-seek">https://github.com/goldfeld/vim-seek</a></p>
<blockquote>
<p>Seek makes navigating long lines effortless, acting like f but taking two characters.</p>
</blockquote>
<p><img src="/images/posts/vim-bundle-vim-seek.png" alt="Vim seek install date" class="console-image" /></p>
<p>I remember installing vim-seek because my habit when writing code tends to be to
use the <code class="highlighter-rouge">f</code> character to motion jump around to the part of the line I need to change.
The problem comes when the line has more than one of a given character, which tends
to happen more often than not. Vim seek maps the <code class="highlighter-rouge">s</code> character to behave similar
to find, but takes two characters then jumps to the first instance of
those two characters.</p>
<p>This works great, but I found myself forgetting the key mapping was there.
Furthermore, I simply adjusted my habit from tending to use the <code class="highlighter-rouge">f</code> key to
invoke find to using the <code class="highlighter-rouge">/</code> key to invoke a pattern search and typing a couple
characters to search within the whole file.</p>
<p>Since the search is made relative to the current cursor position it
usually works well. This also has the added bonus of not needing to be
on the same line as the search term and I can use <code class="highlighter-rouge">?</code> (<code class="highlighter-rouge">shift+/</code>) similar
to <code class="highlighter-rouge">F</code> to search backward. Now I tend to just type <code class="highlighter-rouge">/</code> or <code class="highlighter-rouge">?</code> and begin
typing the code I’m trying to jump to.</p>
<p><strong>Conclusion: removed from bundle</strong></p>
<h2 id="argumentativevim">argumentative.vim</h2>
<p><a href="https://github.com/PeterRincker/vim-argumentative">https://github.com/PeterRincker/vim-argumentative</a></p>
<blockquote>
<p>Argumentative aids with manipulating and moving between function arguments.</p>
</blockquote>
<p>As can be seen in the example below, argumentative lets you easily swap the
order of method arguments. Argumentative defines the <code class="highlighter-rouge">&gt;,</code> and <code class="highlighter-rouge">&lt;,</code> mappings. You
simply place the cursor on one argument and use the former to swap it right and
the latter to swap it left.</p>
<p><img src="/images/posts/vim-bundle-vim-argumentative.gif" alt="Vim argumentative usage" class="console-image" /></p>
<p>In addition, it defines two new text objects <code class="highlighter-rouge">a,</code> and <code class="highlighter-rouge">i,</code> which allows you to
further manipulate function arguments. For instance <code class="highlighter-rouge">vi,</code> and <code class="highlighter-rouge">va,</code> can be used to
make a visual selection inside the method argument under the cursor or select around it, respectively.</p>
<p>Although I still love the idea of this plugin, I have not noticed myself using
it after having installed it over a year ago. Swapping the order of arguments is
just not something I do often enough to develop a muscle memory for the
key mappings.</p>
<p><strong>Conclusion: removed from bundle</strong></p>
<h2 id="exchangevim">exchange.vim</h2>
<p><a href="https://github.com/tommcdo/vim-exchange">https://github.com/tommcdo/vim-exchange</a></p>
<blockquote>
<p>Easy text exchange operator for Vim</p>
</blockquote>
<p>This plugin allows you to quickly swap two lines or two large regions of text.
Vimcasts provides an excellent cover of exchange.vim’s features in their vimcast
<a href="http://vimcasts.org/episodes/swapping-two-regions-of-text-with-exchange-vim/"><em>Swapping two regions of text with
exchange.vim</em></a>.</p>
<p>Similar to argumentative.vim, this is a plugin I must have installed figuring I
would use it a lot. Even as of writing this it still feels like this is something
I do often enough to warrant custom key mappings for.</p>
<p>In practice though, I tend to just visually select and cut a chunk of code then
use motion keys to jump to the position I want it to be, then paste. I rarely
entirely swap two lines or pieces of code with one another.</p>
<p><strong>Conclusion: removed from bundle</strong></p>
<h2 id="vim-expand-region">Vim expand region</h2>
<p><a href="https://github.com/terryma/vim-expand-region">https://github.com/terryma/vim-expand-region</a></p>
<blockquote>
<p>Vim plugin that allows you to visually select increasingly larger regions of
text using the same key combination.</p>
</blockquote>
<p><img src="/images/posts/vim-bundle-vim-expand-region.gif" alt="Vim expand region usage" class="console-image" /></p>
<p>Using the <code class="highlighter-rouge">+</code> and <code class="highlighter-rouge">_</code> key mappings provided by vim-expand-region, you can easily
grow and shrink a visual selection as seen above.</p>
<p>Since I don’t typically visually select some piece of text before operating on
it, I don’t need to modify my selection very often. I tend to just yank or change
inside of whatever text object I’m in. For example <code class="highlighter-rouge">yi(</code> or <code class="highlighter-rouge">ci[</code>.</p>
<p>I do frequently select entire blocks of code, for instance an if statement to
indent further, but in that case I typically place my cursor on the <code class="highlighter-rouge">if</code> or the
<code class="highlighter-rouge">end</code> and do <code class="highlighter-rouge">V%</code> to select to the entire block. The <code class="highlighter-rouge">%</code> mapping comes from the
excellent <a href="https://github.com/tmhedberg/matchit">vim-matchit</a> plugin.</p>
<p><strong>Conclusion: removed from bundle</strong></p>
<h2 id="rip-plugins">RIP plugins</h2>
<p>I ended up removing all four plugins I reviewed today. I think they provide great
functionality, but in the end I used them less times than I can count on one hand
and they had been installed for over a year on average.</p>
<p>On a positive note, I can now look to this write-up if I stumble upon any of
these plugins in the future and want to give any of them another try.</p>
Once in a while I try to audit my vim configuration, plugins, etc. Although I tryto be vigilant against it, I inevitably end up with plugins that go unused formonths and cause some minor wtf moments when I see them later and can’t rememberwhat they’re for.RailsConf 2015, Atlanta Georgia2015-04-24T00:00:00-07:002015-04-24T00:00:00-07:00https://ronniemlr.com/2015/04/24/rails-conf-2015<p>RailsConf 2015 was hosted in the towering downtown Atlanta, Georgia.
I was grateful to have been sent to attend this year thanks to my
<a href="http://crowdcompass.com">rather awesome employer</a>.</p>
<p><img src="/images/posts/rails-conf-2015.jpg" alt="Atlanta, Georgia" /></p>
<p>Rails 5 was a large topic of discussion during DHH’s opening keynote.
A couple of the big feature announcements were Turbolinks 3 and Action Cable.
Despite my tongue-in-cheek post title, I’m actually looking forward to trying
both of these features.</p>
<p>Back when I built <a href="http://www.workstiming.com">WorksTiming</a> I ended up keeping
Turbolinks enabled. For the mostly-static dashboards it uses on the backend, the
standard functionality provided by Rails 4.x works pretty great; the only caveat
being of course the DOM on ready listener issue which actually
<a href="http://stackoverflow.com/a/18770589">isn’t that difficult to fix</a>.</p>
<p>I also settled on using <a href="http://pusher.com">Pusher</a> to provide live updating
features using websockets. I’ve been on the lookout for a better alternative
since, with some <a href="https://github.com/websocket-rails/websocket-rails">promising possibilities</a>,
but having a websocket story integrated into the core of Rails is a welcome
addition.</p>
<p>Another great announcement in Rails 5 is the integration of the Rails API work
that’s been happening to make a slimmer starting point for building API
only or JS heavy applications. Read more about this decision direct from
one who helped make it happen: <a href="http://wyeworks.com/blog/2015/4/20/rails-api-is-going-to-be-included-in-rails-5">Santiago Pastorino: Rails API to be part of
Rails 5</a>.</p>
<p>All in all I had a great time this week at the conference and look forward to
following the development of Rails 5. Check out the <a href="https://www.youtube.com/watch?v=oMlX9i9Icno">keynote
accouncements</a>
on Confreaks’ YouTube channel if you missed it.</p>
RailsConf 2015 was hosted in the towering downtown Atlanta, Georgia.I was grateful to have been sent to attend this year thanks to myrather awesome employer.Trying out new features of Ruby 2.22014-12-25T00:00:00-08:002014-12-25T00:00:00-08:00https://ronniemlr.com/2014/12/25/trying-out-new-features-of-ruby-2.2<p>The Ruby core team shipped an awesome new version –
<a href="https://www.ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released">Ruby 2.2.0</a>
– on Christmas day. What an awesome gift! Notable enhancements include
both incremental garbage and symbol garbage collection.</p>
<p>Both of these together should provide noticeable improvements to memory usage
and will allow the Rails core team to “<a href="http://weblog.rubyonrails.org/2014/8/20/Rails-4-2-beta1/#maintenance-consequences-and-rails-5-0">shed a lot of weight</a>”
with regards to user input and how strings are handled.</p>
<p>Let’s try out a couple of the new features, namely <code class="highlighter-rouge">Enumerable#slice_after</code> and
<code class="highlighter-rouge">Enumerable#slice_when</code></p>
<h2 id="install-ruby-220">Install Ruby 2.2.0</h2>
<p>I really enjoy <a href="https://github.com/postmodern/ruby-install">ruby-install</a>, so
that’s how I installed the new version on my system. If you’re not already using
it, I highly recommend it.</p>
<p>{% highlight bash %}
ruby-install -u http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.0.tar.bz2 ruby 2.2
{% endhighlight %}</p>
<p>Once that’s done downloading and compiling, I simply use
<a href="https://github.com/postmodern/chruby">chruby</a> to switch to the new version.</p>
<p>{% highlight bash %}
chruby 2.2
{% endhighlight %}</p>
<h2 id="enumerableslice_after">Enumerable#slice_after</h2>
<p><code class="highlighter-rouge">#slice_after</code> is a counterpart to the already existing <code class="highlighter-rouge">#slice_before</code></p>
<p>This method lets you split an enumerator with each item being grouped into a new
chunk when the result of the block is true. So in the example below, you can
see a new chunk is created after each odd number.</p>
<p>{% highlight ruby %}
[0,2,4,1,2,4,5,3,1,4,2].slice_after(&amp;:odd?).to_a</p>
<h1 id="-0-2-4-1-2-4-5-3-1-4-2">=&gt; [[0, 2, 4, 1], [2, 4, 5], [3], [1], [4, 2]]</h1>
<p>{% endhighlight %}</p>
<h2 id="enumerableslice_when">Enumerable#slice_when</h2>
<p>This method allows you to slice the enumerable by comparing adjacent elements.
When the block is true a new chunk is created. Say you have an array of numbers
and you want to list them where subsequent numbers are grouped into ranges,
like “1, 5, 9-12, 15” for example.</p>
<p>{% highlight ruby %}
numbers = [1, 5, 9, 10, 11, 12, 15]
grouped = numbers.slice_when { |i, j| i+1 != j }
p grouped.to_a</p>
<h1 id="-1-5-9-10-11-12-15">=&gt; [[1], [5], [9, 10, 11, 12], [15]]</h1>
<p>ranges = grouped.map { |a| a.length &lt; 3 ? a : “#{a.first}-#{a.last}” }
p ranges</p>
<h1 id="-1-5-9-12-15">=&gt; [[1], [5], “9-12”, [15]]</h1>
<p>p ranges.join(“, “)</p>
<h1 id="-1-5-9-12-15-1">=&gt; “1, 5, 9-12, 15”</h1>
<p>{% endhighlight %}</p>
<p>Let me know what you like about this release. Go forth and be productive!</p>
<h2 id="sources-and-info">Sources and Info</h2>
<ul>
<li><a href="https://www.ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released/">Ruby 2.2 release announcement</a></li>
<li>Enumerable#slice_after –
<a href="http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-slice_after">documentation</a>,
<a href="https://bugs.ruby-lang.org/issues/9071">discussion</a></li>
<li>Enumerable#slice_when –
<a href="http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-slice_when">documentation</a>,
<a href="https://bugs.ruby-lang.org/issues/9826">discussion</a></li>
</ul>
The Ruby core team shipped an awesome new version –Ruby 2.2.0– on Christmas day. What an awesome gift! Notable enhancements includeboth incremental garbage and symbol garbage collection.