Ignored By Dinosaurs 🦕

rails

Expository info (skippable)

So, for nigh 6-7 years now I've been a Rails enthusiast. I bought the PragProg AWDWR book when it was covering Rails 2.x and had the beach and hammock on the cover, and then proceeded to take years to figure out everything the book was actually talking about from the bottom of the stack to the top. I find it very enjoyable to be able to get my ideas out (in code), and Rails is still one of the cushiest frameworks around in terms of ease of use. It's almost as though it were a central tenet of it's philosophy...

For nigh 6 years now, I've been a Drupal professional. Drupal isn't the sexiest platform on the block, but it's been marketed in an absolutely genious manner and has seen incredible traction in sectors that have jobs-o-plenty — government, educational institutions, publishing, basically anything that needs a robust CMS. I do not, however, find it very enjoyable to work with. The aspects of Drupal that are the most “Drupalistic” – the Form API, render arrays, “configuration as convention” – just feel so out of sync with how the rest of the development world does things. However, see the above point about jobs-o-plenty, and there is still plenty of fun code to be written around the edges of a larger and more mature Drupal installation.

Drupal 8 is going to come out some day though, and with it will come the invalidation of pretty much everything the wider Drupal community has ever known about writing code for Drupal. It's going to require those of us who enjoy writing code to basically start from scratch with a new framework, only this framework is GIGANTIC and carries with it plenty of interesting architecture opinions and non-opinions alike from it's 12 year history. So I suspect I'm not alone in thinking “if I have to learn an entirely new thing anyway, will learning D8 be the most effective use of my limited time?”

—

So, given these factors, I've been watching Laravel for a long time, since V3. I haven't really started playing with it until this most recent major release – V5. My initial thoughts were “oh, this is basically PHP on Rails” – I mean this as a complement. But, since I already knew enough about Rails to be effective and have fun, why not spend the time learning Rails better? So that's what I did before the Drupal community at large took up “get off the island” as a mantra.

For the last few months though, I've spent a lot of time building small-ish things with Laravel, and have even had the team begin a Lumen-backed project. I've been going back through the old Laravel podcasts, since I think podcasts are a wonderful way to absorb the philosophy of whichever developer they have on. (Kudos to you Taylor for consistently being on the podcast about your framework. That actually says a lot about your dedication to moving this whole thing forward).

Ok, with that long winded exposition out of the way, a few thoughts...


The aforementioned ruminations

  • I like how Laravel is immediately familiar to someone who's worked with Rails down to the API of say, establishing relationships in Eloquent or the up and down methods on migrations. Almost any server-side framework that has come since Rails in any language has been either an embrace of or a reaction to its widely marketed “opinions”. I was listening to an older Laravel podcast where the topic was some haters in the PHP community (supposedly) accusing Laravel of being “too Rails-y” or something. I consider this a plus to the framework (obviously), and I think designing a framework around the opposite paradigm – trying to not make it too Rails-y – immediately builds walls around our various yards in the pan-linguistic developer community. If the rules and the terminology are similar, even if it's in a different programming language, we can all get up to speed with the new thing faster and stop wasting our time learning new concepts that are actually the exact same concepts by a new name.
  • I like how Laravel takes Rails' opinionated-ness and actually goes a step further. Rails does not have Auth built into the core. For some reason Rails has Active Job and Turbolinks and Coffeescript, but not Auth. I'd challenge anyone out there to find me a public Rails app without Auth. Building Auth into the framework is an obvious move, and I thank Laravel for doing so. Same goes for billing, or having a Redis cache driver, or having built in support for queued jobs. I was listening to a really old Laravel podcast last night, and Taylor basically described exactly the interface that now exists in L5 – a Billable trait that only uses Stripe and that's that. The kind of simplicity that can only come out of being opinionated. The kind of simplicity that Drupal can never have precisely because of it's lack of opinions in the architecture.
  • Though it's got its share of fans, I don't get a sense of religion off of Laravel (yet). There's a religion around Drupal, and I think religion is mostly a dangerous thing in that it encourages its followers to follow, but not to ask too many questions of its leaders. The relatively insane pace of development and major version bumps and refactoring of the file structure in these bumps is a double edged sword, but it kinda keeps everyone from getting too set in the “old ways” and offers to me a subconscious clue that Laravel is still casting about for the “righter” way to do things, that nothing is too sacred yet.
  • With that, I like that Laravel is finally offering a LTS release. This will allow me to actually sell it to my boss, and feel better about investing time in really learning the thing. I'm feeling more than a little burned by Angular (talk about learning a mountain of new jargon), but at least the whole Angular 2 flap is a good learning experience for evaluating a new technology. “Be skeptical of shiny new things.”

