tag:blogger.com,1999:blog-43203483525727686592024-03-21T07:49:43.987-07:00Riding Ruby on RailsA blog about the problems I've encountered while being a Ruby on Rails developer, and how I've solved them.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.comBlogger26125tag:blogger.com,1999:blog-4320348352572768659.post-49978215659323790452015-04-23T07:00:00.000-07:002015-04-23T07:00:02.844-07:00How to Link a Git Folder with an Existing Heroku App<br />
<ol>
<li>Follow <a href="https://devcenter.heroku.com/articles/heroku-command" target="_blank">this guide</a> to get Heroku setup on your command line tool of choice.</li>
<li><code>heroku git:remote -a project_name</code></li>
</ol>
<br />
where project name is the name of your Heroku app (if your app's URL is <a href="http://stellablog.herokuapp.com/" target="_blank">stellablog.herokuapp.com</a>, project_name would be stellablog) and you're done! Your local Git folder will be linked with your Heroku app, and you can deploy your latest code to it.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-15609924587662306842015-04-20T07:00:00.000-07:002015-04-20T07:00:05.262-07:00How to Push a Local Branch to a Separate Remote BranchThis is my first post about Git! I'll be talking about how to push the contents of a local branch to a remote branch that is different from what it currently points to. The base syntax is<br />
<br />
<code> git push remote local_branch:remote_branch </code>
<br />
<br />
An example of when to use this is in combination with Heroku. You might want to push your local branch to Heroku's master branch:<br />
<br />
<code> git push heroku foo_branch:master</code><br />
<br />
That is also how you make Heroku run a non-master git branch.
<br />
<br />
Maybe you want to push the contents of your current branch to another branch on git:<br />
<br />
<code> git push origin foo_branch:bar_branch <code></code></code>Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-79787874288725550802015-04-16T07:00:00.000-07:002015-04-16T07:00:04.645-07:00How to use respond_to? with private methods<code>respond_to?</code> is an awesome method for testing if an object has a given method. By default, it will return false for methods that are private. If you want to test if an object has a method regardless of its private status, you can just pass true after the method name:
<br>
<br>
<code>respond_to?(:foo, true)</code>Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-27439832541559567692015-04-13T07:00:00.000-07:002015-04-13T07:00:06.360-07:00How to Load Dependencies in a Rails EngineIn a Rails project, generally all you have to do is add a gem to your Gemfile and the dependencies will automagically get taken care of. Unfortunately, for Rails Engines this is not the case. You will have to take the additional step of requiring the gem's library file.
<br />
<br />
<code>
# engine.gemspec
s.add_dependency 'jquery-rails'
s.add_dependency 'your-foo-gem'
# lib/your_engine/engine.rb
<br />
require 'jquery-rails'
<br />
require 'your-foo-gem'
</code>
<br />
<code><br /></code>
This is related to the problem of <a href="http://railsproblems.blogspot.com/2015/03/undefined-method-faicon-for-class.html" target="_blank">needing</a> to require modules from gems to get them to load properly.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-45945987062293411942015-04-09T07:00:00.000-07:002015-04-09T07:00:06.776-07:00How to specify a local gem in your GemfileIf you're developing a gem and you want to test it out locally in another project, this is easily accomplished by specifying the path to the local gem in your Gemfile:
<br/ >
<br/ >
<code>gem 'bears', path: '/path/to/bears/gem'</code>
<br/ >
<br/ >
and boom, it's there. One <code>bundle install</code> later and you'll be good to go.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-61467460727352530742015-04-06T07:00:00.000-07:002015-04-06T07:00:02.574-07:00Removing extra whitespace from HamlMost of the time, Haml is pretty awesome. One of the cases where it's not so awesome is around multiline mixed Ruby/HTML. For example, when trying to create a timer on the screen:<br />
<br />
<script src="https://gist.github.com/oniofchaos/24ebe23bcb44f4e7eca9.js"></script>
Because of reasons, Haml decides to add extra whitespace around each of the span tags. Sometimes this is desirable, but sometimes you want to remove the whitespace/extra space around Haml tags.
As usual, there is a quick fix, in this case the '>' character:
<br />
<br />
<script src="https://gist.github.com/oniofchaos/5ce25eeb4781c22f1591.js"></script>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-809626277645356602015-04-02T07:00:00.000-07:002015-04-02T07:00:00.583-07:00Capybara::Poltergeist::TimeoutError <pre style="background-color: #f7f7f7; border-radius: 3px; box-sizing: border-box; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11.8999996185303px; font-stretch: normal; line-height: 1.45; margin-bottom: 16px; overflow: auto; padding: 16px; word-wrap: normal;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11.8999996185303px; line-height: inherit; margin: 0px; max-width: initial; overflow: initial; padding: 0px; word-break: normal; word-wrap: normal;"><span style="box-sizing: border-box;">Timed out waiting for response to {"name":"visit","args":["<a class="linkclass" href="http://127.0.0.1:54242/" style="background: transparent; box-sizing: border-box; color: #4183c4; text-decoration: none;">http://127.0.0.1:54242/</a>"]}.
It's possible that this happened because something
took a very long time (for example a page load was slow).
If so, setting the Poltergeist :timeout
option to a higher value will help (see the docs for details). If increasing the timeout does not help, this is probably a bug in Poltergeist - please report it to the issue tracker. (Capybara::Poltergeist::TimeoutError)</span></code></pre>
While working with Capybara feature specs and Poltergeist, you may have encountered this error before. You also probably tried bumping up the timeout value to no avail. I found a surprisingly easy fix for this issue - update your Poltergeist gem and update PhantomJS to version 2.0! After I did these two things, most of these errors went away.<br />
<br />
If you are still having issues after that, read through this <a href="https://github.com/teampoltergeist/poltergeist/issues/375" target="_blank">issue</a> and see if a solution there helps.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-18777836043638043152015-03-26T07:00:00.000-07:002015-03-26T07:00:01.915-07:00Using local assets with save_and_open_page<a href="http://railsproblems.blogspot.com/2015/03/using-saveandopenpage-with-cucumber.html">Previously</a> I talked about how to use save_and_open_page with Cucumber. Unfortunately, when you do this it renders an unstyled page. If you add
<br>
<br>
Capybara.asset_host = 'http://localhost:3000'
<br>
<br>
to your spec_helper.rb or Capybara config file, save_and_open_page will use your local server's assets - making it much easier to diagnose issues.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-30255021185344706402015-03-23T07:00:00.000-07:002015-03-23T07:00:00.928-07:00undefined method `capture_haml' for RSpecIf you're using Haml in a Rails project, testing with RSpec, you might encounter the "undefined method 'capture_haml' for ..." error while trying to run your specs. I encountered this error in a view spec when trying to use Simple Form along with Haml. Fortunately, like many other obscure bugs there is a quick and dirty solution:
<script src="https://gist.github.com/oniofchaos/dd5008ea89ab4cf8305a.js"></script>
The init_haml_helpers section is necessary to prevent other errors from happening.
<br />
<br />
Note that all lines are required. If you drop the <code>init_haml_helpers</code> line, you'll get the following error:
<br />
<code>ActionView::Template::Error:
undefined method `capture_position=' for nil:NilClass</code>
<br />
and of course if you don't have any of this, you will get:
<br />
<code> ActionView::Template::Error:
undefined method `capture_haml' for #<RSpec::ExampleGroups::...</code>Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-11741514338221455442015-03-19T07:00:00.000-07:002015-03-19T07:00:02.384-07:00Using save_and_open_page with CucumberCapybara has a nice utility method called save_and_open_page that will save the current page's HTML as a temp file and open it in your browser. This method is available by default in Ruby code (for example in an RSpec feature spec). As Cucumber is not direct Ruby, you can't directly say that. Fortunately, you can say
<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> Then show me the page
</code></pre>
<br />
and you'll get the same effect.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-48087183084387372752015-03-18T23:03:00.000-07:002015-03-19T19:38:30.199-07:00How to Validate Regular Expressions in Ruby on Rails ModelsActiveRecord has many awesome validators for common things like presence, numericality, etc. Unfortunately, it is missing one common format - regex, or regular expressions. These are normally saved as string fields and, if not validated, can lead to page errors when your app tries to use a field that contains an invalid regular expression. This is especially the case if you allow the user to input a regular expression. With the addition of one small validation class, you will be able to prevent these sorts of bugs with your models.
<br />
<br />
<script src="https://gist.github.com/oniofchaos/b78c5a2c8d6c2a2789fe.js"></script>
The above code adds a validator called RegularExpressionValidator to app/validators/regular_expression_validator.rb. It then adds the validation to the Bear model on the field my_regex_field. With this, if you try and create a Bear with an invalid regular expression, an error will be thrown and the save will not complete.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com1tag:blogger.com,1999:blog-4320348352572768659.post-26515323417287762222015-03-16T07:00:00.000-07:002015-03-18T14:14:08.273-07:00undefined method `fa_icon` for #<#Class:...If you're trying to use the <a href="http://fortawesome.github.io/Font-Awesome/" target="_blank">Font-Awesome</a> gem in your Rails engine, you may run into this "undefined method `fa_icon for #<#Class:.." error. Fortunately, there is a pretty simple fix.<br />
<br />
Add<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> include FontAwesome::Rails::IconHelper
</code></pre>
<br />
to<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> engine_name/app/helpers/engine_name/application_helper.rb
</code></pre>
<br />
and the error should go away.<br />
<br />
Thanks to <a href="https://github.com/bokmann/font-awesome-rails/issues/86" target="_blank">Github</a> for the solution.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-52954682948123221092015-03-12T07:00:00.000-07:002015-03-12T07:00:05.304-07:00How to Kill an "open" Rails ServerIf you've force-closed your Rails server instance for whatever reason (like a bug that froze up the server), you might run into issues when you try and restart it. For example, you might get the error "WARN TCPServer Error: Address already in use - bind(2) Exiting" What do you do?<br />
<br />
Assuming that you're running on port 3000, find the PID of the process with<br />
<br />
lsof -wni tcp:3000<br />
<br />
and kill the process with<br />
<br />
kill -9 PIDDillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-47187239142124116452015-03-09T07:30:00.000-07:002015-03-09T07:30:04.378-07:00Testing with a Dummy Controller in a Rails EngineIf you have a controller in your dummy app (spec/dummy/app/controllers/dummy_controller.rb) for your Rails engine, you might need to access the list of routes within your spec code. You can do this, surprisingly, with Rails.application.routes.routes!Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-79324940627333268622015-03-05T07:00:00.000-08:002015-03-05T07:00:00.798-08:00Using Bundler in a Rake TaskIf you want to use Bundler in a rake task to automate something like bundle install, you can't simply <a href="https://github.com/bundler/bundler/issues/925" target="_blank">call</a> bundle install in a system call. Fortunately, Bundler provides a method called Bundler.with_clean_env. Using that, you can do something like
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> require 'bundler'
Bundler.with_clean_env
system 'bundle install'
end
</code></pre>
to run your task.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-41194629379611292092015-03-02T07:00:00.000-08:002015-03-14T21:22:54.586-07:00AssetFilteredError: Assets filtered out and will not be servedIf you've decided to use the <a href="https://github.com/modeset/teaspoon" target="_blank">Teaspoon</a> gem for running your JS tests, you may have encountered this error when trying to navigate to your test suite:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> Error: Sprockets::Rails::Helper::AssetFilteredError: Asset filtered out and will not be served:
add `Rails.application.config.assets.precompile += %w( teaspoon.css )` to `config/initializers/assets.rb` and restart your server
</code></pre>
Unfortunately, this is an <a href="https://github.com/modeset/teaspoon/issues/197" target="_blank">issue </a>with Teaspoon and newer (>4.0) versions of Rails. For some reason, the asset paths for Teaspoon are no longer included in the list of assets that are automatically precompiled. Therefore, you must add them to your config/initializers/assets.rb file like so:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> unless Rails.env.production?
Rails.application.config.assets.precompile += %w(
teaspoon.css
teaspoon-teaspoon.js
teaspoon-mocha.js
# other assets
)
</code></pre>
Note: I added a surrounding check for production. Since Teaspoon is a testing gem, you shouldn't be using it in production, nor should you need its assets. If you have assets in this file that you need for production, you can separately add them with another addition to Rails.application.config.assets.precompile.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-44042633627940967482015-02-27T07:00:00.000-08:002015-02-27T07:00:06.680-08:00Stubbing Global Methods in JS Specs with SinonIn your Rails app, your JS should be tested too! Setting that up is a topic for another post. When testing your JS, a lot of the same philosophies apply. One of those is mocking methods other than the one you're testing. How do you do this for methods in other libraries, such as vanilla JS or jQuery?
<br />
<br />
Vanilla JS (the confirm method):
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> sinon.stub(window, 'confirm')
</code></pre>
jQuery (the fadeOut method):
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> sinon.stub(jQuery.prototype, 'fadeOut')
</code></pre>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-43484855500429927942015-02-23T07:00:00.000-08:002015-02-23T07:00:02.263-08:00Cleaning out server assets in developmentDepending on your server settings (config/environments/development.rb), some of your assets (css, js, images, etc.) may end up being cached by the server and won't reflect your changes. Fortunately, there is a command to clear all of these out so they will be regenerated. With the server shut down, run rake assets:clobber and all of your asset caches will be removed. The next time you run the server and navigate to your app, the assets will be regenerated with the current version of the files.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-36663507165321451202015-02-19T07:00:00.000-08:002015-02-19T07:00:06.352-08:00Using self.included to Include DependenciesAs part of a project I'm working on, I created a mountable engine for extracting all authentication concerns for the app. I created a TestHelper module that includes methods for mocking various aspects required for authentication. These methods require Devise::TestHelpers to function properly, and my goal was for the calling app to not know or care about this dependency. To solve this, I used self.included to automatically include the helper when necessary (in my case, only in controller specs).<br />
Here is the code:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> def self.included(receiver)
if defined?(receiver.metadata) && receiver.metadata[:type] == :controller
receiver.send :include, Devise::TestHelpers
end
end
</code></pre>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-7983051252364488312015-02-15T12:27:00.000-08:002015-02-15T21:51:55.816-08:00Testing Multiple Attributes of an Input FieldSometimes in a view or feature spec, you want to test multiple attributes of an input field (or some other field). For example, you might want to test that the input field is a checkbox and is checked, as well as testing that the field has the id #rawr. This is how you would do that in a view spec (feature spec is similar):<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> expect(content).to have_css("input[type='checkbox'][checked='checked']#rawr")
</code></pre>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-82808958945605203162015-02-02T08:00:00.000-08:002015-02-15T21:51:14.869-08:00Updating a Rails Model with a Non-default Primary KeyIn my <a href="http://railsproblems.blogspot.com/2015/01/creating-rails-model-with-non-default.html" target="_blank">last post</a>, I talked about creating a Rails model with a non-default primary key. You can create new instances of the model with no problem, but what happens when you want to update an instance of the model?<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> TypeError: nil is not a symbol nor a string
</code></pre>
Oh no!!!!<br />
<br />
Fortunately, there is an easy fix. You just have to specify the column name of the primary key from within the model.<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> class BearHabitat < ActiveRecord::Base
self.primary_key = 'bear_id'
end
</code></pre>
Now you should be able to update your model within your code with no problem. Why does this happen in the first place? My guess is that somewhere within ActiveModel, it assumes that the primary_key field is set. Since your migration set id to false, this value gets defaulted to nil. If you don't manually set it, ActiveModel gets sad. Now you can make ActiveModel happy again.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-19796081135298174332015-01-31T08:00:00.000-08:002015-02-15T21:50:35.536-08:00Creating a Rails Model with a Non-default Primary KeyOne of the main Rails philosophies is "convention over configuration". This works great most of the time, but sometimes it's best to do something outside of convention. One example is the primary key for models. Sometimes you want the key to be something other than ID; maybe you want it to be the ID of a related model.<br />
<div>
<br /></div>
<div>
This is possible to set up in a migration, you just need to do a few important things:</div>
<div>
<ul>
<li>Set id: false on the create_table line</li>
<li>Set your new primary key to null: false</li>
<li>Add a unique index on your new primary key</li>
</ul>
<div>
Here is an example:</div>
</div>
<div>
<br /></div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> class CreateBearHabitats < ActiveRecord::Migration
def change
create_table :bear_habitats, id: false do |t| // prevents a default ID column from being created
t.belongs_to :bear, null: false //creates a bear_id column that can not be null
t.string :habitat
end
add_index :bear_habitats, :bear_id, unique: true // ensures that the bear_id column is unique
end
end
</code></pre>
This BearHabitat model will now use bear_id as a primary key instead of the standard id.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-67701879190736573362015-01-26T08:00:00.000-08:002015-02-15T21:48:57.573-08:00Testing Controllers in Rails EnginesTesting your controllers in a Rails engine requires just a few extra steps compared to testing in a Rails application.<br />
<div>
<br />
For example, you're working on an engine called Animals, and you have a BearsController with an index action that returns all the bears you've registered.<br />
<br /></div>
<div>
With a controller like this:</div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // app/controllers/animals/bears_controller.rb
module Animals
class BearsController < ApplicationController
def index
// return all bears
end
end
end
</code></pre>
<div>
and a routes file like this:</div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // config/routes.rb
Animals::Engine.routes.draw do
get '/index' => 'bears#index'
end
</code></pre>
<div>
you will need to update your dummy routes file to look like this:</div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // spec/dummy/config/routes.rb
Rails.application.routes.draw do
mount Animals::Engine => "/animals"
end
</code></pre>
<div>
and in your controller spec you will need to add one extra line compared to normal:</div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // spec/controllers/animals/bears_controller_spec.rb
RSpec.describe BearsController do
// this is the key line!
routes { Animals::Engine.routes }
describe 'GET :index' do
it 'does the thing' do
get :index
// test that all bears are returned
end
end
end
</code></pre>
<div>
and now your controller test will go to the appropriate routes :)</div>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-3678376106737951782015-01-19T07:00:00.000-08:002015-03-14T21:27:36.007-07:00Testing Loaded Devise Modules In Your ModelsSo you're using <a href="https://github.com/plataformatec/devise" target="_blank">Devise</a> or <a href="https://github.com/lynndylanhurley/devise_token_auth" target="_blank">devise_token_auth</a> for user authentication in your Rails project. Awesome! Both are great libraries for authentication. Now, how are you testing your code? You probably have feature tests to ensure tasks like creating a new user work (and if you don't, stop reading this right now and make them!). You might even have controller tests to ensure, for example, that each action authenticates the user before proceeding.<br />
<div>
<br /></div>
<div>
What about your models? They are important too, and fortunately these libraries handle most of the model work in the background for you. There are still things you can test though! The main thing that come to mind are ensuring that you only load the devise modules that you want. For devise_token_auth, you also want to ensure you load the model concern.</div>
<div>
<br /></div>
<div>
<h2>
Devise Modules</h2>
</div>
<div>
Let's say that you're only using the Database Authenticateable and Registerable modules. With Devise, your model might look something like this:<br />
<br /></div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> class User < ActiveRecord::Base
devise :database_authenticatable, :registerable
end
</code></pre>
<div>
<br />
Devise provides a method called devise_modules that returns an array of symbols representing the modules that you include. With this, you can easily test that you're using only these two modules by doing this:<br />
<br /></div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // spec/models/user_spec.rb
// using RSpec and FactoryGirl for testing
RSpec.describe User do
it 'has the right devise modules loaded' do
user = FactoryGirl.build(:user)
expect(user.devise_modules).to eq([:database_authenticatable, :registerable])
end
end </code></pre>
<div>
<br />
What if there was a business requirement that Omniauth was not available? Well, you can test that easily too:<br />
<br /></div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> it 'does not load the Omniauth module' do
user = FactoryGirl.build(:user)
expect(user.devise_modules.include?(:omniauthable)).to be_falsey
end
</code></pre>
<div>
<br />
<h2>
DeviseTokenAuth concern</h2>
</div>
<div>
With devise_token_auth, you also include a concern to add extra methods to User that are used internally. How can you test this? Easily!</div>
<div>
<br /></div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> it 'loads the DeviseTokenAuth concern' do
expect(User.ancestors.include?(DeviseTokenAuth::Concerns::User)).to be_truthy
end
</code></pre>
<div>
<br />
<h2>
Summary</h2>
</div>
<div>
With these tests, you can be sure that your model includes everything from Devise that you're expecting it too. This is great insurance for when you decide to refactor your model 6 months from now. It makes your assumptions clear, ensures that any changes are explicit, and allows you to test your user model is using Devise without having to run your expensive feature specs.</div>
Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0tag:blogger.com,1999:blog-4320348352572768659.post-28655601417357603422015-01-16T11:53:00.001-08:002015-02-15T21:46:58.033-08:00Rake Database Tasks Outside RailsMy first content post is, ironically, not directly about Rails. It is about using a very common Rails feature - rake database tasks - in a Ruby project that utilizes ActiveRecord but not Rails.<br />
<div>
<br /></div>
<div>
By default, tasks like rake db:migrate are available in Rails but not in a Ruby project that includes ActiveRecord. Fortunately, with a couple of lines added to your Rakefile this issue can be fixed.</div>
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoKIh52WiVap2dpcmlWOQLZlHB-NyK097492nCdJ2iewoqbl6bDxsC6OYssaQNC46w5KjVIJGvBUDd-Mbl46edIpA-FA8fism4Ehy29pe9qfcDCPLRZuq4odPdn2KgU2Cg443F6hyphenhyphenZz0Qr/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // Rakefile
DatabaseTasks.env = ENV['YOUR_ENV_VAR'] || 'development'
DatabaseTasks.db_dir = 'db'
DatabaseTasks.database_configuration = YAML.load_file('config/database.yml')
DatabaseTasks.migrations_paths = "#{DatabaseTasks.db_dir}/migrate"
task :environment do
ActiveRecord::Base.configurations = DatabaseTasks.database_configuration
ActiveRecord::Base.establish_connection DatabaseTasks.env.to_sym
end
load 'active_record/railties/databases.rake'
</code></pre>
And voila. Now, you can use create, migrate, and all of the other rake db tasks you're used to in a Rails project.Dillon Welchhttp://www.blogger.com/profile/00264009338084889894noreply@blogger.com0