Evan Lecklider
Evan Lecklider

Reputation: 185

Accessing Sinatra scope from another class

I'm running a Sinatra application with a few extra classes pulled in for creating a User and a few others on the fly (no DB, it feeds in from a web service). I'm trying send out a flash notice (using https://github.com/nakajima/rack-flash) from within my User model but can't figure out how to get access to the flash method/variable because I'm out of scope.

Something like:

class User
  def something
    if true
      flash[:notice] = 'Good job'
    else
      # nope
    end
  end
end

Which gets required into the Sinatra app by a simple require 'models/user'

Upvotes: 2

Views: 595

Answers (2)

Phrogz
Phrogz

Reputation: 303244

You should not ask your User (model) to talk to the UI (view). That's bad/not MVC-clean. That's what a controller is for.

You can use either return values, exceptions, or throw/catch (which is not exception handling) to pass information from your model to your controller. For example, using return values:

post "/create_user" do
  flash[:notice] = case User.something
    when User   then "User Created!"
    when :nono  then "That's not allowed"
    when :later then "User queued to be created later."
  end
end

class User
  def self.something
    if authorized
      if can_create_now
        new(...)
      else
        queue_create(...)
        :later
      end
    else
      :nono
    end
  end
end

Since I mentioned them above, following are examples using throw/catch and begin/rescue (exceptions). As the advisability of using either of these constructs is questionable, let us take a moment of silence to ponder if this is a good idea.


Here is an example using throw/catch:

post "/create_user" do
  result = catch(:msg){ User.something }
  flash[:notice] = case 
    when :nono  then "That's not allowed"
    when :later then "User queued to be created later."
    else             "User Created!"
  end
end

class User
  def self.something
    throw :msg, :nono unless authorized
    if can_create_now
      new(...)
    else
      queue_create(...)
      throw :msg, :later
    end
  end
end

Finally, here's an example using exceptions, though I'm not convinced that this will be appropriate for all (non-disastrous) cases where you want to flash unique messages to the user:

post "/create_user" do
  flash[:notice] = "User Created!" # Assume all good
  begin
    User.something
  rescue User::Trouble=>e
    flash[:notice] = case e
      when Unauthorized  then "That's not allowed"
      when DelayedCreate then "User queued to be created later."
      else                    "Uh...Something bad happened."
    end
  end
end

class User
  class Trouble < RuntimeError; end
  class Unauthorized  < Trouble; end
  class DelayedCreate < Trouble; end
  def self.something
    raise Unauthorized unless authorized
    if can_create_now
      new(...)
    else
      queue_create(...)
      raise DelayedCreate
    end
  end
end

Exceptions let you pass an additional data along (e.g. raise Unauthorized.new "No such account", or any custom properties you want to add to your class), and so may be more useful (when appropriate). Just remember to pass semantic results from your model to your controller, and let it translate them into user-facing messages.

Upvotes: 1

Rein Henrichs
Rein Henrichs

Reputation: 15605

This is an XY Problem[1]. Sinatra is responsible for sending out flash messages, not your User objects, so the code for setting the flash should be in your Sinatra app, not in your User class.

[1] http://www.perlmonks.org/index.pl?node_id=542341

Upvotes: 2

Related Questions