RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

My first thought also.

Coming from .NET/Java this was the first concern I had with Rails. Where does business logic go? I was baffled to find out there's no standard way of doing that. Sorry, but putting it into model code always seemed like a hack to me.

Service layer has been the industry standard of coding complex business logic for quite a while now. This vid is great, but it just scratched the surface. Imagine dealing with trees of business logic or testing parts of it in complete isolation by stubbing out the context. Exceptions, transactions, logging all managed automagically using AOP. Sigh... those were the days :)

Next up - moving the instantiation and lifetime management of these service objects to config or DSL using some dependency injection framework.

Avatar

Hi einstyne,

I answered this question on Stackoverflow recently, here's how I did it.

Avatar

I see that Uncle Bob's talk about architecture in ruby apps is giving an impact on our community !

http://www.confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years

Avatar

You can just set reasonable defaults in your presenter class's method definitions

ruby
class UserPresenter < BasePresenter
  def some_method(size = 1)

  end
end
Avatar

On point (+1) reminds me of my java days.. soon we could be singing value objects!!

Avatar

Just wanted to contribute a bit of info, and mention the Teaspoon project.

I used Jasmine for a long time (in Rails and non-Rails projects), but when Rails 3.0 came out it didn't work well with it, so I switched to Evergreen since it had the features that I needed at the time and used that for well over 2 years, but it lacks nice support for the Rails 3.1 asset pipeline (and it doesn't seem to be maintained anymore?). I switched to JasmineRice fairly recently and used Cucumber to run the javascript suite on CI, but that wasn't great either.

Anyway, I'm not a fan of switching testing frameworks (honestly), and ended up writing a test runner that had all the features that I wanted -- along with several others.

It utilizes the asset pipeline, uses PhantomJS to run headless (or you can use Selenium Webdriver).. It comes with support for Jasmine, Mocha, and QUnit. You can run it via a rake task, or in the browser -- and since it's running via the Rails server itself it can be used in a more integration sort of way -- testing json api's etc.

Anyway, that's my experience, and my attempt at a solid solution. Hopefully the project will help others.

https://github.com/modeset/teaspoon

Avatar

I personally don't think there is. I worked on a rails app where I built the authorisation from scratch with the roles hardcoded and denormalized in a string in my user model.

Avatar

app/assets/javascripts/paintings.js.coffee

Avatar

I always browse gems, I MUST know what the gem does for me.
also I made a plugin to browse gems for Sublime Text.
Take a look: https://github.com/NaN1488/sublime-gem-browser

Avatar

Great episode!

However, has anyone considered the use of "protocols" like Smalltalk does? Any thoughts on pros and cons about using it?

Avatar

I had a similar problem, wherein my updated code is not being loaded by unicorn even after reload. Turns out that it's because I have my unicorn config file set preload_app to true, and sending a HUP signal, which unicorn_init.sh uses for reload and restart, would not make unicorn load the code changes (see HUP under http://unicorn.bogomips.org/SIGNALS.html). So I modified deploy.rb to use

%w[start stop reload upgrade].each do |command|

instead and use 'reload' for changes in unicorn.rb and 'upgrade' for rails code changes

Avatar

The contents of the from_email method is short-hand for

ruby
def self.from_email(email)
    PasswordReset.new(User.find_by_email(email))
end

Ryan uses implicit parentheses and the fact that you can call other class-level methods without a prefix in a class-level method.

Avatar

Sorry, but you just blew my mind with Page.find_each(&:save)
I knew about find_each for batch loading, but what the heck is &:save and where else can I use it?
I mean, obviously I can see what it's doing, but can I use this anywhere a block is expected? When can't you use this notation?

Avatar

Hi Guys,
Richard Schneeman is very nice. He posted the article how to implement wicked for another resource.

https://github.com/schneems/wicked/wiki/Building-Partial-Objects-Step-by-Step

Stay tuned :-)

Avatar

Hi everyone,
if you want to use another resource say "Products", you must
pass the product id in the ProductsController like this:

if @product.save
redirect_to product_step_path(:id => "first_step", :product_id => @ product.id)
else
...

And then you can find this product in the ProductStepsController:

product_steps_controller.rb:

def show
@product = Product.find(params[:product_id])
render_wizard
end

Hope it will help someone out there ;-)

Avatar

An update on profiling a rails app would be great

Avatar

Need help understanding Ryan's code for the services implementation!

I am reading Ryans code for Service Objects, specifically the PasswordReset class code. It has both the class methods and instance methods plus one instance variable @user which can be accessed via attr_reader class method, so far so good.

Consider the following class method in this service:

password_reset.rb
  def self.from_email(email)
    new User.find_by_email(email)
  end

It shows that the class method is returning a new instance of the "User" class, correct? See how he calls and uses it further in the PasswordResetsController class as shown below:

password_resets_controller.rb
  def create
    password_reset = PasswordReset.from_email(params[:email])
    if password_reset.user
      password_reset.send_email
      redirect_to root_url, notice: "Email sent with password reset instructions."
    else
      redirect_to new_password_reset_url, alert: "Email address does not match a user account."
    end
  end