#laravel #rails

I've got this decoupled CMS brewing in my head, and wanted to jot down what I think would be a kind of cool method for generating URLs and URL redirects and making sure everything stays in sync without having to maintain a giant table of redirects (ala Drupal).

The basic scheme would look like this — site.com/{item_type}/{item_id}/{item_slug}. The type parameter could probably be optional, but it's there for now. An example URL could be ignoredbydinoaurs.com/posts/123/the-totally-awesome-seo-juice.

Of course, in whatever framework you're working in, those url segments are going to be broken down into tokens passed into whatever controller function is running the query. So your query would/could look like this in the controller


def show
	@post = Post.find(params[:id])
	if @post.slug != params[:slug]
	redirect_to "/#{@post.type}/#{post.id}/#{@post.slug}", :status => :moved_permanently
	end
end

This has the advantage of never going out of sync with a redirect table, and never opening up the possibility of having an alias and a redirect create a loop. This happens often in Drupal, so with this scheme, you're only looking up based on an item's ID, which should never change. If somehow a URL has made it out in the the wild that is not the preferred URL, nothing breaks, it just gracefully performs a redirect to the proper URL.

The only significant portion of the URL is the ID, everything else is decoration or SEO juice.


Somewhat off topic, but if you had a use cases where you were running multiple sites out of this CMS, and you had editors that frequently shared content, or wrote up each other's content for a sister site, then the primary key of of the article can stay consistent across different publications.

“How would different editors utilize each other's content in that case? Like, how would different pubs have the same article with a different summary on this instance from that instance?”

PostgreSQL schemas, that's how. I'll write that up, probably sometime in 2017.


2016, Django update

This wasn't exactly hard to do, but I was surprised to find that nobody really wrote up how to simply issue a redirect like this in a Django view (controller to the rest of the world). Assuming that get() or get_queryset() was the answer, but I was wrong. This was the help piece of info that I needed. Did I mention how much I love Django's docs?

This is the method that drives this very page.


class DetailView(generic.DetailView):
	model = Post
	
	def dispatch(self, request, *args, **kwargs):
	object = get_object_or_404(Post, pk=self.kwargs['pk'])
	if object.slug != self.kwargs['slug']:
	return redirect(object, permanent=True)
	# else, delegate up
	return super(DetailView, self).dispatch(request, *args, **kwargs)

#generaldevelopment #rails #django

Russian Doll caching

It's a well branded name for something that makes total sense after reading one blog post. The second half of this blog post will actually get you pretty much there.

If you're coming from a PHP/Drupal background like me, you might be surprised to find out that the database is not the bottleneck in Rails-land. Whereas on your typical Drupal page you might have anywhere from 50 to 1000 database queries being run, you'll be hard pressed to find a Rails app that comes anywhere near that number of DB calls being made. The Hobo companion, even on the front page (the heaviest page in terms of data so far) only runs about 8-10 queries.

What takes so long in Ruby land is view rendering.

I've witnessed this first hand on my job's Redmine install. I'd have thought that what was taking so long was the database, but what actually takes 95% of the time on each page load is the view rendering. I guess the Basecamp folks noticed the same things, so they went to work on how to speed that up.

Fragment caching

There's always a blurb in evey article about Rails caching that has to do with “fragment caching”. Basically, you cache little bits of each page as they're rendered, and the next page requests pull the rendered HTML from the cache to reassemble your page. It's simple, except that it's not. You've heard the old adage about the 2 hardest problems in computer science – cache invalidation is the PITA in this one. That basically means making sure (somehow) that you're not serving stale fragments when something has been updated in the meantime. I'm not sure what the old scheme was for taking care of this, but it wasn't friendly or intuitive.

Fragment caching ++

The solution that they came up with involved making a digest of the actual object being rendered be the cache key.


All blog posts
--------------

<% cache [ "archive", @posts_by_year.first.first ] do %>
  <% @posts_by_year.each do |year| %>
    <% cache [ "archive", year[1].size ] do %>
      ### <%= year[0] %>


      <% year[1].each do |post| %>
        <% cache [ "archive", post ] do %>
        
        #### <%= link_to post.title, post_date_path_helper(post) %> - <%= post.created_at.strftime('%D') %>



        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

