mezis/appfab

View on GitHub
app/controllers/ideas_controller.rb

Summary

Maintainability
B
6 hrs
Test Coverage
# encoding: UTF-8
class IdeasController < ApplicationController
  include Traits::RequiresLogin

  before_filter :require_account!
  before_filter :cleanup_session
  before_filter :map_state_names,        only:[:create, :update]
  before_filter :map_product_manager_id, only:[:update]
  before_filter :map_no_category,        only:[:create, :update]
  before_filter :can_create_idea,        only:[:new, :create]

  VALID_ANGLES = %w(discussable followed)
  DEFAULT_ANGLE = VALID_ANGLES.last

  VALID_ORDERS  = %w(impact activity progress creation)
  DEFAULT_ORDER = VALID_ORDERS.first

  VALID_FILTERS = %w(all authored commented vetted backed)
  DEFAULT_FILTER = VALID_FILTERS.first

  VALID_VIEWS = %w(cards board)
  DEFAULT_VIEW = VALID_VIEWS.first

  LIMIT_RANGE = 5..100
  DEFAULT_LIMIT = { 'board' => 24, 'cards' => 12 }

  def index
    @angle    = set_angle_from_params
    @order    = set_order_from_params_and_angle
    @filter   = set_filter_from_params_and_angle
    @category = set_category_from_params_and_angle
    @view     = set_view_from_params
    @limit    = set_limit_from_params(@view)
    @state    = set_state_from_params


    # account, angle and filter
    @ideas = current_account.ideas.undead_ideas
    @ideas = @ideas.send(:"#{@angle}_by", current_user)
    @ideas = @ideas.send(:"#{@filter}_by", current_user) unless @filter == 'all'

    if @category == 'none'
      @ideas = @ideas.where(category: nil)
    elsif @category != 'all'
      @ideas = @ideas.where(category: @category)
    end

    @ideas = @ideas.with_state(@state.to_sym) unless @state == 'all'

    # counts before ordering and pagination
    @idea_counts = @ideas.counts_per_state if @view == 'board'

    # order
    @ideas = @ideas.send(:"by_#{@order}")

    # pagination
    case @view
    when 'board'
      @ideas = @ideas.limit_per_state(limit:@limit)
    else
      @ideas = @ideas.paginate(page:1, per_page:@limit)
    end

    # eager-load participants
    @ideas = @ideas.includes(:participants => :login)
  end

  def show
    @idea = find_idea(params[:id])
    self.current_account = @idea.account

    @just_submitted = session.delete(:just_submitted)

    if request.xhr?
      case params['part']
      when 'attachments'
        render_ujs @idea.attachments
      else
        render nothing:true, status:400
      end
    end
  end

  def new
    @idea = current_user.ideas.new
  end

  def create
    @idea = current_user.ideas.new(idea_params)
    @idea.account = current_account
    if @idea.save
      session[:just_submitted] = true
      redirect_to @idea
    else
      render :action => 'new'
    end
  end

  def edit
    @idea = find_idea(params[:id])
  end

  def update
    @idea = find_idea(params[:id])

    # specifics on account change
    if new_account_id = idea_params.delete(:account_id)
      update_account @idea, new_account_id
      return
    end

    # specifics on state changes
    if state = idea_params.delete(:state)
      # auto-set product manager when picking
      if state == IdeaStateMachine.state_value(:picked)
        @idea.product_manager = current_user
      end

      if @idea.state_machine.draft? && state == IdeaStateMachine.state_value(:submitted)
        # this is a draft promoted to submitted
        session[:just_submitted] = true
      end

      @idea.state = state
      IdeaStateMachineService.new(@idea).run
    end

    if @idea.update_attributes(idea_params)
      current_user.bookmarked_ideas.add!(@idea)
      redirect_to @idea, notice: _("Successfully updated idea.")
    else
      session.delete(:just_submitted)
      render :action => 'edit'
    end
  end

  def destroy
    @idea = Idea.find(params[:id])
    @idea.destroy
    redirect_to ideas_url, notice: _("Successfully destroyed idea.")
  end


  private
  include AuthorizationHelper

  def update_account(idea, new_account_id)
    new_account = Account.find(new_account_id)

    # check autorization
    unless can?(:move, @idea)
      flash[:error] = _('You cannot move this idea unless you are the author, the product manager, or an account owner.')
      redirect_to @idea
      return
    end

    unless current_login.accounts.include?(new_account)
      flash[:error] = _('You must be a member of the target account to move and idea there.')
      redirect_to @idea
      return
    end

    IdeaMoverService.new(idea:@idea, account:new_account).run
    flash[:notice] = _('Successfully changed idea account')
    redirect_to @idea
  end


  def find_idea(id)
    idea = Idea.where(account_id:current_login.accounts.pluck(:id)).find(id)
    self.current_account = idea.account
    return idea
  end

  def cleanup_session
    {
      ideas_angle:  String,
      ideas_order:  Hash,
      ideas_filter: Hash
    }.each_pair do |session_key, type|
      next if session[session_key].kind_of?(type)
      session.delete(session_key)
    end
  end

  def set_angle_from_params
    params[:angle] =
    session[:ideas_angle] = begin
      (VALID_ANGLES.include?(params[:angle])        and params[:angle]) ||
      (VALID_ANGLES.include?(session[:ideas_angle]) and session[:ideas_angle]) ||
      session[:ideas_angle] ||
      DEFAULT_ANGLE
    end
  end

  def set_view_from_params
    params[:view] =
    session[:ideas_view] = begin
      (VALID_VIEWS.include?(params[:view])        and params[:view]) ||
      (VALID_VIEWS.include?(session[:ideas_view]) and session[:ideas_view]) ||
      session[:ideas_view] ||
      DEFAULT_VIEW
    end
  end

  def set_order_from_params_and_angle
    session[:ideas_order] ||= {}
    params[:order] =
    session[:ideas_order][@angle] = begin
      (VALID_ORDERS.include?(params[:order])                and params[:order]) ||
      (VALID_ORDERS.include?(session[:ideas_order][@angle]) and session[:ideas_order][@angle]) ||
      DEFAULT_ORDER
    end
  end

  def set_filter_from_params_and_angle
    session[:ideas_filter] ||= {}
    params[:filter] =
    session[:ideas_filter][@angle] = begin
      (VALID_FILTERS.include?(params[:filter])                and params[:filter]) ||
      (VALID_FILTERS.include?(session[:ideas_filter][@angle]) and session[:ideas_filter][@angle]) ||
      DEFAULT_FILTER
    end
  end


  def set_category_from_params_and_angle
    session[:ideas_category] ||= {}
    params[:category] =
    session[:ideas_category][@angle] = begin
      valid_categories = (current_user.account.andand.categories || []) + %w(all none)
      (valid_categories.include?(params[:category])                and params[:category]) ||
      (valid_categories.include?(session[:ideas_category][@angle]) and session[:ideas_category][@angle]) ||
      'all'
    end
  end


  def set_limit_from_params(view)
    @limit = begin
      limit = params[:limit].to_i rescue 0
      LIMIT_RANGE.include?(limit) ? limit : DEFAULT_LIMIT[view]
    end
  end


  VALID_STATES = IdeaStateMachine.all_state_names.map(&:to_s)
  def set_state_from_params
    @state = begin
      state = params.fetch(:state, 'all')
      VALID_STATES.include?(state) ? state : 'all'
    end
  end


  # map state names to values
  def map_state_names
    return unless state = idea_params[:state]
    idea_params[:state] = IdeaStateMachine.state_value(state)
  end


  # map 'none' category to the valid nil
  def map_no_category
    return unless category = idea_params[:category]
    return unless category == 'none'
    idea_params[:category] = nil
  end


  # replace user id with a user instance
  def map_product_manager_id
    return unless product_manager_id = idea_params.delete(:product_manager_id)
    idea_params[:product_manager] = current_account.users.find(product_manager_id)
  end


  def can_create_idea
    return if can?(:create, Idea)
    flash[:error] = not_authorized_message(:create, Idea)
    redirect_back_or_to ideas_path
  end

  def idea_params
    @_idea_params ||= params.
      require(:idea).
      permit(:account_id, :state, :product_manager_id, :title, :problem, :solution, :metrics, :design_size, :development_size, :category)
  end
end