Module: IsListable::ClassMethods

Defined in:
lib/is_listable.rb

Instance Method Summary collapse

Instance Method Details

#is_listable(options = {}) ⇒ Object

Is Listable

Is Listable should be invoked inside a controller and takes a hash of options. This will generate a controller action, based on the name of the column in the database. If the column is named, for example “position”, which is the default, then the action inside the controller will be defined as “position”. What this also does is it provides two view helpers per controller. If you for example have a controller named “PostsController” then this will generate two view helpers, namely: “up_button_for_posts” and “down_button_for_posts”. Both of them take two arguments, of which only the first is required, and asks for the post object for which the button should be generated.

Example: up_button_for_posts(post) down_button_for_posts(post)

That’s the bare minimum for getting the listability to work! By default, the button that will be rendered will already have id and class attributes embedded in them.

The above examples would generate these id and class attribute name and values:

id => “posts_up_button_1”, :class => “posts_up_button” id => “posts_down_button_1”, :class => “posts_up_button”

So this assists in easy button styling and applying javascript calls on. These attributes can be overwritten by adding options inside the hash

up_button_for_posts(post, :url => {}, :html => {})

Inside the :url hash attribute you may specify controller, action, id and any custom attributes you may like when using the button_to and link_to helper methods. As the :url hash attribute, you may also specify the :html hash attribute to add additional or overwrite existing html attributes for the html button tag.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/is_listable.rb', line 39

def is_listable(options = {})
  
  # Options - Has default values.
  # Can be overwritten by user through the hash-argument
  options = {
    :column       => 'position',
    :controller   => controller_name,
    :model        => controller_name.camelcase.singularize,
    :scope        => nil,
    :redirect_to  => :back,
    :permalink    => nil
  }.update(options)
  
  # Include the ActionView::Helpers inside of ActionController::Base
  # This will enable html button parsing
  ActionController::Base.send(:include, ActionView::Helpers)
  
  # This will create an action dynamically inside the corresponding controller
  # which the view helpers will link to automatically.
  define_method options[:column] do
    if options[:scope].nil?
      if params[:direction].eql?('higher')
        if options[:permalink].nil?
          Kernel.const_get(options[:model]).find(params[:id]).move_higher
        else
          Kernel.const_get(options[:model]).send("find_by_#{options[:permalink]}", params[:id]).move_higher
        end
      elsif params[:direction].eql?('lower')
        if options[:permalink].nil?
          Kernel.const_get(options[:model]).find(params[:id]).move_lower
        else
          Kernel.const_get(options[:model]).send("find_by_#{options[:permalink]}", params[:id]).move_lower
        end
      end
    else
      if params[:direction].eql?('higher')
        if options[:permalink].nil?
          options[:scope].send(options[:model].underscore.pluralize.to_sym).find(params[:id]).move_higher
        else
          options[:scope].send(options[:model].underscore.pluralize.to_sym).send("find_by_#{options[:permalink]}", params[:id]).move_higher
        end
      elsif params[:direction].eql?('lower')
        if options[:permalink].nil?
          options[:scope].send(options[:model].underscore.pluralize.to_sym).find(params[:id]).move_lower
        else
          options[:scope].send(options[:model].underscore.pluralize.to_sym).send("find_by_#{options[:permalink]}", params[:id]).move_lower
        end
      end
    end
    redirect_to(options[:redirect_to])
  end
  
  # Defines the up_button_for_(@controller_name) method
  # This method will be available inside all views inside the whole application
  define_method "up_button_for_#{options[:controller]}" do |object, *method_options|
    
    # Set default options and overwrite the existing ones with
    # possible user input
    method_options = {
      :name       => 'up',
      :attribute  => :id,
      :url        => { :controller => options[:controller], :action => options[:column], :id => object, :direction => 'higher' },
      :html       => { :id => "#{options[:controller]}_up_button_#{object.id}", :class => "#{options[:controller]}_up_button" }
    }.update(method_options.empty? ? {} : method_options.first)
    

    # Generate the up button
    button_to(method_options[:name], method_options[:url], method_options[:html]) unless object.first?
  end
  
  # Defines the down_button_for_(@controller_name) method
  # This method will be available inside all views inside the whole application
  define_method "down_button_for_#{options[:controller]}" do |object, *method_options|
    
    # Find the last position of the objects list and store it inside an instance variable
    # This will prevent multiple queries from being executed when the method is invoked
    # from inside a for/each loop. A simple form of variable caching to improve performance.
    if options[:scope].nil?
      @last_position ||= Kernel.const_get(options[:model]).last.send(options[:column].to_sym)
    else
      @last_position ||= options[:scope].send(options[:model].pluralize.underscore.to_sym).last.send(options[:column].to_sym)
    end
    
    # Set default options and overwrite the existing ones with
    # possible user input
    method_options = {
      :name => 'down',
      :url  => { :controller => options[:controller], :action => options[:column], :id => object, :direction => 'lower' },
      :html => { :id => "#{options[:controller]}_down_button_#{object.id}", :class => "#{options[:controller]}_down_button" }
    }.update(method_options.empty? ? {} : method_options.first)
    
    # Generate the down button
    unless object.send(options[:column]).eql?(@last_position)
      button_to(method_options[:name], method_options[:url], method_options[:html]) 
    end
  end
  
  # Makes the methods available to the views
  helper_method "up_button_for_#{options[:controller]}",
                "down_button_for_#{options[:controller]}"
end