Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
heptapod
heptapod
Commits
de399fb151ca
Commit
de399fb1
authored
Feb 07, 2020
by
GitLab Bot
Browse files
Add latest changes from gitlab-org/gitlab@master
parent
c4b517767528
Changes
106
Hide whitespace changes
Inline
Side-by-side
.gitlab/ci/rails.gitlab-ci.yml
View file @
de399fb1
...
...
@@ -102,9 +102,9 @@
extends
:
-
.rspec-base-pg9
-
.rspec-base-migration
parallel
:
4
parallel
:
5
rspec migration pg9-foss
:
extends
:
-
.rspec-base-pg9-foss
-
.rspec-base-migration
...
...
@@ -106,9 +106,9 @@
rspec migration pg9-foss
:
extends
:
-
.rspec-base-pg9-foss
-
.rspec-base-migration
parallel
:
4
parallel
:
5
rspec unit pg9
:
extends
:
.rspec-base-pg9
...
...
@@ -120,7 +120,7 @@
rspec integration pg9
:
extends
:
.rspec-base-pg9
parallel
:
6
parallel
:
8
rspec integration pg9-foss
:
extends
:
.rspec-base-pg9-foss
...
...
@@ -124,7 +124,7 @@
rspec integration pg9-foss
:
extends
:
.rspec-base-pg9-foss
parallel
:
6
parallel
:
8
rspec system pg9
:
extends
:
.rspec-base-pg9
...
...
@@ -140,7 +140,7 @@
rspec integration pg10
:
extends
:
.rspec-base-pg10
parallel
:
6
parallel
:
8
rspec system pg10
:
extends
:
.rspec-base-pg10
...
...
@@ -170,7 +170,7 @@
rspec-ee integration pg9
:
extends
:
.rspec-ee-base-pg9
parallel
:
3
parallel
:
4
rspec-ee system pg9
:
extends
:
.rspec-ee-base-pg9
...
...
@@ -174,7 +174,7 @@
rspec-ee system pg9
:
extends
:
.rspec-ee-base-pg9
parallel
:
5
parallel
:
6
rspec-ee migration pg10
:
extends
:
...
...
app/assets/javascripts/monitoring/components/charts/time_series.vue
View file @
de399fb1
<
script
>
import
{
omit
}
from
'
lodash
'
;
import
{
omit
,
throttle
}
from
'
lodash
'
;
import
{
GlLink
,
GlButton
,
GlTooltip
,
GlResizeObserverDirective
}
from
'
@gitlab/ui
'
;
import
{
GlAreaChart
,
GlLineChart
,
GlChartSeriesLabel
}
from
'
@gitlab/ui/dist/charts
'
;
import
dateFormat
from
'
dateformat
'
;
...
...
@@ -18,6 +18,13 @@
import
{
makeDataSeries
}
from
'
~/helpers/monitor_helper
'
;
import
{
graphDataValidatorForValues
}
from
'
../../utils
'
;
const
THROTTLED_DATAZOOM_WAIT
=
1000
;
// miliseconds
const
timestampToISODate
=
timestamp
=>
new
Date
(
timestamp
).
toISOString
();
const
events
=
{
datazoom
:
'
datazoom
'
,
};
export
default
{
components
:
{
GlAreaChart
,
...
...
@@ -98,6 +105,7 @@
height
:
chartHeight
,
svgs
:
{},
primaryColor
:
null
,
throttledDatazoom
:
null
,
};
},
computed
:
{
...
...
@@ -245,6 +253,11 @@
this
.
setSvg
(
'
rocket
'
);
this
.
setSvg
(
'
scroll-handle
'
);
},
destroyed
()
{
if
(
this
.
throttledDatazoom
)
{
this
.
throttledDatazoom
.
cancel
();
}
},
methods
:
{
formatLegendLabel
(
query
)
{
return
`
${
query
.
label
}
`
;
...
...
@@ -287,8 +300,39 @@
console
.
error
(
'
SVG could not be rendered correctly:
'
,
e
);
});
},
onChartUpdated
(
chart
)
{
[
this
.
primaryColor
]
=
chart
.
getOption
().
color
;
onChartUpdated
(
eChart
)
{
[
this
.
primaryColor
]
=
eChart
.
getOption
().
color
;
},
onChartCreated
(
eChart
)
{
// Emit a datazoom event that corresponds to the eChart
// `datazoom` event.
if
(
this
.
throttledDatazoom
)
{
// Chart can be created multiple times in this component's
// lifetime, remove previous handlers every time
// chart is created.
this
.
throttledDatazoom
.
cancel
();
}
// Emitting is throttled to avoid flurries of calls when
// the user changes or scrolls the zoom bar.
this
.
throttledDatazoom
=
throttle
(
()
=>
{
const
{
startValue
,
endValue
}
=
eChart
.
getOption
().
dataZoom
[
0
];
this
.
$emit
(
events
.
datazoom
,
{
start
:
timestampToISODate
(
startValue
),
end
:
timestampToISODate
(
endValue
),
});
},
THROTTLED_DATAZOOM_WAIT
,
{
leading
:
false
,
},
);
eChart
.
off
(
'
datazoom
'
);
eChart
.
on
(
'
datazoom
'
,
this
.
throttledDatazoom
);
},
onResize
()
{
if
(
!
this
.
$refs
.
chart
)
return
;
...
...
@@ -331,6 +375,7 @@
:height=
"height"
:average-text=
"legendAverageText"
:max-text=
"legendMaxText"
@
created=
"onChartCreated"
@
updated=
"onChartUpdated"
>
<template
v-if=
"tooltip.isDeployment"
>
...
...
app/assets/javascripts/monitoring/components/dashboard.vue
View file @
de399fb1
...
...
@@ -21,7 +21,6 @@
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
mergeUrlParams
,
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
invalidUrl
from
'
~/lib/utils/invalid_url
'
;
import
{
convertToFixedRange
}
from
'
~/lib/utils/datetime_range
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
DateTimePicker
from
'
~/vue_shared/components/date_time_picker/date_time_picker.vue
'
;
...
...
@@ -102,6 +101,11 @@
type
:
String
,
required
:
true
,
},
logsPath
:
{
type
:
String
,
required
:
false
,
default
:
invalidUrl
,
},
defaultBranch
:
{
type
:
String
,
required
:
true
,
...
...
@@ -247,9 +251,10 @@
dashboardsEndpoint
:
this
.
dashboardsEndpoint
,
currentDashboard
:
this
.
currentDashboard
,
projectPath
:
this
.
projectPath
,
logsPath
:
this
.
logsPath
,
});
},
mounted
()
{
if
(
!
this
.
hasMetrics
)
{
this
.
setGettingStartedEmptyState
();
}
else
{
...
...
@@ -250,16 +255,12 @@
});
},
mounted
()
{
if
(
!
this
.
hasMetrics
)
{
this
.
setGettingStartedEmptyState
();
}
else
{
const
{
start
,
end
}
=
convertToFixedRange
(
this
.
selectedTimeRange
);
this
.
fetchData
({
start
,
end
,
});
this
.
setTimeRange
(
this
.
selectedTimeRange
);
this
.
fetchData
();
}
},
methods
:
{
...
mapActions
(
'
monitoringDashboard
'
,
[
...
...
@@ -262,7 +263,8 @@
}
},
methods
:
{
...
mapActions
(
'
monitoringDashboard
'
,
[
'
setTimeRange
'
,
'
fetchData
'
,
'
setGettingStartedEmptyState
'
,
'
setEndpoints
'
,
...
...
app/assets/javascripts/monitoring/components/embed.vue
View file @
de399fb1
...
...
@@ -19,10 +19,4 @@
},
data
()
{
const
timeRange
=
timeRangeFromUrl
(
this
.
dashboardUrl
)
||
defaultTimeRange
;
const
{
start
,
end
}
=
convertToFixedRange
(
timeRange
);
const
params
=
{
start
,
end
,
};
return
{
...
...
@@ -28,5 +22,5 @@
return
{
params
,
timeRange
:
convertToFixedRange
(
timeRange
)
,
elWidth
:
0
,
};
},
...
...
@@ -49,7 +43,9 @@
},
mounted
()
{
this
.
setInitialState
();
this
.
fetchMetricsData
(
this
.
params
);
this
.
setTimeRange
(
this
.
timeRange
);
this
.
fetchDashboard
();
sidebarMutationObserver
=
new
MutationObserver
(
this
.
onSidebarMutation
);
sidebarMutationObserver
.
observe
(
document
.
querySelector
(
'
.layout-page
'
),
{
attributes
:
true
,
...
...
@@ -64,7 +60,8 @@
},
methods
:
{
...
mapActions
(
'
monitoringDashboard
'
,
[
'
fetchMetricsData
'
,
'
setTimeRange
'
,
'
fetchDashboard
'
,
'
setEndpoints
'
,
'
setFeatureFlags
'
,
'
setShowErrorBanner
'
,
...
...
app/assets/javascripts/monitoring/components/panel_type.vue
View file @
de399fb1
<
script
>
import
{
mapState
}
from
'
vuex
'
;
import
{
pickBy
}
from
'
lodash
'
;
import
invalidUrl
from
'
~/lib/utils/invalid_url
'
;
import
{
GlDropdown
,
GlDropdownItem
,
...
...
@@ -18,7 +19,7 @@
import
MonitorStackedColumnChart
from
'
./charts/stacked_column.vue
'
;
import
MonitorEmptyChart
from
'
./charts/empty_chart.vue
'
;
import
TrackEventDirective
from
'
~/vue_shared/directives/track_event
'
;
import
{
downloadCSVOptions
,
generateLinkToChartOptions
}
from
'
../utils
'
;
import
{
timeRangeToUrl
,
downloadCSVOptions
,
generateLinkToChartOptions
}
from
'
../utils
'
;
export
default
{
components
:
{
...
...
@@ -58,4 +59,9 @@
default
:
'
panel-type-chart
'
,
},
},
data
()
{
return
{
zoomedTimeRange
:
null
,
};
},
computed
:
{
...
...
@@ -61,5 +67,5 @@
computed
:
{
...
mapState
(
'
monitoringDashboard
'
,
[
'
deploymentData
'
,
'
projectPath
'
]),
...
mapState
(
'
monitoringDashboard
'
,
[
'
deploymentData
'
,
'
projectPath
'
,
'
logsPath
'
,
'
timeRange
'
]),
alertWidgetAvailable
()
{
return
IS_EE
&&
this
.
prometheusAlertsAvailable
&&
this
.
alertsEndpoint
&&
this
.
graphData
;
},
...
...
@@ -70,6 +76,14 @@
this
.
graphData
.
metrics
[
0
].
result
.
length
>
0
);
},
logsPathWithTimeRange
()
{
const
timeRange
=
this
.
zoomedTimeRange
||
this
.
timeRange
;
if
(
this
.
logsPath
&&
this
.
logsPath
!==
invalidUrl
&&
timeRange
)
{
return
timeRangeToUrl
(
timeRange
,
this
.
logsPath
);
}
return
null
;
},
csvText
()
{
const
chartData
=
this
.
graphData
.
metrics
[
0
].
result
[
0
].
values
;
const
yLabel
=
this
.
graphData
.
y_label
;
...
...
@@ -107,6 +121,10 @@
},
downloadCSVOptions
,
generateLinkToChartOptions
,
onDatazoom
({
start
,
end
})
{
this
.
zoomedTimeRange
=
{
start
,
end
};
},
},
};
</
script
>
...
...
@@ -130,8 +148,9 @@
<component
:is=
"monitorChartComponent"
v-else-if=
"graphDataHasMetrics"
ref=
"timeChart"
:graph-data=
"graphData"
:deployment-data=
"deploymentData"
:project-path=
"projectPath"
:thresholds=
"getGraphAlertValues(graphData.metrics)"
:group-id=
"groupId"
...
...
@@ -133,8 +152,9 @@
:graph-data=
"graphData"
:deployment-data=
"deploymentData"
:project-path=
"projectPath"
:thresholds=
"getGraphAlertValues(graphData.metrics)"
:group-id=
"groupId"
@
datazoom=
"onDatazoom"
>
<div
class=
"d-flex align-items-center"
>
<alert-widget
...
...
@@ -157,6 +177,15 @@
<template
slot=
"button-content"
>
<icon
name=
"ellipsis_v"
class=
"text-secondary"
/>
</
template
>
<gl-dropdown-item
v-if=
"logsPathWithTimeRange"
ref=
"viewLogsLink"
:href=
"logsPathWithTimeRange"
>
{{ s__('Metrics|View logs') }}
</gl-dropdown-item>
<gl-dropdown-item
v-track-event=
"downloadCSVOptions(graphData.title)"
:href=
"downloadCsv"
...
...
app/assets/javascripts/monitoring/stores/actions.js
View file @
de399fb1
import
*
as
types
from
'
./mutation_types
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
createFlash
from
'
~/flash
'
;
import
{
convertToFixedRange
}
from
'
~/lib/utils/datetime_range
'
;
import
{
gqClient
,
parseEnvironmentsResponse
,
removeLeadingSlash
}
from
'
./utils
'
;
import
trackDashboardLoad
from
'
../monitoring_tracking_helper
'
;
import
getEnvironments
from
'
../queries/getEnvironments.query.graphql
'
;
...
...
@@ -32,6 +33,10 @@
commit
(
types
.
SET_ENDPOINTS
,
endpoints
);
};
export
const
setTimeRange
=
({
commit
},
timeRange
)
=>
{
commit
(
types
.
SET_TIME_RANGE
,
timeRange
);
};
export
const
filterEnvironments
=
({
commit
,
dispatch
},
searchTerm
)
=>
{
commit
(
types
.
SET_ENVIRONMENTS_FILTER
,
searchTerm
);
dispatch
(
'
fetchEnvironmentsData
'
);
...
...
@@ -63,9 +68,9 @@
export
const
receiveEnvironmentsDataFailure
=
({
commit
})
=>
commit
(
types
.
RECEIVE_ENVIRONMENTS_DATA_FAILURE
);
export
const
fetchData
=
({
dispatch
}
,
params
)
=>
{
dispatch
(
'
fetch
MetricsData
'
,
params
);
export
const
fetchData
=
({
dispatch
})
=>
{
dispatch
(
'
fetch
Dashboard
'
);
dispatch
(
'
fetchDeploymentsData
'
);
dispatch
(
'
fetchEnvironmentsData
'
);
};
...
...
@@ -68,9 +73,7 @@
dispatch
(
'
fetchDeploymentsData
'
);
dispatch
(
'
fetchEnvironmentsData
'
);
};
export
const
fetchMetricsData
=
({
dispatch
},
params
)
=>
dispatch
(
'
fetchDashboard
'
,
params
);
export
const
fetchDashboard
=
({
state
,
dispatch
},
params
)
=>
{
export
const
fetchDashboard
=
({
state
,
dispatch
})
=>
{
dispatch
(
'
requestMetricsDashboard
'
);
...
...
@@ -75,3 +78,11 @@
dispatch
(
'
requestMetricsDashboard
'
);
const
params
=
{};
if
(
state
.
timeRange
)
{
const
{
start
,
end
}
=
convertToFixedRange
(
state
.
timeRange
);
params
.
start
=
start
;
params
.
end
=
end
;
}
if
(
state
.
currentDashboard
)
{
...
...
@@ -77,5 +88,4 @@
if
(
state
.
currentDashboard
)
{
// eslint-disable-next-line no-param-reassign
params
.
dashboard
=
state
.
currentDashboard
;
}
...
...
app/assets/javascripts/monitoring/stores/mutation_types.js
View file @
de399fb1
...
...
@@ -14,7 +14,7 @@
export
const
RECEIVE_METRIC_RESULT_SUCCESS
=
'
RECEIVE_METRIC_RESULT_SUCCESS
'
;
export
const
RECEIVE_METRIC_RESULT_FAILURE
=
'
RECEIVE_METRIC_RESULT_FAILURE
'
;
export
const
SET_TIME_
WINDOW
=
'
SET_TIME_
WINDOW
'
;
export
const
SET_TIME_
RANGE
=
'
SET_TIME_
RANGE
'
;
export
const
SET_ALL_DASHBOARDS
=
'
SET_ALL_DASHBOARDS
'
;
export
const
SET_ENDPOINTS
=
'
SET_ENDPOINTS
'
;
export
const
SET_GETTING_STARTED_EMPTY_STATE
=
'
SET_GETTING_STARTED_EMPTY_STATE
'
;
...
...
app/assets/javascripts/monitoring/stores/mutations.js
View file @
de399fb1
...
...
@@ -182,6 +182,10 @@
state
.
dashboardsEndpoint
=
endpoints
.
dashboardsEndpoint
;
state
.
currentDashboard
=
endpoints
.
currentDashboard
;
state
.
projectPath
=
endpoints
.
projectPath
;
state
.
logsPath
=
endpoints
.
logsPath
||
state
.
logsPath
;
},
[
types
.
SET_TIME_RANGE
](
state
,
timeRange
)
{
state
.
timeRange
=
timeRange
;
},
[
types
.
SET_GETTING_STARTED_EMPTY_STATE
](
state
)
{
state
.
emptyState
=
'
gettingStarted
'
;
...
...
app/assets/javascripts/monitoring/stores/state.js
View file @
de399fb1
import
invalidUrl
from
'
~/lib/utils/invalid_url
'
;
export
default
()
=>
({
// API endpoints
metricsEndpoint
:
null
,
deploymentsEndpoint
:
null
,
dashboardEndpoint
:
invalidUrl
,
...
...
@@ -4,6 +5,12 @@
metricsEndpoint
:
null
,
deploymentsEndpoint
:
null
,
dashboardEndpoint
:
invalidUrl
,
// Dashboard request parameters
timeRange
:
null
,
currentDashboard
:
null
,
// Dashboard data
emptyState
:
'
gettingStarted
'
,
showEmptyState
:
true
,
showErrorBanner
:
true
,
...
...
@@ -7,7 +14,6 @@
emptyState
:
'
gettingStarted
'
,
showEmptyState
:
true
,
showErrorBanner
:
true
,
dashboard
:
{
panel_groups
:
[],
},
...
...
@@ -11,4 +17,5 @@
dashboard
:
{
panel_groups
:
[],
},
allDashboards
:
[],
...
...
@@ -14,5 +21,6 @@
// Other project data
deploymentData
:
[],
environments
:
[],
environmentsSearchTerm
:
''
,
environmentsLoading
:
false
,
...
...
@@ -15,7 +23,7 @@
deploymentData
:
[],
environments
:
[],
environmentsSearchTerm
:
''
,
environmentsLoading
:
false
,
allDashboards
:
[],
currentDashboard
:
null
,
// GitLab paths to other pages
projectPath
:
null
,
...
...
@@ -21,2 +29,3 @@
projectPath
:
null
,
logsPath
:
invalidUrl
,
});
app/assets/javascripts/monitoring/utils.js
View file @
de399fb1
...
...
@@ -103,8 +103,9 @@
/**
* Returns a time range from the current URL params
*
* @returns {Object} The time range defined by the
* current URL, reading from `window.location.search`
* @returns {Object|null} The time range defined by the
* current URL, reading from search query or `window.location.search`.
* Returns `null` if no parameters form a time range.
*/
export
const
timeRangeFromUrl
=
(
search
=
window
.
location
.
search
)
=>
{
const
params
=
queryToObject
(
search
);
...
...
app/assets/stylesheets/framework/typography.scss
View file @
de399fb1
...
...
@@ -102,6 +102,7 @@
padding-bottom
:
0
.3em
;
border-bottom
:
1px
solid
$white-dark
;
color
:
$gl-text-color
;
overflow
:
hidden
;
&
:first-child
{
margin-top
:
0
;
...
...
@@ -115,6 +116,7 @@
padding-bottom
:
0
.3em
;
border-bottom
:
1px
solid
$white-dark
;
color
:
$gl-text-color
;
overflow
:
hidden
;
}
h3
{
...
...
app/controllers/admin/services_controller.rb
View file @
de399fb1
...
...
@@ -7,7 +7,7 @@
before_action
:service
,
only:
[
:edit
,
:update
]
def
index
@services
=
services_templat
es
@services
=
instance_level_servic
es
end
def
edit
...
...
@@ -19,7 +19,7 @@
def
update
if
service
.
update
(
service_params
[
:service
])
Propagate
ServiceTemplat
eWorker
.
perform_async
(
service
.
id
)
if
service
.
active?
Propagate
InstanceLevelServic
eWorker
.
perform_async
(
service
.
id
)
if
service
.
active?
redirect_to
admin_application_settings_services_path
,
notice:
'Application settings saved successfully'
...
...
@@ -31,5 +31,5 @@
private
# rubocop: disable CodeReuse/ActiveRecord
def
services_templat
es
def
instance_level_servic
es
Service
.
available_services_names
.
map
do
|
service_name
|
...
...
@@ -35,9 +35,9 @@
Service
.
available_services_names
.
map
do
|
service_name
|
service
_template
=
"
#{
service_name
}
_service"
.
camelize
.
constantize
service
_template
.
where
(
templat
e:
true
).
first_or_create
service
=
"
#{
service_name
}
_service"
.
camelize
.
constantize
service
.
where
(
instanc
e:
true
).
first_or_create
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def
service
...
...
@@ -38,10 +38,10 @@
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def
service
@service
||=
Service
.
where
(
id:
params
[
:id
],
templat
e:
true
).
first
@service
||=
Service
.
where
(
id:
params
[
:id
],
instanc
e:
true
).
first
end
# rubocop: enable CodeReuse/ActiveRecord
...
...
app/controllers/concerns/lfs_request.rb
View file @
de399fb1
...
...
@@ -112,10 +112,6 @@
has_authentication_ability?
(
:build_download_code
)
&&
can?
(
user
,
:build_download_code
,
project
)
end
def
storage_project
@storage_project
||=
project
.
lfs_storage_project
end
def
objects
@objects
||=
(
params
[
:objects
]
||
[]).
to_a
end
...
...
app/controllers/repositories/lfs_storage_controller.rb
View file @
de399fb1
...
...
@@ -80,5 +80,4 @@
LfsObject
.
create!
(
oid:
oid
,
size:
size
,
file:
uploaded_file
)
end
# rubocop: disable CodeReuse/ActiveRecord
def
link_to_project!
(
object
)
...
...
@@ -84,5 +83,8 @@
def
link_to_project!
(
object
)
if
object
&&
!
object
.
projects
.
exists?
(
storage_project
.
id
)
object
.
lfs_objects_projects
.
create!
(
project:
storage_project
)
end
return
unless
object
LfsObjectsProject
.
safe_find_or_create_by!
(
project:
project
,
lfs_object:
object
)
end
...
...
@@ -88,4 +90,3 @@
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
app/models/concerns/sortable.rb
View file @
de399fb1
...
...
@@ -8,13 +8,13 @@
extend
ActiveSupport
::
Concern
included
do
scope
:with_order_id_desc
,
->
{
order
(
id: :
desc
)
}
scope
:order_id_desc
,
->
{
reorder
(
id: :
desc
)
}
scope
:order_id_asc
,
->
{
reorder
(
id: :
asc
)
}
scope
:order_created_desc
,
->
{
reorder
(
created_at
: :
desc
)
}
scope
:order_created_asc
,
->
{
reorder
(
created_at
: :
asc
)
}
scope
:order_updated_desc
,
->
{
reorder
(
updated_at
: :
desc
)
}
scope
:order_updated_asc
,
->
{
reorder
(
updated_at
: :
asc
)
}
scope
:with_order_id_desc
,
->
{
order
(
self
.
arel_table
[
'id'
].
desc
)
}
scope
:order_id_desc
,
->
{
reorder
(
self
.
arel_table
[
'id'
].
desc
)
}
scope
:order_id_asc
,
->
{
reorder
(
self
.
arel_table
[
'id'
].
asc
)
}
scope
:order_created_desc
,
->
{
reorder
(
self
.
arel_table
[
'
created_at
'
].
desc
)
}
scope
:order_created_asc
,
->
{
reorder
(
self
.
arel_table
[
'
created_at
'
].
asc
)
}
scope
:order_updated_desc
,
->
{
reorder
(
self
.
arel_table
[
'
updated_at
'
].
desc
)
}
scope
:order_updated_asc
,
->
{
reorder
(
self
.
arel_table
[
'
updated_at
'
].
asc
)
}
scope
:order_name_asc
,
->
{
reorder
(
Arel
::
Nodes
::
Ascending