# encoding: utf-8
require 'rails_best_practices/reviews/review'

module RailsBestPractices
  module Reviews
    # Review a view file to make sure there is no complex options_for_select message call.
    #
    # See the best practice details here http://rails-bestpractices.com/posts/26-move-code-into-helper.
    #
    # TODO: we need a better soluation, any suggestion?
    #
    # Implementation:
    #
    # Review process:
    #   check al method calls to see if there is a complex options_for_select helper.
    #
    #   if the message of the call node is options_for_select,
    #   and the first argument of the call node is array,
    #   and the size of the array is greater than array_count defined,
    #   then the options_for_select method should be moved into helper.
    class MoveCodeIntoHelperReview < Review
      def url
        "http://rails-bestpractices.com/posts/26-move-code-into-helper"
      end

      def interesting_nodes
        [:call]
      end

      def interesting_files
        VIEW_FILES
      end

      def initialize(options = {})
        super()
        @array_count = options['array_count'] || 3
      end

      # check call node with message options_for_select (sorry we only check options_for_select helper now).
      #
      # if the first argument of options_for_select method call is an array,
      # and the size of the array is more than @array_count defined,
      # then the options_for_select helper should be moved into helper.
      def start_call(node)
        add_error "move code into helper (array_count >= #{@array_count})" if complex_select_options?(node)
      end

      private
        # check if the arguments of options_for_select are complex.
      #
      # if the first argument is an array,
      # and the size of array is greater than @array_count you defined,
      # then it is complext, e.g.
      #
      #     s(:call, nil, :options_for_select,
      #       s(:arglist,
      #         s(:array,
      #           s(:array,
      #             s(:call, nil, :t, s(:arglist, s(:lit, :draft))),
      #             s(:str, "draft")
      #           ),
      #           s(:array,
      #             s(:call, nil, :t, s(:arglist, s(:lit, :published))),
      #             s(:str, "published")
      #           )
      #         ),
      #         s(:call,
      #           s(:call, nil, :params, s(:arglist)),
      #           :[],
      #           s(:arglist, s(:lit, :default_state))
      #         )
      #       )
      #     )
        def complex_select_options?(node)
          :options_for_select == node.message and :array == node.arguments[1].node_type and node.arguments[1].size > @array_count
        end
    end
  end
end