voluntary 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/app/assets/javascripts/voluntary/application.js +1 -0
- data/app/assets/javascripts/voluntary/optional_lib/jquery.location_picker.js +405 -0
- data/app/controllers/concerns/voluntary/v1/base_controller.rb +6 -0
- data/app/controllers/stories_controller.rb +1 -1
- data/app/controllers/tasks_controller.rb +23 -32
- data/app/controllers/users_controller.rb +12 -5
- data/app/controllers/workflow/products_controller.rb +2 -0
- data/app/models/story.rb +8 -0
- data/app/models/user.rb +1 -1
- data/app/presenters/resources/user/form_presenter.rb +1 -1
- data/app/views/layouts/application.html.erb +2 -1
- data/app/views/tasks/_form.html.erb +5 -0
- data/app/views/tasks/edit.html.erb +3 -0
- data/app/views/tasks/index.html.erb +5 -0
- data/app/views/tasks/new.html.erb +3 -0
- data/app/views/tasks/show.html.erb +5 -5
- data/app/views/users/_form.html.erb +2 -0
- data/config/locales/general/en.yml +12 -1
- data/config/locales/resources/task/en.yml +6 -0
- data/db/migrate/20150808155719_add_api_key_to_users.rb +5 -1
- data/db/migrate/20151022163015_add_timezone_to_users.rb +5 -0
- data/lib/vendors/simple_navigation/renderer/twitter_sidenav.rb +2 -4
- data/lib/voluntary.rb +2 -0
- data/lib/voluntary/version.rb +1 -1
- metadata +27 -8
- data/app/assets/javascripts/voluntary/lib/moment.js +0 -3195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e733d65d2735dc9749ec9be14d46cdb88c56ca85
|
4
|
+
data.tar.gz: b14d3f3b9dfc746dd1f906208610d47f2b0cdb1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdb14cddd47477319fb349c3fea0ae821e3b070e95db6030fb39b6f5efab15cf512a17b1e249615658beea7c34586bea1e03be65539ada401e88eadf46794dc4
|
7
|
+
data.tar.gz: 1b7d07d75916d31fd56d77f4762c60a8f6d975b517d49d29b71e361aba451cd2b8fd80ea2d217b9c7f5410c2944c0d1023d9fbfcd082358b346e1441ccbbd965
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
## unreleased ##
|
2
2
|
|
3
|
+
## 0.7.0 (October 29, 2015) ##
|
4
|
+
|
5
|
+
* [#94](https://github.com/volontariat/voluntary/issues/94) Customization for classified advertisement product.
|
6
|
+
* [#97](https://github.com/volontariat/voluntary/issues/97) User form does not show errors like country can't be blank.
|
7
|
+
* [#96](https://github.com/volontariat/voluntary/issues/96) Regard user's timezone.
|
8
|
+
* [BUGFIX] Remove obsolete scroll bar from modals.
|
9
|
+
|
3
10
|
## 0.6.0 (October 1, 2015) ##
|
4
11
|
|
5
12
|
* [#93](https://github.com/volontariat/voluntary/issues/93) Core customizations for survey product.
|
@@ -0,0 +1,405 @@
|
|
1
|
+
(function ( $ ) {
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Holds google map object and related utility entities.
|
5
|
+
* @constructor
|
6
|
+
*/
|
7
|
+
function GMapContext(domElement, options) {
|
8
|
+
var _map = new google.maps.Map(domElement, options);
|
9
|
+
var _marker = new google.maps.Marker({
|
10
|
+
position: new google.maps.LatLng(54.19335, -3.92695),
|
11
|
+
map: _map,
|
12
|
+
title: "Drag Me",
|
13
|
+
draggable: options.draggable
|
14
|
+
});
|
15
|
+
return {
|
16
|
+
map: _map,
|
17
|
+
marker: _marker,
|
18
|
+
circle: null,
|
19
|
+
location: _marker.position,
|
20
|
+
radius: options.radius,
|
21
|
+
locationName: options.locationName,
|
22
|
+
addressComponents: {
|
23
|
+
formatted_address: null,
|
24
|
+
addressLine1: null,
|
25
|
+
addressLine2: null,
|
26
|
+
streetName: null,
|
27
|
+
streetNumber: null,
|
28
|
+
city: null,
|
29
|
+
district: null,
|
30
|
+
state: null,
|
31
|
+
stateOrProvince: null
|
32
|
+
},
|
33
|
+
settings: options.settings,
|
34
|
+
domContainer: domElement,
|
35
|
+
geodecoder: new google.maps.Geocoder()
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
// Utility functions for Google Map Manipulations
|
40
|
+
var GmUtility = {
|
41
|
+
/**
|
42
|
+
* Draw a circle over the the map. Returns circle object.
|
43
|
+
* Also writes new circle object in gmapContext.
|
44
|
+
*
|
45
|
+
* @param center - LatLng of the center of the circle
|
46
|
+
* @param radius - radius in meters
|
47
|
+
* @param gmapContext - context
|
48
|
+
* @param options
|
49
|
+
*/
|
50
|
+
drawCircle: function(gmapContext, center, radius, options) {
|
51
|
+
if (gmapContext.circle != null) {
|
52
|
+
gmapContext.circle.setMap(null);
|
53
|
+
}
|
54
|
+
if (radius > 0) {
|
55
|
+
radius *= 1;
|
56
|
+
options = $.extend({
|
57
|
+
strokeColor: "#0000FF",
|
58
|
+
strokeOpacity: 0.35,
|
59
|
+
strokeWeight: 2,
|
60
|
+
fillColor: "#0000FF",
|
61
|
+
fillOpacity: 0.20
|
62
|
+
}, options);
|
63
|
+
options.map = gmapContext.map;
|
64
|
+
options.radius = radius;
|
65
|
+
options.center = center;
|
66
|
+
gmapContext.circle = new google.maps.Circle(options);
|
67
|
+
return gmapContext.circle;
|
68
|
+
}
|
69
|
+
return null;
|
70
|
+
},
|
71
|
+
/**
|
72
|
+
*
|
73
|
+
* @param gMapContext
|
74
|
+
* @param location
|
75
|
+
* @param callback
|
76
|
+
*/
|
77
|
+
setPosition: function(gMapContext, location, callback) {
|
78
|
+
gMapContext.location = location;
|
79
|
+
gMapContext.marker.setPosition(location);
|
80
|
+
gMapContext.map.panTo(location);
|
81
|
+
this.drawCircle(gMapContext, location, gMapContext.radius, {});
|
82
|
+
if (gMapContext.settings.enableReverseGeocode) {
|
83
|
+
gMapContext.geodecoder.geocode({latLng: gMapContext.location}, function(results, status){
|
84
|
+
if (status == google.maps.GeocoderStatus.OK && results.length > 0){
|
85
|
+
gMapContext.locationName = results[0].formatted_address;
|
86
|
+
gMapContext.addressComponents =
|
87
|
+
GmUtility.address_component_from_google_geocode(results[0].address_components);
|
88
|
+
}
|
89
|
+
if (callback) {
|
90
|
+
callback.call(this, gMapContext);
|
91
|
+
}
|
92
|
+
});
|
93
|
+
} else {
|
94
|
+
if (callback) {
|
95
|
+
callback.call(this, gMapContext);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
},
|
100
|
+
locationFromLatLng: function(lnlg) {
|
101
|
+
return {latitude: lnlg.lat(), longitude: lnlg.lng()}
|
102
|
+
},
|
103
|
+
address_component_from_google_geocode: function(address_components) {
|
104
|
+
var result = {};
|
105
|
+
for (var i = address_components.length-1; i>=0; i--) {
|
106
|
+
var component = address_components[i];
|
107
|
+
// Postal code
|
108
|
+
if (component.types.indexOf('postal_code') >= 0) {
|
109
|
+
result.postalCode = component.short_name;
|
110
|
+
}
|
111
|
+
// Street number
|
112
|
+
else if (component.types.indexOf('street_number') >= 0) {
|
113
|
+
result.streetNumber = component.short_name;
|
114
|
+
}
|
115
|
+
// Street name
|
116
|
+
else if (component.types.indexOf('route') >= 0) {
|
117
|
+
result.streetName = component.short_name;
|
118
|
+
}
|
119
|
+
// City
|
120
|
+
else if (component.types.indexOf('locality') >= 0) {
|
121
|
+
result.city = component.short_name;
|
122
|
+
}
|
123
|
+
// District
|
124
|
+
else if (component.types.indexOf('sublocality') >= 0) {
|
125
|
+
result.district = component.short_name;
|
126
|
+
}
|
127
|
+
// State \ Province
|
128
|
+
else if (component.types.indexOf('administrative_area_level_1') >= 0) {
|
129
|
+
result.stateOrProvince = component.short_name;
|
130
|
+
}
|
131
|
+
// State \ Province
|
132
|
+
else if (component.types.indexOf('country') >= 0) {
|
133
|
+
result.country = component.short_name;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
result.addressLine1 = [result.streetNumber, result.streetName].join(' ').trim();
|
137
|
+
result.addressLine2 = '';
|
138
|
+
return result;
|
139
|
+
}
|
140
|
+
};
|
141
|
+
|
142
|
+
function isPluginApplied(domObj) {
|
143
|
+
return getContextForElement(domObj) != undefined;
|
144
|
+
}
|
145
|
+
|
146
|
+
function getContextForElement(domObj) {
|
147
|
+
return $(domObj).data("locationpicker");
|
148
|
+
}
|
149
|
+
|
150
|
+
function updateInputValues(inputBinding, gmapContext){
|
151
|
+
if (!inputBinding) return;
|
152
|
+
var currentLocation = GmUtility.locationFromLatLng(gmapContext.location);
|
153
|
+
if (inputBinding.latitudeInput) {
|
154
|
+
inputBinding.latitudeInput.val(currentLocation.latitude).change();
|
155
|
+
}
|
156
|
+
if (inputBinding.longitudeInput) {
|
157
|
+
inputBinding.longitudeInput.val(currentLocation.longitude).change();
|
158
|
+
}
|
159
|
+
if (inputBinding.radiusInput) {
|
160
|
+
inputBinding.radiusInput.val(gmapContext.radius).change();
|
161
|
+
}
|
162
|
+
if (inputBinding.locationNameInput) {
|
163
|
+
inputBinding.locationNameInput.val(gmapContext.locationName).change();
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
function setupInputListenersInput(inputBinding, gmapContext) {
|
168
|
+
if (inputBinding) {
|
169
|
+
if (inputBinding.radiusInput){
|
170
|
+
inputBinding.radiusInput.on("change", function(e) {
|
171
|
+
if (!e.originalEvent) { return }
|
172
|
+
gmapContext.radius = $(this).val();
|
173
|
+
GmUtility.setPosition(gmapContext, gmapContext.location, function(context){
|
174
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
175
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
176
|
+
});
|
177
|
+
});
|
178
|
+
}
|
179
|
+
if (inputBinding.locationNameInput && gmapContext.settings.enableAutocomplete) {
|
180
|
+
var blur = false;
|
181
|
+
gmapContext.autocomplete = new google.maps.places.Autocomplete(inputBinding.locationNameInput.get(0));
|
182
|
+
google.maps.event.addListener(gmapContext.autocomplete, 'place_changed', function() {
|
183
|
+
blur = false;
|
184
|
+
var place = gmapContext.autocomplete.getPlace();
|
185
|
+
if (!place.geometry) {
|
186
|
+
gmapContext.settings.onlocationnotfound(place.name);
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
GmUtility.setPosition(gmapContext, place.geometry.location, function(context) {
|
190
|
+
updateInputValues(inputBinding, context);
|
191
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
192
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
193
|
+
});
|
194
|
+
});
|
195
|
+
if(gmapContext.settings.enableAutocompleteBlur) {
|
196
|
+
inputBinding.locationNameInput.on("change", function(e) {
|
197
|
+
if (!e.originalEvent) { return }
|
198
|
+
blur = true;
|
199
|
+
});
|
200
|
+
inputBinding.locationNameInput.on("blur", function(e) {
|
201
|
+
if (!e.originalEvent) { return }
|
202
|
+
setTimeout(function() {
|
203
|
+
var address = $(inputBinding.locationNameInput).val();
|
204
|
+
if (address.length > 5 && blur) {
|
205
|
+
blur = false;
|
206
|
+
gmapContext.geodecoder.geocode({'address': address}, function(results, status) {
|
207
|
+
if(status == google.maps.GeocoderStatus.OK && results && results.length) {
|
208
|
+
GmUtility.setPosition(gmapContext, results[0].geometry.location, function(context) {
|
209
|
+
updateInputValues(inputBinding, context);
|
210
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
211
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
212
|
+
});
|
213
|
+
}
|
214
|
+
});
|
215
|
+
}
|
216
|
+
}, 1000);
|
217
|
+
});
|
218
|
+
}
|
219
|
+
}
|
220
|
+
if (inputBinding.latitudeInput) {
|
221
|
+
inputBinding.latitudeInput.on("change", function(e) {
|
222
|
+
if (!e.originalEvent) { return }
|
223
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng($(this).val(), gmapContext.location.lng()), function(context){
|
224
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
225
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
226
|
+
});
|
227
|
+
});
|
228
|
+
}
|
229
|
+
if (inputBinding.longitudeInput) {
|
230
|
+
inputBinding.longitudeInput.on("change", function(e) {
|
231
|
+
if (!e.originalEvent) { return }
|
232
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(gmapContext.location.lat(), $(this).val()), function(context){
|
233
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
234
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
235
|
+
});
|
236
|
+
});
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
function autosize(gmapContext) {
|
242
|
+
google.maps.event.trigger(gmapContext.map, 'resize');
|
243
|
+
setTimeout(function() {
|
244
|
+
gmapContext.map.setCenter(gmapContext.marker.position);
|
245
|
+
}, 300);
|
246
|
+
}
|
247
|
+
|
248
|
+
function updateMap(gmapContext, $target, options) {
|
249
|
+
var settings = $.extend({}, $.fn.locationpicker.defaults, options ),
|
250
|
+
latNew = settings.location.latitude,
|
251
|
+
lngNew = settings.location.longitude,
|
252
|
+
radiusNew = settings.radius,
|
253
|
+
latOld = gmapContext.settings.location.latitude,
|
254
|
+
lngOld = gmapContext.settings.location.longitude,
|
255
|
+
radiusOld = gmapContext.settings.radius;
|
256
|
+
|
257
|
+
if (latNew == latOld && lngNew == lngOld && radiusNew == radiusOld)
|
258
|
+
return;
|
259
|
+
|
260
|
+
gmapContext.settings.location.latitude = latNew;
|
261
|
+
gmapContext.settings.location.longitude = lngNew;
|
262
|
+
gmapContext.radius = radiusNew;
|
263
|
+
|
264
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(gmapContext.settings.location.latitude, gmapContext.settings.location.longitude), function(context){
|
265
|
+
setupInputListenersInput(gmapContext.settings.inputBinding, gmapContext);
|
266
|
+
context.settings.oninitialized($target);
|
267
|
+
});
|
268
|
+
}
|
269
|
+
/**
|
270
|
+
* Initializeialization:
|
271
|
+
* $("#myMap").locationpicker(options);
|
272
|
+
* @param options
|
273
|
+
* @param params
|
274
|
+
* @returns {*}
|
275
|
+
*/
|
276
|
+
$.fn.locationpicker = function( options, params ) {
|
277
|
+
if (typeof options == 'string') { // Command provided
|
278
|
+
var _targetDomElement = this.get(0);
|
279
|
+
// Plug-in is not applied - nothing to do.
|
280
|
+
if (!isPluginApplied(_targetDomElement)) return;
|
281
|
+
var gmapContext = getContextForElement(_targetDomElement);
|
282
|
+
switch (options) {
|
283
|
+
case "location":
|
284
|
+
if (params == undefined) { // Getter
|
285
|
+
var location = GmUtility.locationFromLatLng(gmapContext.location);
|
286
|
+
location.radius = gmapContext.radius;
|
287
|
+
location.name = gmapContext.locationName;
|
288
|
+
return location;
|
289
|
+
} else { // Setter
|
290
|
+
if (params.radius) {
|
291
|
+
gmapContext.radius = params.radius;
|
292
|
+
}
|
293
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(params.latitude, params.longitude), function(gmapContext) {
|
294
|
+
updateInputValues(gmapContext.settings.inputBinding, gmapContext);
|
295
|
+
});
|
296
|
+
}
|
297
|
+
break;
|
298
|
+
case "subscribe":
|
299
|
+
/**
|
300
|
+
* Provides interface for subscribing for GoogleMap events.
|
301
|
+
* See Google API documentation for details.
|
302
|
+
* Parameters:
|
303
|
+
* - event: string, name of the event
|
304
|
+
* - callback: function, callback function to be invoked
|
305
|
+
*/
|
306
|
+
if (params == undefined) { // Getter is not available
|
307
|
+
return null;
|
308
|
+
} else {
|
309
|
+
var event = params.event;
|
310
|
+
var callback = params.callback;
|
311
|
+
if (!event || ! callback) {
|
312
|
+
console.error("LocationPicker: Invalid arguments for method \"subscribe\"")
|
313
|
+
return null;
|
314
|
+
}
|
315
|
+
google.maps.event.addListener(gmapContext.map, event, callback);
|
316
|
+
}
|
317
|
+
break;
|
318
|
+
case "map":
|
319
|
+
/**
|
320
|
+
* Returns object which allows access actual google widget and marker paced on it.
|
321
|
+
* Structure: {
|
322
|
+
* map: Instance of the google map widget
|
323
|
+
* marker: marker placed on map
|
324
|
+
* }
|
325
|
+
*/
|
326
|
+
if (params == undefined) { // Getter
|
327
|
+
var locationObj = GmUtility.locationFromLatLng(gmapContext.location);
|
328
|
+
locationObj.formattedAddress = gmapContext.locationName;
|
329
|
+
locationObj.addressComponents = gmapContext.addressComponents;
|
330
|
+
return {
|
331
|
+
map: gmapContext.map,
|
332
|
+
marker: gmapContext.marker,
|
333
|
+
location: locationObj
|
334
|
+
}
|
335
|
+
} else { // Setter is not available
|
336
|
+
return null;
|
337
|
+
}
|
338
|
+
case "autosize":
|
339
|
+
autosize(gmapContext);
|
340
|
+
return this;
|
341
|
+
}
|
342
|
+
return null;
|
343
|
+
}
|
344
|
+
return this.each(function() {
|
345
|
+
var $target = $(this);
|
346
|
+
// If plug-in hasn't been applied before - initialize, otherwise - skip
|
347
|
+
if (isPluginApplied(this)){
|
348
|
+
updateMap(getContextForElement(this), $(this), options);
|
349
|
+
return;
|
350
|
+
}
|
351
|
+
// Plug-in initialization is required
|
352
|
+
// Defaults
|
353
|
+
var settings = $.extend({}, $.fn.locationpicker.defaults, options );
|
354
|
+
// Initialize
|
355
|
+
var gmapContext = new GMapContext(this, {
|
356
|
+
zoom: settings.zoom,
|
357
|
+
center: new google.maps.LatLng(settings.location.latitude, settings.location.longitude),
|
358
|
+
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
359
|
+
mapTypeControl: false,
|
360
|
+
disableDoubleClickZoom: false,
|
361
|
+
scrollwheel: settings.scrollwheel,
|
362
|
+
streetViewControl: false,
|
363
|
+
radius: settings.radius,
|
364
|
+
locationName: settings.locationName,
|
365
|
+
settings: settings,
|
366
|
+
draggable: settings.draggable
|
367
|
+
});
|
368
|
+
$target.data("locationpicker", gmapContext);
|
369
|
+
// Subscribe GMap events
|
370
|
+
google.maps.event.addListener(gmapContext.marker, "dragend", function(event) {
|
371
|
+
GmUtility.setPosition(gmapContext, gmapContext.marker.position, function(context){
|
372
|
+
var currentLocation = GmUtility.locationFromLatLng(gmapContext.location);
|
373
|
+
context.settings.onchanged.apply(gmapContext.domContainer, [currentLocation, context.radius, true]);
|
374
|
+
updateInputValues(gmapContext.settings.inputBinding, gmapContext);
|
375
|
+
});
|
376
|
+
});
|
377
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(settings.location.latitude, settings.location.longitude), function(context){
|
378
|
+
updateInputValues(settings.inputBinding, gmapContext);
|
379
|
+
// Set input bindings if needed
|
380
|
+
setupInputListenersInput(settings.inputBinding, gmapContext);
|
381
|
+
context.settings.oninitialized($target);
|
382
|
+
});
|
383
|
+
});
|
384
|
+
};
|
385
|
+
$.fn.locationpicker.defaults = {
|
386
|
+
location: {latitude: 40.7324319, longitude: -73.82480777777776},
|
387
|
+
locationName: "",
|
388
|
+
radius: 500,
|
389
|
+
zoom: 15,
|
390
|
+
scrollwheel: true,
|
391
|
+
inputBinding: {
|
392
|
+
latitudeInput: null,
|
393
|
+
longitudeInput: null,
|
394
|
+
radiusInput: null,
|
395
|
+
locationNameInput: null
|
396
|
+
},
|
397
|
+
enableAutocomplete: false,
|
398
|
+
enableAutocompleteBlur: false,
|
399
|
+
enableReverseGeocode: true,
|
400
|
+
draggable: true,
|
401
|
+
onchanged: function(currentLocation, radius, isMarkerDropped) {},
|
402
|
+
onlocationnotfound: function(locationName) {},
|
403
|
+
oninitialized: function (component) {}
|
404
|
+
}
|
405
|
+
}( jQuery ));
|
@@ -10,6 +10,8 @@ module Voluntary
|
|
10
10
|
|
11
11
|
helper_method :parent, :application_navigation, :navigation_product_path, :navigation_product_name, :voluntary_application_stylesheets
|
12
12
|
helper_method :voluntary_application_javascripts
|
13
|
+
|
14
|
+
before_filter :set_timezone
|
13
15
|
end
|
14
16
|
|
15
17
|
def voluntary_application_stylesheets
|
@@ -77,6 +79,10 @@ module Voluntary
|
|
77
79
|
|
78
80
|
private
|
79
81
|
|
82
|
+
def set_timezone
|
83
|
+
Time.zone = current_user.try(:timezone).present? ? current_user.timezone : 'UTC'
|
84
|
+
end
|
85
|
+
|
80
86
|
def current_namespace
|
81
87
|
controller_name_segments = params[:controller].split('/')
|
82
88
|
controller_name_segments.pop
|