What's the recommended way to have mountable route handlers in Suave, as the routers in Express.js?
On a high level, what I want to achieve is a way to define my routings using the relative paths, and mount them under a parent path. E.g, I want to write a REST API with path handlers:
channel/ -> handle channel
users/ -> handle users
...
Then mount them under version 1 path v1, so that they are accessible with:
/v1/channel/
/v1/users/
In express.js, I can do this with the following code. How can I achieve something functionally similar in Suave? I understand Suave tackles the problem in a totally different fashion, but just wondering what's the approach Suave takes to solve this in a modular way.
var express = require('express');
var app = express();
var routerV1 = express
.Router()
.all('/:channel', (req, res) => {
var channel = req.params.channel,
msg = req.query.msg;
var body = handleRequest(channel, msg);
res.send(body);
});
app.use('/v1', routerV1);
Suave currently does not provide such a facility.
It should be easy to implement such a thing though; see for example the following blog post;
Suave.IO vdir to run choose routes relative to a path
Related
I have the following services that return Bookshelf model data. The server is built on express. My core question is: I want to write tests for the services. Secondarily, I'm not sure if the services tests should include interaction with db; as you can see below, the services are currently intertwined with express.
// services.js
import Region from "../../models/region";
import Subregion from "../../models/subregion";
import bookshelf from "../../bookshelf.config";
/** Regions **/
export const getRegions = (req, res, next) => {
Region.forge()
.fetchAll()
.then(regions => {
log.info("Got all regions");
res.status(200).json(regions);
})
.catch(next);
};
/** Subegions **/
export const getSubregions = (req, res, next) => {
Subregion.forge()
.fetchAll({
columns: ["id", "name"],
})
.then(subregions => {
res.status(200).json(subregions);
})
.catch(next);
};
Questions
1. Whats is the proper way to test a function like getRegions?
2. Do best practices require getRegions and getSubregions to be extracted from the express context?
You have to analyze what your function does and what is the best way to test it. Looking at the getRegions function it just fetches all models of a certain type and returns the result as JSON to the user.
Given this, you have very little logic of your own, it's just a little bit of glue between two modules, so it doesn't make sense to unit test that function because you would just be testing if the used modules (Bookshelf and Express) are working properly, which should be beyond the scope of your project.
However, you probably do want to test if your API is responding correctly with various user inputs, so you should do some integration testing with something like SuperTest.
As for testing with an actual database or mocking it I think that's just a matter of personal opinion or project goals.
getRegions is just a function. You'd test it like a normal function. You would need to mock the Express' res, req, and next objects. In addition to the Express objects, you would need to mock Bookshelf/knex as well since you don't want to depend on an actual database. See this answer for bookshelf testing.
It is already extracted from Express' context since you defined it as a function. If you had defined it as app.post('/example' (req, res, next) => {}), then that would be coupled with Express.
I'm converting a globals based real-time Ember app to an es6 based app that utilizes ember-cli. In my app I need to know the current route fairly often. In the globals version, I was doing this.
Globals Pattern
var MyApp = Ember.Application.create({
currentRoute : ''
});
MyApp.Route = Ember.Route.extend({
actions : {
didTransition : function () {
MyApp.set('currentRoute', this);
}
}
});
I could then do MyApp.get('currentRoute') from within my session or offline controllers when determining how / where to transition when certain events occurred.
When using ember-cli, I import the app to be able to reference it from the necessary controllers.
import MyApp from "../app";
But it turns out that MyApp.currentRoute, MyApp.get, and MyApp.set are all undefined.
Part of me thinks this is a bug in ember-cli that the application instance no longer has bound getters and setters. Part of me realizes it's not a great practice to store things on the application instance either.
I could get around this issue by converting all instances of MyApp.get and MyApp.set to Ember.get(MyApp, ...) and Ember.set(MyApp, ...) respectively, but I thought I'd ask here first as this seems to either be an issue with Ember-Cli or else something where there's a better recommended way to achieve what I need.
If you look at app.js (what you are importing), it is not your application instance, it is exporting a subclass of Ember.Application. That's why get et al are not available on it.
var App = Ember.Application.extend({ <----- NOT create
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver: Resolver
});
export default App;
To get the actual application instance from within your route use:
Ember.getOwner(this).lookup('application:main')
I asked a question similar to this, here, specifically about how to implement specific settings for a specific controller. In short, I wanted to implement checkInSettings for the whole CheckInController so that my index, settings, and reports templates and controllers have access to the checkInSettings.
I did get my answer to that; however, I think that specific settings might be limiting and it would be better served by making a settings object or store, and defining something like settings.checkIn for the check in settings.
I've looked for resources online but haven't come up with many answers... So, how should I best go about creating application wide settings, with sub settings for specific areas of my app?
A note: I would like to refrain from using Ember Data since it is not Production Ready yet, and this app will eventually be consumer facing.
Thank you!
Ember Data is a different beast. Store them on the application controller. Or if you don't want o clutter the application controller, create a singleton instance of a settings controller and store them there. (The same thing can be done just on the application controller, just use application instead of settings).
App.SettingsController = Ember.Controller.extend({
someSettingOn: false,
someOtherSetting: null
});
And then in other routes/controllers:
App.AnyRoute = Ember.Route.extend({
anyMethod: function(){
this.controllerFor('settings').toggleProperty('someSettingOn');
}
})
App.AnyController = Ember.Controller.extend({
needs: ['settings'],
anyMethod: function(){
var setting = this.get('controllers.settings.someOtherSetting');
console.log(setting);
},
anyProperty: function(){
if(this.get('controllers.settings.someSettingOn')){
return 'yes';
}
return 'no';
}.property('controllers.settings.someSettingOn')
})
I would like to have a route substate not show up in the URL, but still be able to take advantage of having a route class on which I can define renderTemplate, model, setupController, etc. hooks. Is this possible with the v2 router? I am using Ember release candidate 2.
Here's an example.
Suppose I have the routes:
/exercise/:exercise_id
/exercise/:exercise_id/correct
/exercise/:exercise_id/incorrect
I would like all of these to show up in the URL as:
/exercise/:exercise_id
As I don't want the student to just directly type in /correct onto the end of the ULR and get to the correct answer. And although I have a way to prevent that from working, the full route still shows up in the URL. From the student's perspective, I only want them to think about the state as /exercise/:exercise_id.
Of course I could just store the state correct vs. incorrect in some controller variable, but then I loose the convenience of having route classes, ExerciseCorrectRoute and ExerciseIncorrectRoute, which I want to behave differently, and so the hooks, like renderTemplate and setupController, are nice to have defined cleanly in separate places.
Thoughts?
Kevin
UPDATE:
I went with Dan Gebhardt's suggestion because I like to keep things as much as possible within the framework's considered design cases, as this seems to reduce headaches given Ember is still evolving. Also I didn't get a chance to try out inDream's hack.
Although I still think it would be nice if the router added a feature to mask substates from the URL.
Every route must be associated with a URL for Ember's current router.
Instead of using multiple routes, I'd recommend that you use conditionals in your exercise template to call the appropriate {{render}} based on the state of the exercise. In this way you can still maintain separate templates and controllers for each state.
You can reference to my answer in Ember.js - Prevent re-render when switching route.
Reopen the location API you're using and set window.suppressUpdateURL to true if you want to handle the state manually.
Ember.HistoryLocation:
Ember.HistoryLocation.reopen({
onUpdateURL: function(callback) {
var guid = Ember.guidFor(this),
self = this;
Ember.$(window).bind('popstate.ember-location-'+guid, function(e) {
if(window.suppressUpdateURL)return;
// Ignore initial page load popstate event in Chrome
if(!popstateFired) {
popstateFired = true;
if (self.getURL() === self._initialUrl) { return; }
}
callback(self.getURL());
});
}
});
Ember.HashLocation:
Ember.HashLocation.reopen({
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
if(window.suppressUpdateURL)return;
Ember.run(function() {
var path = location.hash.substr(1);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(path);
});
});
}
});
Curious about the proper procedure, or at least common procedure for using sproutcore-routing.
In the read me there it shows this basic example for routing:
SC.routes.add(':controller/:action/:id', MyApp, MyApp.route);
I'm assuming that in most cases MyApp.route would call the supplied action on the supplied controller. My question is more about beyond this step how you handle the setup/teardown stuff for an application where you have lots of primary views.
Are people instantiating new controllers when the controller changes as to always start with a clean slate of data and views? Or is it more common/advisable to instantiate all the controllers and such at load and simply use the routing to show/hide primary views?
I suppose the same question goes when bouncing between actions within a controller. Is it proper to do some teardown, especially on bindings/listeners, and then re-establishing them if the action is recalled?
My question may be a little fuzzy, but I'm basically wondering how people handle lots of primary views, and deal with cleanup so stuff doesn't get stale or chew up lots of resources.
I wrote a blog post that describes a method for this: http://codebrief.com/2012/02/anatomy-of-a-complex-ember-js-app-part-i-states-and-routes/
In most Ember and Sproutcore apps and examples I have seen, controllers are instantiated at app initialization. Routes drives state changes in statecharts, where controllers are updated and views are created/destroyed as needed.
I have the following setup.
in my Ember.Application.create() I have the following code:
MyApp.routes = Em.Object.create({
currentRoute: null,
gotoRoute: function(routeParams) {
console.log('MyApp.routes gotoRoute. type: ' + routeParams.type + ' action: ' + routeParams.action + " id: " + routeParams.id);
if (routeParams.type === 'expectedType' && routeParams.action === 'expectedAction' && routeParams.id) {
//find item with ID and load in controller
MyApp.MyController.findItemWithId(routeParams.id);
//Navigate to the correct state
MyApp.stateManager.goToState('stateName');
}
}
})
SC.routes.add(":action/:type/:id", MyApp.routes, 'gotoRoute');
Then, when I click on things that should cause the URL to change I do:
SC.routes.set("location", "show/item/ID-123-123");
Your app should now be listening to changes in the URL and cause the correct action to happen based on the URL-part.
You could probably move the MyApp.MyController.findItemWithId(routeParams.id); to the enter() function of the statechart (if you are using them), but you do need to store that ID somewhere in some controller.