This is the view that renders my blog's index page. I'm still getting the hang of how to name these cache fragments, but the idea is that you recursively wrap each rendered fragment on the page. If one item gets updated, the digest for that item changes, and the cache key for it changes as well. The next time the page is rendered, the value won't exist in the cache for that key (because the key has changed based on the digest of the object). It'll be stuck in the cache, and every fragment that wraps it will be invalidated as well. They'll be re-rendered, but rather than having to re-render everything on the page from scratch, the other items that haven't changed will be pulled from the cache. The vast majority of the page will not have changed, and will still be alive and well in the cache. In this way, the whole page can be 95% cached and only the parts that change will have to go through the whole trip to re-render.

It does still call your database to get the objects for digesting, but as we've already discussed, this is a small cost comparitively. Down the road there are solutions to this issue as well one your optimizations get to that point.

Cache store

When I first started implementing caching on this site, I started off easy with Rack Cache. It's a simple HTTP cache that causes the whole page to be stored with a TTL time on it. The TTL is set in the controller with something like expires_in 5.minutes, public: true. Once I started moving into the fragment caching business, I moved out of Rack Cache and into using memcached as the store. It's easy to set up. So easy I'll probably never write a post about it. It just works.

It did, however, seem to take up a fair share of memory – as you'd expect from a memory cache store. I already had Redis running for queuing background jobs via Sidekiq though, so it occurred to me over dishes that I should give that a try. Turns out it's just as easy as memcached. Just swap out gem 'dalli' in your Gemfile for gem 'redis-rails' and change config.cache_store = :dalli_store to config.cache_store = :redis_store. It seriously doesn't get any easier. Redis is a lot like memcached, except that it has some more advanced fatures that I might never use. It also writes to disk every now and then so if you restart your box, Redis can keep the cache warm rather than losing everything it's stored.

#rails #devops

I have a friend for whom I'm building a site right now, and I chose Rails to do so. I think I'll probably reach for Rails for most sites I build until I get bored of it, which isn't going to happen any time soon. I also learned a few things about different browser's implementations of HTML5 audio, which I'll get into first.

My buddy is in a band, and so part of the functionality of the site is a photo gallery, and another part is a music player. Whereas in the past I'd have just reached for something off the shelf like jPlayer, I decided to go the HTML5 route this time, as I was fairly confident that my buddy wouldn't be asking for legacy browser support. In any event, he's not paying for legacy browser support, so I decided to teach myself a few long overdue tricks.

First up is a bit of a primer on the HTML Audio element. I found, as usual, Mozilla to have the most understandable and trustworthy documentation – here, and here. Building the player was fairly straightforward, but required a bunch of repetitive code that I'm too embarrassed to post here. It's fairly simple to create an audio element, list out a bunch of song objects with recording attributes attached to them, set a data-source attribute on those songs that points to the path where your uploaded recording file lives, courtesy of the Carrierwave and jQuery FileUpload gems. When you click on one of the songs, it kicks off the player.play() method and your song plays.

The surprise was that Firefox, in which I work every day, doesn't like mp3 files. There's some contradictory info out there about whether or not FF does or doesn't support mp3, but my version does not, so I had to figure out how to get an ogg version of the file up there also.

The method I came up with was to install FFmpeg to do the conversion, but to place the conversion into a background Sidekiq job so it didn't hang up the browser when my buddy uploaded his song. Sidekiq makes this so absurdly easy, and the Railscast steps you right through the process. Basically any processing that you'd want to do in the create or update action in your controller can be moved into a Sidekiq worker that's called instead of doing the processing synchronously. Watch -

# songs#create

def create
  @song = Song.new(params[:song])
  @song.title = @song.default_name unless @song.title
  if @song.save
    ConversionWorker.perform_async(@song.id)
    #logger.debug path
    redirect_to music_path
  end
end

And the worker —

# app/workers/conversion_worker.rb