it seems like his call to PassworReset's class method from_email is returning a new instance of the PasswordReset class itself. The method implementation shows that it is returning a new instance of the "User" class however. He then accesses the associated instance variable user via the newly created instance as shown in "if password_reset.user" line of the create method shown above. I am totally confused as to what is going on here. Is this an error? or some "quack quack" (duck typing mumbo jumbo) stuff going on here?

Kindly explain.

Thanks.

Bharat

Avatar

Noob question for the jQuery File Upload setup:
Should i be checking that the image url sent from AWS S3 after user upload is actually from my S3 bucket and not from somewhere else harmful?

Avatar

In case anyone else had trouble maintaining the current directory when opening a new tab, I wrote the following plugin:

console
# Tell the terminal about the working directory at each prompt.

update_terminal_cwd() {
    # Identify the directory using a "file:" scheme URL,
    # including the host name to disambiguate local vs.
    # remote connections. Percent-escape spaces.
local SEARCH=' '
local REPLACE='%20'
local PWD_URL="file://$HOSTNAME${PWD//$SEARCH/$REPLACE}"
printf '\e]7;%s\a' "$PWD_URL"
}

precmd_functions+='update_terminal_cwd'

Then just call the plugin in your .zshrc

Avatar

You can implement it with the history object, and it will solve problems with the back button, bookmarks etc.

Avatar

I too have an empty tag_list in my form when editing or updating. If I leave it empty it removes my tags.

Avatar

Yes, I also came to Rails from Grails. Grails has a strong service layer concept, and encourages to push a lot of business logic into stateless (and transactional) service layer. Something I've missed in Rails, so I started doing something like this implicitly by creating non-db backed model classes.

http://grails.org/doc/latest/guide/services.html

Also, I would suggest if you are going to use service objects, to use similar convention as with controllers, i.e. call an object authentication_service vs service. That will help to know where the original definition lives - i.e. is it a model or a service.

Avatar

Sorry for double post, but that's obviously:

ruby
user = User.new
user.apply_omniauth(omniauth)

in the authentications controller, not registrations

Avatar

I'm getting:

undefined method 'apply_omniauth' for #<User:0x007f84ec10b940>

when calling:

ruby
user.new
user.apply_omniauth(omniauth)

in the registrations controller.

Is there a better way of doing this via the methods in Ryans later episode? http://railscasts.com/episodes/235-devise-and-omniauth-revised

Avatar

+1

Using service objects definitely seems cleaner than concerns. As it is, if you have many concerns your model will become fat with include statements :).

Avatar

I tried "counter_culture" gem and it is working great. Thank you.

Avatar

Couldn't you use a 'scope' for this?

Edit: @Ryan L. Cross: +1

Avatar

I love the service object approach. Your episode on Presenters from scratch really expanded my mind on how to simplify view code using models. I've come to really hate helper modules because they are so hard to test and easy to make a mess with.

For controllers I've been using a set of classes I call ControllerAssistants. I set them up through a before filter and give them info like the params, etc. They are really easy to test and debug.

My models are huge and could really use some help.

Views need Presenters
Controllers need Assistants
Models need Services

How great is that?

Avatar

Id be very interested in an answer to this. We run a large data warehouse and the amount of models talking to it is becoming to much. I have name spaced it under a Dw class which handles the connection to the DB and any other shared logic. But I wonder if there is a better way to handle such a large quantity of models without cluttering the app models..?

Avatar

Maybe the walk through videos should be free.

Avatar

check out the other Gem I mentioned in a previous comment - it gives full control over delimiters, column headers, etc..

Avatar

Today I had a big problem with deploying on remote machine. Many hours spent on problem with hanging Capitrano.
When I did cap deploy:install all required packages were installed properly except one - ruby 1.9.3-p327.

Capistrano hangs forever on executing rbenv install 1.9.3-p327 with message Installing ruby-1.9.3-p327....

I've used second ssh connection with top running for checking numer of users logged in. I've detected that message Installing ruby-1.9.3-p327... still exists after number of logged in users decrease from 2 into 1.

What is the reason? SSH timeout session breaks connection.

How to solve the problem? My solution is:
1) Just edit file /etc/ssh/sshd_config and add following lines:

config
  ClientAliveInterval 18
  ClientAliveCountMax 100

Parameter ClientAliveInterval should have very small number to make pinging very often.
18*100 = 1800 seconds means 30 minutes of session timeout (enough I think).
2) Then restart ssh service:

terminal
sudo service ssh restart

3) Try to deploy:

terminal
cap deploy:install

It works now for me :-)

If you not, additionally try to edit file /etc/ssh/ssh_config:

settings
  ServerAliveInterval 18
  ServerAliveCountMax 100

Then restart ssh service.
I hope it will be helpfull for somebody.

Avatar

I'd like to know this also, I'm often given tab separated files and various text enclosures.

Avatar

How do I not use delayed_job directly with rails?

Avatar

I tried to do a little gem about this https://github.com/14113/hello_bar
Perhaps, for someone will be useful.

Avatar

Just FYI there's a typo in the destroy method

cookies.delete[:secure_user_id] should be cookies.delete(:secure_user_id)