2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2016-04-02 18:10:28 +05:30
module IssuablesHelper
2017-08-17 22:00:37 +05:30
include GitlabRoutingHelper
2021-03-11 19:13:27 +05:30
include IssuablesDescriptionTemplatesHelper
2021-11-11 11:23:49 +05:30
include :: Sidebars :: Concerns :: HasPill
2017-08-17 22:00:37 +05:30
2016-04-02 18:10:28 +05:30
def sidebar_gutter_toggle_icon
2021-01-03 14:25:43 +05:30
content_tag ( :span , class : 'js-sidebar-toggle-container' , data : { is_expanded : ! sidebar_gutter_collapsed? } ) do
sprite_icon ( 'chevron-double-lg-left' , css_class : " js-sidebar-expand #{ 'hidden' unless sidebar_gutter_collapsed? } " ) +
sprite_icon ( 'chevron-double-lg-right' , css_class : " js-sidebar-collapse #{ 'hidden' if sidebar_gutter_collapsed? } " )
end
2016-04-02 18:10:28 +05:30
end
def sidebar_gutter_collapsed_class
2022-07-16 23:28:13 +05:30
return " right-sidebar-expanded " if moved_mr_sidebar_enabled?
2016-04-02 18:10:28 +05:30
" right-sidebar- #{ sidebar_gutter_collapsed? ? 'collapsed' : 'expanded' } "
end
2018-10-15 14:42:47 +05:30
def sidebar_gutter_tooltip_text
sidebar_gutter_collapsed? ? _ ( 'Expand sidebar' ) : _ ( 'Collapse sidebar' )
end
2019-07-31 22:56:46 +05:30
def assignees_label ( issuable , include_value : true )
2021-04-29 21:17:54 +05:30
assignees = issuable . assignees
2019-07-31 22:56:46 +05:30
if include_value
sanitized_list = sanitize_name ( issuable . assignee_list )
2021-04-29 21:17:54 +05:30
ns_ ( 'NotificationEmail|Assignee: %{users}' , 'NotificationEmail|Assignees: %{users}' , assignees . count ) % { users : sanitized_list }
2018-10-15 14:42:47 +05:30
else
2021-04-29 21:17:54 +05:30
ns_ ( 'NotificationEmail|Assignee' , 'NotificationEmail|Assignees' , assignees . count )
2018-10-15 14:42:47 +05:30
end
end
2019-02-15 15:39:39 +05:30
def sidebar_milestone_tooltip_label ( milestone )
return _ ( 'Milestone' ) unless milestone . present?
2020-08-18 19:51:02 +05:30
[ escape_once ( milestone [ :title ] ) , sidebar_milestone_remaining_days ( milestone ) || _ ( 'Milestone' ) ] . join ( '<br/>' )
2019-02-15 15:39:39 +05:30
end
def sidebar_milestone_remaining_days ( milestone )
due_date_with_remaining_days ( milestone [ :due_date ] , milestone [ :start_date ] )
end
def sidebar_due_date_tooltip_label ( due_date )
[ _ ( 'Due date' ) , due_date_with_remaining_days ( due_date ) ] . compact . join ( '<br/>' )
end
def due_date_with_remaining_days ( due_date , start_date = nil )
return unless due_date
" #{ due_date . to_s ( :medium ) } ( #{ remaining_days_in_words ( due_date , start_date ) } ) "
2018-10-15 14:42:47 +05:30
end
2016-06-02 11:05:42 +05:30
def multi_label_name ( current_labels , default_label )
2019-02-15 15:39:39 +05:30
return default_label if current_labels . blank?
title = current_labels . first . try ( :title ) || current_labels . first [ :title ]
if current_labels . size > 1
" #{ title } + #{ current_labels . size - 1 } more "
2016-06-02 11:05:42 +05:30
else
2019-02-15 15:39:39 +05:30
title
2016-06-02 11:05:42 +05:30
end
end
2019-09-04 21:01:54 +05:30
def serialize_issuable ( issuable , opts = { } )
2018-03-17 18:26:18 +05:30
serializer_klass = case issuable
when Issue
IssueSerializer
when MergeRequest
MergeRequestSerializer
end
serializer_klass
. new ( current_user : current_user , project : issuable . project )
2019-09-04 21:01:54 +05:30
. represent ( issuable , opts )
2018-03-17 18:26:18 +05:30
. to_json
2017-08-17 22:00:37 +05:30
end
def users_dropdown_label ( selected_users )
case selected_users . length
when 0
2022-01-26 12:08:38 +05:30
_ ( 'Unassigned' )
2017-08-17 22:00:37 +05:30
when 1
selected_users [ 0 ] . name
else
" #{ selected_users [ 0 ] . name } + #{ selected_users . length - 1 } more "
end
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2016-06-02 11:05:42 +05:30
def user_dropdown_label ( user_id , default_label )
2016-06-16 23:09:34 +05:30
return default_label if user_id . nil?
2016-06-02 11:05:42 +05:30
return " Unassigned " if user_id == " 0 "
2016-06-16 23:09:34 +05:30
user = User . find_by ( id : user_id )
2016-06-02 11:05:42 +05:30
if user
user . name
else
default_label
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2016-06-02 11:05:42 +05:30
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2016-09-29 09:46:39 +05:30
def project_dropdown_label ( project_id , default_label )
return default_label if project_id . nil?
return " Any project " if project_id == " 0 "
project = Project . find_by ( id : project_id )
if project
2018-03-27 19:54:05 +05:30
project . full_name
2016-09-29 09:46:39 +05:30
else
default_label
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2016-09-29 09:46:39 +05:30
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2018-11-18 11:00:15 +05:30
def group_dropdown_label ( group_id , default_label )
return default_label if group_id . nil?
return " Any group " if group_id == " 0 "
group = :: Group . find_by ( id : group_id )
if group
group . full_name
else
default_label
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2018-11-18 11:00:15 +05:30
2022-01-26 12:08:38 +05:30
def milestone_dropdown_label ( milestone_title , default_label = _ ( 'Milestone' ) )
2017-08-17 22:00:37 +05:30
title =
case milestone_title
when Milestone :: Upcoming . name then Milestone :: Upcoming . title
when Milestone :: Started . name then Milestone :: Started . title
else milestone_title . presence
end
2016-06-02 11:05:42 +05:30
2017-08-17 22:00:37 +05:30
h ( title || default_label )
end
2021-02-22 17:27:13 +05:30
def issuable_meta_author_status ( author )
2022-08-27 11:52:29 +05:30
return " " unless author & . status & . customized? && status = user_status ( author )
2021-02-22 17:27:13 +05:30
" #{ status } " . html_safe
2016-06-02 11:05:42 +05:30
end
2021-02-22 17:27:13 +05:30
def issuable_meta ( issuable , project )
2018-12-05 23:21:45 +05:30
output = [ ]
2022-08-27 11:52:29 +05:30
if issuable . respond_to? ( :work_item_type ) && WorkItems :: Type :: WI_TYPES_WITH_CREATED_HEADER . include? ( issuable . work_item_type . base_type )
2022-10-11 01:57:18 +05:30
output << content_tag ( :span , sprite_icon ( " #{ issuable . work_item_type . icon_name } " , css_class : 'gl-icon gl-vertical-align-middle gl-text-gray-500' ) , class : 'gl-mr-2' , aria : { hidden : 'true' } )
2022-08-27 11:52:29 +05:30
output << s_ ( 'IssuableStatus|%{wi_type} created %{created_at} by ' ) . html_safe % { wi_type : issuable . issue_type . capitalize , created_at : time_ago_with_tooltip ( issuable . created_at ) }
else
output << s_ ( 'IssuableStatus|Created %{created_at} by' ) . html_safe % { created_at : time_ago_with_tooltip ( issuable . created_at ) }
end
2018-12-05 23:21:45 +05:30
2021-02-22 17:27:13 +05:30
if issuable . is_a? ( Issue ) && issuable . service_desk_reply_to
output << " #{ html_escape ( issuable . service_desk_reply_to ) } via "
end
2016-06-02 11:05:42 +05:30
output << content_tag ( :strong ) do
2019-02-15 15:39:39 +05:30
author_output = link_to_member ( project , issuable . author , size : 24 , mobile_classes : " d-none d-sm-inline " )
2019-07-07 11:18:12 +05:30
author_output << link_to_member ( project , issuable . author , size : 24 , by_username : true , avatar : false , mobile_classes : " d-inline d-sm-none " )
2018-11-18 11:00:15 +05:30
2020-05-24 23:13:21 +05:30
author_output << issuable_meta_author_slot ( issuable . author , css_class : 'ml-1' )
2021-02-22 17:27:13 +05:30
author_output << issuable_meta_author_status ( issuable . author )
2018-11-18 11:00:15 +05:30
author_output
2016-06-02 11:05:42 +05:30
end
2017-08-17 22:00:37 +05:30
2020-11-24 15:15:51 +05:30
if access = project . team . human_max_access ( issuable . author_id )
2021-01-03 14:25:43 +05:30
output << content_tag ( :span , access , class : " user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 " , title : _ ( " This user has the %{access} role in the %{name} project. " ) % { access : access . downcase , name : project . name } )
2020-11-24 15:15:51 +05:30
elsif project . team . contributor? ( issuable . author_id )
output << content_tag ( :span , _ ( " Contributor " ) , class : " user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 " , title : _ ( " This user has previously committed to the %{name} project. " ) % { name : project . name } )
end
2020-10-24 23:57:45 +05:30
output << content_tag ( :span , ( sprite_icon ( 'first-contribution' , css_class : 'gl-icon gl-vertical-align-middle' ) if issuable . first_contribution? ) , class : 'has-tooltip gl-ml-2' , title : _ ( '1st contribution!' ) )
2018-03-17 18:26:18 +05:30
2021-01-29 00:20:46 +05:30
output << content_tag ( :span , ( issuable . task_status if issuable . tasks? ) , id : " task_status " , class : " d-none d-md-inline-block gl-ml-3 " )
2018-11-08 19:23:39 +05:30
output << content_tag ( :span , ( issuable . task_status_short if issuable . tasks? ) , id : " task_status_short " , class : " d-md-none " )
2017-08-17 22:00:37 +05:30
2018-12-05 23:21:45 +05:30
output . join . html_safe
2016-06-02 11:05:42 +05:30
end
2020-05-24 23:13:21 +05:30
# This is a dummy method, and has an override defined in ee
def issuable_meta_author_slot ( author , css_class : nil )
nil
end
2018-05-09 12:01:36 +05:30
def issuables_state_counter_text ( issuable_type , state , display_count )
2022-01-26 12:08:38 +05:30
titles = {
opened : _ ( " Open " ) ,
closed : _ ( " Closed " ) ,
merged : _ ( " Merged " ) ,
all : _ ( " All " )
}
2016-11-03 12:29:30 +05:30
state_title = titles [ state ] || state . to_s . humanize
html = content_tag ( :span , state_title )
2018-05-09 12:01:36 +05:30
2021-03-11 19:13:27 +05:30
return html . html_safe unless display_count
count = issuables_count_for_state ( issuable_type , state )
if count != - 1
2022-04-04 11:22:00 +05:30
html << " " << gl_badge_tag ( format_count ( issuable_type , count , Gitlab :: IssuablesCountForState :: THRESHOLD ) , { variant : :muted , size : :sm } , { class : " gl-tab-counter-badge gl-display-none gl-sm-display-inline-flex " } )
2018-05-09 12:01:36 +05:30
end
2016-11-03 12:29:30 +05:30
html . html_safe
end
2017-08-17 22:00:37 +05:30
def assigned_issuables_count ( issuable_type )
2018-03-17 18:26:18 +05:30
case issuable_type
when :issues
current_user . assigned_open_issues_count
when :merge_requests
current_user . assigned_open_merge_requests_count
else
raise ArgumentError , " invalid issuable ` #{ issuable_type } ` "
end
2017-08-17 22:00:37 +05:30
end
def issuable_reference ( issuable )
@show_full_reference ? issuable . to_reference ( full : true ) : issuable . to_reference ( @group || @project )
end
2021-09-04 01:27:46 +05:30
def issuable_project_reference ( issuable )
" #{ issuable . project . full_name } #{ issuable . to_reference } "
end
2017-09-10 17:25:29 +05:30
def issuable_initial_data ( issuable )
data = {
2018-03-17 18:26:18 +05:30
endpoint : issuable_path ( issuable ) ,
updateEndpoint : " #{ issuable_path ( issuable ) } .json " ,
canUpdate : can? ( current_user , :" update_ #{ issuable . to_ability_name } " , issuable ) ,
canDestroy : can? ( current_user , :" destroy_ #{ issuable . to_ability_name } " , issuable ) ,
2022-10-11 01:57:18 +05:30
canUpdateTimelineEvent : can? ( current_user , :admin_incident_management_timeline_event , issuable ) ,
2017-09-10 17:25:29 +05:30
issuableRef : issuable . to_reference ,
2022-06-21 17:19:12 +05:30
markdownPreviewPath : preview_markdown_path ( parent , target_type : issuable . model_name , target_id : issuable . iid ) ,
2018-03-17 18:26:18 +05:30
markdownDocsPath : help_page_path ( 'user/markdown' ) ,
2019-03-02 22:35:43 +05:30
lockVersion : issuable . lock_version ,
2022-07-16 23:28:13 +05:30
state : issuable . state ,
2019-12-21 20:55:43 +05:30
issuableTemplateNamesPath : template_names_path ( parent , issuable ) ,
2017-09-10 17:25:29 +05:30
initialTitleHtml : markdown_field ( issuable , :title ) ,
initialTitleText : issuable . title ,
initialDescriptionHtml : markdown_field ( issuable , :description ) ,
initialDescriptionText : issuable . description ,
initialTaskStatus : issuable . task_status
}
2020-01-01 13:55:28 +05:30
data . merge! ( issue_only_initial_data ( issuable ) )
data . merge! ( path_data ( parent ) )
data . merge! ( updated_at_by ( issuable ) )
2017-09-10 17:25:29 +05:30
2020-01-01 13:55:28 +05:30
data
end
2019-09-30 21:07:59 +05:30
2020-01-01 13:55:28 +05:30
def issue_only_initial_data ( issuable )
return { } unless issuable . is_a? ( Issue )
2018-03-17 18:26:18 +05:30
2020-01-01 13:55:28 +05:30
{
hasClosingMergeRequest : issuable . merge_requests_count ( current_user ) != 0 ,
2020-11-24 15:15:51 +05:30
issueType : issuable . issue_type ,
2020-01-01 13:55:28 +05:30
zoomMeetingUrl : ZoomMeeting . canonical_meeting_url ( issuable ) ,
2020-11-24 15:15:51 +05:30
sentryIssueIdentifier : SentryIssue . find_by ( issue : issuable ) & . sentry_issue_identifier , # rubocop:disable CodeReuse/ActiveRecord
2021-11-11 11:23:49 +05:30
iid : issuable . iid . to_s ,
2021-12-11 22:18:48 +05:30
isHidden : issue_hidden? ( issuable ) ,
canCreateIncident : create_issue_type_allowed? ( issuable . project , :incident )
2020-01-01 13:55:28 +05:30
}
end
2017-09-10 17:25:29 +05:30
2020-01-01 13:55:28 +05:30
def path_data ( parent )
return { groupPath : parent . path } if parent . is_a? ( Group )
{
2020-11-24 15:15:51 +05:30
projectPath : ref_project . path ,
2021-03-11 19:13:27 +05:30
projectId : ref_project . id ,
2020-11-24 15:15:51 +05:30
projectNamespace : ref_project . namespace . full_path
2020-01-01 13:55:28 +05:30
}
2017-09-10 17:25:29 +05:30
end
def updated_at_by ( issuable )
2018-03-17 18:26:18 +05:30
return { } unless issuable . edited?
2017-09-10 17:25:29 +05:30
{
2018-03-17 18:26:18 +05:30
updatedAt : issuable . last_edited_at . to_time . iso8601 ,
2017-09-10 17:25:29 +05:30
updatedBy : {
name : issuable . last_edited_by . name ,
path : user_path ( issuable . last_edited_by )
}
}
end
2018-03-17 18:26:18 +05:30
def issuables_count_for_state ( issuable_type , state )
2021-12-11 22:18:48 +05:30
Gitlab :: IssuablesCountForState . new ( finder , store_in_redis_cache : true ) [ state ]
2018-03-17 18:26:18 +05:30
end
2017-09-10 17:25:29 +05:30
2018-03-17 18:26:18 +05:30
def close_issuable_path ( issuable )
issuable_path ( issuable , close_reopen_params ( issuable , :close ) )
2017-09-10 17:25:29 +05:30
end
2018-03-17 18:26:18 +05:30
def reopen_issuable_path ( issuable )
issuable_path ( issuable , close_reopen_params ( issuable , :reopen ) )
2017-09-10 17:25:29 +05:30
end
2018-03-17 18:26:18 +05:30
def issuable_path ( issuable , * options )
polymorphic_path ( issuable , * options )
2017-09-10 17:25:29 +05:30
end
def issuable_author_is_current_user ( issuable )
issuable . author == current_user
end
def issuable_display_type ( issuable )
2021-01-29 00:20:46 +05:30
case issuable
when Issue
issuable . issue_type . downcase
when MergeRequest
issuable . model_name . human . downcase
end
2017-09-10 17:25:29 +05:30
end
2019-02-15 15:39:39 +05:30
def has_filter_bar_param?
finder . class . scalar_params . any? { | p | params [ p ] . present? }
2018-03-17 18:26:18 +05:30
end
2019-12-04 20:38:33 +05:30
def assignee_sidebar_data ( assignee , merge_request : nil )
{ avatar_url : assignee . avatar_url , name : assignee . name , username : assignee . username } . tap do | data |
data [ :can_merge ] = merge_request . can_be_merged_by? ( assignee ) if merge_request
2021-03-11 19:13:27 +05:30
data [ :availability ] = assignee . status . availability if assignee . association ( :status ) . loaded? && assignee . status & . availability
2019-12-04 20:38:33 +05:30
end
end
2021-01-03 14:25:43 +05:30
def reviewer_sidebar_data ( reviewer , merge_request : nil )
{ avatar_url : reviewer . avatar_url , name : reviewer . name , username : reviewer . username } . tap do | data |
data [ :can_merge ] = merge_request . can_be_merged_by? ( reviewer ) if merge_request
end
end
2020-07-28 23:09:34 +05:30
def issuable_squash_option? ( issuable , project )
if issuable . persisted?
issuable . squash
else
project . squash_enabled_by_default?
end
end
2021-06-08 01:23:25 +05:30
def state_name_with_icon ( issuable )
2022-07-16 23:28:13 +05:30
if issuable . is_a? ( MergeRequest )
if issuable . open?
[ _ ( " Open " ) , " merge-request-open " ]
elsif issuable . merged?
[ _ ( " Merged " ) , " merge " ]
else
[ _ ( " Closed " ) , " merge-request-close " ]
end
2021-06-08 01:23:25 +05:30
else
2022-07-16 23:28:13 +05:30
if issuable . open?
[ _ ( " Open " ) , " issues " ]
else
[ _ ( " Closed " ) , " issue-closed " ]
end
2021-06-08 01:23:25 +05:30
end
end
2016-04-02 18:10:28 +05:30
private
def sidebar_gutter_collapsed?
cookies [ :collapsed_gutter ] == 'true'
end
2019-02-15 15:39:39 +05:30
def issuable_todo_button_data ( issuable , is_collapsed )
2017-08-17 22:00:37 +05:30
{
2021-01-03 14:25:43 +05:30
todo_text : _ ( 'Add a to do' ) ,
2019-09-30 21:07:59 +05:30
mark_text : _ ( 'Mark as done' ) ,
2019-02-15 15:39:39 +05:30
todo_icon : sprite_icon ( 'todo-add' ) ,
mark_icon : sprite_icon ( 'todo-done' , css_class : 'todo-undone' ) ,
issuable_id : issuable [ :id ] ,
issuable_type : issuable [ :type ] ,
create_path : issuable [ :create_todo_path ] ,
delete_path : issuable . dig ( :current_user , :todo , :delete_path ) ,
placement : is_collapsed ? 'left' : nil ,
container : is_collapsed ? 'body' : nil ,
boundary : 'viewport' ,
2019-12-04 20:38:33 +05:30
is_collapsed : is_collapsed ,
track_label : " right_sidebar " ,
track_property : " update_todo " ,
2021-11-11 11:23:49 +05:30
track_action : " click_button " ,
2019-12-04 20:38:33 +05:30
track_value : " "
2017-08-17 22:00:37 +05:30
}
end
2017-09-10 17:25:29 +05:30
def close_reopen_params ( issuable , action )
{
issuable . model_name . to_s . underscore = > { state_event : action }
} . tap do | params |
params [ :format ] = :json if issuable . is_a? ( Issue )
end
end
2018-03-17 18:26:18 +05:30
def labels_path
if @project
project_labels_path ( @project )
elsif @group
group_labels_path ( @group )
end
end
2019-02-15 15:39:39 +05:30
def issuable_sidebar_options ( issuable )
2017-09-10 17:25:29 +05:30
{
2019-02-15 15:39:39 +05:30
endpoint : " #{ issuable [ :issuable_json_path ] } ?serializer=sidebar_extras " ,
toggleSubscriptionEndpoint : issuable [ :toggle_subscription_path ] ,
moveIssueEndpoint : issuable [ :move_issue_path ] ,
projectsAutocompleteEndpoint : issuable [ :projects_autocomplete_path ] ,
editable : issuable . dig ( :current_user , :can_edit ) ,
currentUser : issuable [ :current_user ] ,
2017-09-10 17:25:29 +05:30
rootPath : root_path ,
2019-09-30 21:07:59 +05:30
fullPath : issuable [ :project_full_path ] ,
2020-04-08 14:13:33 +05:30
iid : issuable [ :iid ] ,
2021-06-08 01:23:25 +05:30
id : issuable [ :id ] ,
2020-11-24 15:15:51 +05:30
severity : issuable [ :severity ] ,
2021-03-08 18:12:59 +05:30
timeTrackingLimitToHours : Gitlab :: CurrentSettings . time_tracking_limit_to_hours ,
2021-04-17 20:07:23 +05:30
createNoteEmail : issuable [ :create_note_email ] ,
2021-06-08 01:23:25 +05:30
issuableType : issuable [ :type ]
2017-09-10 17:25:29 +05:30
}
end
2018-03-17 18:26:18 +05:30
2021-01-29 00:20:46 +05:30
def sidebar_labels_data ( issuable_sidebar , project )
{
allow_label_create : issuable_sidebar . dig ( :current_user , :can_admin_label ) . to_s ,
allow_scoped_labels : issuable_sidebar [ :scoped_labels_available ] . to_s ,
can_edit : issuable_sidebar . dig ( :current_user , :can_edit ) . to_s ,
iid : issuable_sidebar [ :iid ] ,
issuable_type : issuable_sidebar [ :type ] ,
labels_fetch_path : issuable_sidebar [ :project_labels_path ] ,
labels_manage_path : project_labels_path ( project ) ,
project_issues_path : issuable_sidebar [ :project_issuables_path ] ,
project_path : project . full_path ,
selected_labels : issuable_sidebar [ :labels ] . to_json
}
end
2021-09-30 23:02:18 +05:30
def sidebar_status_data ( issuable_sidebar , project )
{
iid : issuable_sidebar [ :iid ] ,
issuable_type : issuable_sidebar [ :type ] ,
full_path : project . full_path ,
can_edit : issuable_sidebar . dig ( :current_user , :can_edit ) . to_s
}
end
2018-03-17 18:26:18 +05:30
def parent
@project || @group
end
2021-11-11 11:23:49 +05:30
def format_count ( issuable_type , count , threshold )
2021-12-11 22:18:48 +05:30
if issuable_type == :issues && parent . is_a? ( Group )
2021-11-11 11:23:49 +05:30
format_cached_count ( threshold , count )
else
number_with_delimiter ( count )
end
end
2016-04-02 18:10:28 +05:30
end
2019-12-04 20:38:33 +05:30
2021-06-08 01:23:25 +05:30
IssuablesHelper . prepend_mod_with ( 'IssuablesHelper' )