class ConversionWorker
  include Sidekiq::Worker

  def perform(song_id)
    song = Song.find song_id
    path = "#{Rails.root}/public#{song.recording_url}"
    ogg = path.gsub(/mp3$/, "ogg")
    yup = %x[ffmpeg -i #{path} -acodec libvorbis #{ogg}]
  end
end

Converting stuff with FFmpeg was really straightforward, but only in Ubuntu. I fought with trying to get it set up with libvorbis on the Mac and eventually gave up. %x[] is the easiest way I found to execute shell commands from Ruby, complete with string interpolation. Basically this says – load that song, give me the recording_url (convenient that these Carrierwave methods are in scope), and create an ogg version. Do that by putting it right next to the mp3 version, but with the ogg file extension.

#ruby #rails #devops

The short version -

https://github.com/JGrubb/laptop


The longer version -

Setting up your Macintosh for Rails development is actually sort of like Rail development itself – you have to at least kind of know about a lot of different things before you can really get anywhere. I'd say I'm new to Rails development even though I've been poking at it since 2.1. 2.1 is the first version I remember after I first bought a Mac and started trying to teach myself to “program”. It's taken me until 3.2 to come back to Rails with enough of a rounded web development skill set to really be able to fly around and build the things that have been locked up in my head for the last 4 years. I've written about this before, as have many others, but Rails isn't exactly a framework for beginners. That is, it requires you to have at least some idea of what you're trying to do before it will let you sit down and do it. Many of us probably grew up programming in Basic, but some of us might have gotten sidetracked into other vocations besides software development. the world has come quite a ways since then. So this post, or series maybe, will be an attempt to teach you not only what to install, but why.


Step 0 – the compiler

If you are on a Mac, you are lucky to already have a rather large lot of tools that software developers use already installed. But they haven't given you everything. Some of the ones that will come in handy down the line as you get further in to this process will have to be installed by you. This will toughen you up and help you get more acquainted with a side of your computer that perhaps you didn't even know about. The world (and by that I mean the guy who made up Homebrew) has made life a lot easier for the Mac-using developer, but in order to access all of those goodies, you'll need to install a compiler. A compiler is basically a piece of software that builds other software.

One of the first pieces of software that you're going to build will be the Ruby language itself, but first things first.

XCode

If you're on Lion or the latest Snow Leopard, you can open up the App Store and search for “XCode”. XCode is the official Apple development environment. With it you can build iPhone apps, applications for the Macintosh, and really pretty much anything that isn't specifically meant to be run on Windows. It's an enormous download, about 4GB recently.

After you download it, open it up, open the Preferences, and find the extra downloads part. What you're looking for is the “Command Line Tools” bundle. It's in one of the tabs toward the right. Download and install. Should be fairly simple. Just to be safe, restart your computer. Once it's back on open the Terminal.app (get used to the terminal, you'll be there a lot) and type which gcc. If it doesn't spit anything back at you, you haven't installed the compiler. Figure it out and come back for the next step – the package manager.

#rails #devops

updated Jan 2014 for Rails 4, see bottom


I just got this blog up and running yesterday, a marathon of pain but ultimately successful. So today I wanted to add some Markdown action so I didn't have to drop into TextMate to add <p> tags to everything all the time.

RedCarpet seemed to be the gem that was recommended, but it's recently seen a complete API overhaul that has rendered useless the vast majority of the de facto documentation out there. So, viewers of the Markdown Railscast, this one's for you.

It only involves a few changes, so if you're here it's likely that you know how to install it and you're here because of that “no 'new' method” error. Instead of

<%= Redcarpet.new(@post.body).to_html %>

as mentioned in the Railscast, you'll need to do a little more setting up. This is how I did it.

# app/helpers/application_helper.rb

def dat_markdown(text)
 markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
 :autolink => true, :space_after_headers => true, :no_intra_emphasis => true)
 markdown.render(text).html_safe
end

The first parameter is which renderer you want to use (either HTML or XHTML, for what?), and everything after that is your options glob which you can get from here. For readability you could also do -

# app/helpers/application_helper.rb

def dat_markdown(text)
 options = {
 :autolink => true,
 :space_after_headers => true,
 :no_intra_emphasis => true
 }
 markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
 markdown.render(text).html_safe
end

After that all you have to do is

<%= dat_markdown(@post.body) %>

and you're on your merry way. Enjoy.

January 2014 update

Rails 4 – check out Kramdown. This is how easy it is now.

# application_helper.rb

 def markdown_filter(text)
 Kramdown::Document.new(text).to_html.html_safe
 end

and <%= markdown_filter @post.body %>

Super bonus for the demographic reading this – syntax highlighting is included for free. See above. Docs for the options hash are here.

#rails

My first post on my new blog! I wrote this one all by myself, with the help of hundreds and hundreds of open source collaborators, Stack Overflow commenters, IRC, Google Groups, and sheer force of WILL!!!

