Module: Condi

Defined in:
lib/condi/condi.rb,
lib/version.rb,
lib/condi/version.rb

Overview

Include this module in an ActionController to define predicates or synonyms within an action context that can be used later in the related action view.

Examples:

class StoreController
  include Condi
  ...
end

Constant Summary collapse

VERSION =
"1.0.0"

Instance Method Summary collapse

Instance Method Details

#predicate(method_name, &block) ⇒ Object Also known as: synonym

Note:

A synonym is an alias for defining methods that return values other than true or false.

Note:

You are not required to end a predicate with a question mark, however it is conventional in Ruby to do so.

Note:

Predicates can only be called during the request scope they are defined in, otherwise a RuntimeError is raised. This restriction prevents the associated closures from inadvertently leaking previous request data when the controller classes are cached (i.e. in production).

Note:

Predicates are semantically lambdas and may contain returns.

define a method on the controller which is callable from the related view and returns a true or false value.

Examples:

define a predicate that determines whether or not to show a "free shipping" option.

predicate(:show_free_shipping?) { user.new_customer? && cart.amount > 100 }

define a predicate that takes an element of a collection as an argument.

predicate(:shipping?) { |item| @items.count >= 3 && 
   item.status == :shipped && 
   DeliveryService.status(item.tracking_number) !~ /arrived/ }

define a synonym that returns a css class based on item status

synonym(:css_for_item_status) do |item| 
  if @items.count >= 3 && item.status == :shipped 
    if DeliveryService.status(item.tracking_number) !~ /arrived/
      "shipping"
    else
      "shipped"
    end
  else
    "processing"
  end
end

Parameters:

  • method_name (Symbol)

    name of the predicate or synonym method. (e.g. :show_action_button?)

  • block (Proc)

    {} or do...end block.

See Also:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/condi/condi.rb', line 36

def predicate(method_name, &block)
  self.class.instance_eval do
    # this is the request id at the moment the predicate is defined
    request_id = eval("request.object_id",block.binding)

    # We need to keep the block impl as a method/lambda so that it supports returns 
    # by using this particular invocation of define_method...
    method_name_impl = "impl_#{method_name}".to_sym
    define_method(method_name_impl, &block)

    # Next, this invocation of define_method creates a Proc, which
    # wraps the impl and allows us to check the request id.
    define_method(method_name) do |*args|
      # this is the request id at the moment the predicate is called
      check_request_id = request.object_id
      # if they don't match, raise an error!
      unless check_request_id == request_id
        raise RuntimeError, "predicate '#{method_name}' cannot be called outside of the request scope it was defined in (#{request_id}). please redefine the predicate in this request scope (#{check_request_id}).", caller(2)
      end
      send(method_name_impl, *args)
    end

    # finally, expose the wrapped predicate to the view.
    helper_method(method_name)
  end
end