This is my first running, production Ruby on Rails app. It took me about two days/6 hours to write, and about 4 days/30 hours to deploy. Deploy means “make work on the web so people can see it” and it was every bit the pain in the ass that I'd heard it was supposed to be. But by god, here it is! Nginx/Passenger/MySql/Rails. I'll make it look prettier later, and hopefully also have something to say.

Guess I also need to set it to something other than Greenwich mean time. It begins! Some more!

#rails #random

This is not for programmers. This is for myself, because when I first started poking at Rails 6 months ago, I didn't have any idea why I needed to edit a migration file, much less what a migration was, except that it must have something to do with a database. I only knew that because of the command

$>rake db:migrate

that I was told to perform and the fact that it had the letters “db” in it.

So here's the deal. This morning I talked about version control and it's place in the life cycle of your application. What version control does is keep track of the changes that you make to your applications source code from start to finish (whenever that is). The only problem with this is that a big chunk of the data and functionality of a modern web application doesn't reside in the app's source code, it resides in the database, and nobody has invented an efficient version control system for a database yet. Some friends of mine have tipped me off to different methods that they've used – mostly around writing down DB schema changes into a text file that then gets placed under version control with the rest of the source. That's not entirely efficient, though, is it?

Ruby on Rails has come up with a system called “migrations”. Actually, someone surely came up with this way before Rails was invented, but I'm new here, so feel free to correct me in the comments section down there. Let's say you create a new rails app (This is directly from the awesome book “Agile Web Development with Rails” by a bunch of smart folks, and this example is the beginning of building a shopping cart system) :

$>rails newapp

This generates a load of boilerplate code which makes up the bones of any Rails app. That means that instead of spending the first week of the development cycle sitting there writing low-level code that provides primordial programmatic structure, you can spend it writing code that actually does something that you (or your client) can interact with. Let's keep it as simple as possible and say that you are going to have three fields that will be stored in the database to start. Rails let's you run this scaffold generating command:

$>ruby script/generate scaffold product title:string description:text image_url:string

This command runs a ruby script that builds a LOT of code for you, so much that you're only minutes away from tinkering around and you only started a few minutes before that. This command creates the “products model”, which is how a programmer says that they are going to start by creating some place for the admin to store the products that they want to sell online. The fields that were initially created were for the name of the product, the description, and the URL where you're storing the picture of the product. Don't worry about it too much, but take a look at the command up there, it'll make sense if you know what a string is. Just know that the data entered into these fields is going to get stored in a database. Wait, how are we gonna store this stuff in our database? Have we even created the tables for the database yet? No. Rails does it for us. Here's how.

One of the files that is created when we run that scaffold generator is called a “migration file”. This migration file is what actually creates the tables in the database, the columns in the tables, and can even populate the tables with data (used for testing), depending on what you do to the migration file. There is a command (or method if you're OOP savvy) created when the scaffold is run called the “up method”. That's the one that changes your database. There is also a “down method”, which is what undoes the changes to your database. When you run the good old rake db:migrate command Rails goes applies the appropriate changes to your database for you, and provides a way to undo them later. So, your client comes in and wants to add a Price column to the store. In the old days, you'd execute this SQL statement :

ALTER TABLE products ADD column price DECIMAL (8,2) NOT NULL;

Now, what if you wanted to undo that? That's a command you ran there, not some source code that you can save and delete later if you change a few things. You can't place SQL statements under version control. You'd have to go and type that statement into the text file that was under version control, and then hope that you could effectively backtrack to it later if you wanted. If your application and it's database have come a ways since that command you're going to have a good time figuring out how to undo it. Rails doesn't make you do that. In fact, you almost never are going to talk directly to your database in Rails. Instead, you go to the most recent migration file (which is source code and thus under version control) and alter a line to look like this:

add_column :products, :price, :decimal,
  :precision => 8, :scale => 2, :default => 0

Then you run a script that alters the database for you. Below this add column instruction, or “up method”, you add the remove column instruction, or “down method”:

remove_column :products, :price

This is the undo button for your database and any schema changes along the way. Since this command is under version control, and since you're not directly interacting with your database, and since Rails knows how and which migrations to apply and in what order if you ever want to go back to a previous version, you've just made your life a LOT easier. Now if you only understood what the hell you were talking about!!

For a vastly better explanation, try here. Good luck.

#rails #general-development