Wednesday, April 1, 2015

Setting up our Silex Application

Setting up the application 

When I first started with Silex, it was very easy. Just a few files, a few routes, and then I was good to go. What I realized is that eventually my index.php page started to look like this:

error_reporting(-1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
date_default_timezone_set('America/Phoenix');
// web/index.php
require_once __DIR__.'/vendor/autoload.php';


use Whoops\Provider\Silex\WhoopsServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Mailgun\Mailgun;

$app = new Silex\Application();
$app['debug']=true;
if ($app['debug']) {
    $app->register(new WhoopsServiceProvider());
}

$app->register(new Silex\Provider\SwiftmailerServiceProvider());
$app->register(new Silex\Provider\SessionServiceProvider());
$app['swiftmailer.options']=[
    'host'=>'mail.site.com',
    'port'=>'25',
    'username'=>'support@site.com',
    'password'=>'secret',
    'encryption'=>null,
    'auth_mode'=>null,
];

$app->register(new Silex\Provider\TwigServiceProvider(), [
    'twig.path' => [
        __DIR__.'/src/Api/Views/Emails',
    ],
]);


$app->get('/',function(){
    return'hello world';
});
$prefix = '/api';

$app->get($prefix.'auth/check', 'Api\\Auth::check');
$app->post($prefix.'auth/login','Api\\Auth::login');
$app->post($prefix.'auth/logout','Api\\Auth::logout');
$app->post($prefix.'contact/contact','Api\\Contact::us');
$app->post($prefix.'contact/password','Api\\Contact::password');
$app->get($prefix.'site/info','Api\\Site::info');
$app->post($prefix.'signup/individual','Api\\SignUp::individual')->after('Api\\Events\\Emails\\Welcome::individual');
$app->get($prefix.'signup/loginCheck','Api\\SignUp::checkLogin');
$app->get($prefix.'signup/userCheck','Api\\SignUp::checkUser');
$app->post($prefix.'signup/site','Api\\SignUp::site')->after('Api\\Events\\Emails\\Welcome::site');
$app->post($prefix.'user/updateProfile','Api\\User::updateProfile');
$app->post($prefix.'user/addApplication/{appId}','Api\\User::addApplication');
$app->post($prefix.'user/removeApplication/{appId}','Api\\User::removeApplication');
$app->post($prefix.'user/updateProfile','Api\\User::updateProfile');

$app->run();

This was a very simple API that I wrote, and we stopped production on it. What I started to notice was that my index.php was starting to get big. Routes started piling up. Application configuration, which was simple now, was getting more and more complex when I would add new service providers. Basically, it was becoming a cluster, and I knew there had to be a better way. I first looked at a few frameworks and the index.php page they used. Yii (not 2.0) looked like this:

$yii=dirname(__FILE__).'/yii/framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
require_once($yii);
date_default_timezone_set('America/Phoenix');
Yii::createWebApplication($config)->run();

Laravel looked like this:

/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell 
 */

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/

require __DIR__.'/../bootstrap/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let's turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight these users.
|
*/

$app = require_once __DIR__.'/../bootstrap/start.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can simply call the run method,
| which will execute the request and send the response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have whipped up for them.
|
*/

$app->run();

To set up my index.php page, I needed to do something like this, and break up all the clutter and organize my application better. To accomplish this, you can do a few different things. First option is to write a class and inject the Silex\Application, the other option is to extend the Silex\Application class. By doing either of these options, you can essentially reduce your index.php down to this:

// public/index.php
require_once __DIR__.'/../vendor/autoload.php';

$app = new MyApp\Application();
$app->bootstrap();
$app->run();

Very simple, very sleak, very easy to read. Broken down, all the index.php does is load the composer autoloader, creates the application, bootstraps the app (does the configuration, routes, etc), and then runs the application.

Folder Structure 

Before we go any further, I wanted to share my folder structure.

MySilexApp
----node_modules
----public
--------app
--------assets
--------dist
--------lib
--------.htaccess
--------index.php
----src
--------MyApp
------------Config
----------------Routing
----------------dev.json
----------------prod.json
----------------testing.json
------------Controllers
------------Middlewares
------------Repositories
------------Services
------------Views
----------------Main
--------------------index.twig
------------Application.php
----tests
--------MyApp
----vendor
----.bowerrc
----bower.json
----composer.json
----composer.lock
----gulpfile.js
----package.json
----phpunit.xml

I hope this all makes sense, but essentially my PHP application is inside of the /src/MyApp folder.  Tests are in the tests folder.  The Javascript application is in the public folder.  Composer packages in the vendor folder.  The reason I have the application structure set up this way just because it is simple.  It makes sense.  We can expose just the public folder to the user and hide the PHP application.  Tests are separated from our application, but will conform to the same folder structure as the application when tests are written.

Inside the application (src/MyApp) folder there are several sub-folders.

Config 

This is where your configuration files will go. Right now I have different config files for different environments. Inside the folder, there is a place to add the Route files, which we will go over later. The reason I added it here is because it is a flat file, no logic needed. The Routes files are simply files that specify the paths, controllers, and middlewares to be used.

Controllers 

This folder is just a folder full of controllers. Following best practices, your controllers should be slim and simple, so the main logic for your application will be done in a the other folders. 

Middlewares 

Name says it all. It just contains the middlewares that will be used by the various controllers. 

Repositories 

If you are using a database, this would be where you handle your data. If you read my previous posts, you know that I am using Soap right now to interact with an external API server. So the Repository folder houses all my connections and data retreival methods.

Services

This is a generic term for miscellaneous classes. One instance would be a class that handles data transformations (take a database object and return a smaller array or combines multiple arrays), or maybe a wrapper for another class/package you use. I have a wrapper for Zend/Soap/Client that is specific to my application. It just helps make the soap calls a little easier. I could potentially use it for another Soap server, but it is designed with the options hard coded for my application. If I wanted, I could move the options (Soap version, compression settings, WSDL url, etc) into a config file, but right now there is no need since I will most likely be moving to a database in the future and not additional soap servers. </rant>

Views

The folder that contains the twig files, or any templating file if you are using another templating system.

 One folder I haven't included, but will probably add in the future is a Validation folder. The folder would essentially handle validation for the application data submitted. When a request is received, the controller would load a Validator and data would be passed to it. Validated data would be returned and then, if valid, it could be submitted to the database.

The Application.php file

namespace MyApp;

use Whoops\Provider\Silex\WhoopsServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Silex\Provider;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;

class Application extends \Silex\Application{
    public function bootstrap(){
        $this->loadProviders();
        $this->loadRoutes();
        $this->loadConfig();
        $this->connectMemcached();
    }
    private function loadProviders(){
        // load session service provider
        $this->register(new \Silex\Provider\SessionServiceProvider());
        $this['session.storage.handler']=null; // since using memcache as session handler, don't use silex to manage anything.

        // load twig service provider
        $this->register(new Provider\TwigServiceProvider(),['twig.path' => __DIR__.'/Views']);

        // the following providers are required by the WebProfiler
        $this->register(new Provider\HttpFragmentServiceProvider());
        $this->register(new Provider\ServiceControllerServiceProvider());
        $this->register(new Provider\UrlGeneratorServiceProvider());

        // get the application environment
        $env = getenv('APP_ENV') ?: 'prod';

        // load a configuration service provider and add configuration options
        $this->register(new \Igorw\Silex\ConfigServiceProvider(__DIR__."/Config/$env.json"));

        // load debug only providers (whoops)
        if ($this['debug']) {
            \Symfony\Component\Debug\Debug::enable(E_ALL, true);
            $this->register(new WhoopsServiceProvider());

            // level 2 debug or localDebug enable the WebProfiler
            if($this['localDebug']){
                $this->register($p = new Provider\WebProfilerServiceProvider(), [
                    'profiler.cache_dir' => __DIR__.'/../../cache/profiler',
                ]);
                $this->mount("_profiler", $p);
            }
            
        }
    }


    private function loadRoutes(){
        // use the Yaml File Loader to load up the routes in the routing folder
        $loader = new YamlFileLoader(new FileLocator(__DIR__ . '/Config/Routing'));
        $collection = $loader->load('routes.yml');

        // add the routes to the application
        $this['routes']->addCollection($collection);
    }
    private function loadConfig(){
        // the other options that you can set go here
        date_default_timezone_set('America/Phoenix');

        // add a simple middleware that looks for application/json requests and adds the paramters to the request object
        $this->before(function (Request $request) {
            if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
                $data = json_decode($request->getContent(), true);
                $request->request->replace(is_array($data) ? $data : array());
            }
        });
    }
    private function connectMemcached(){
        $this->register(new \SilexMemcache\MemcacheExtension(), [
            'memcache.library' => $this['memcacheLibrary'],
            'memcache.server' => [
                ['127.0.0.1', 11211]
            ],
        ]);
    }
}

What I have done is simply extend the Silex\Application and added a few more methods. Again, I could have written a wrapper instead, but felt this was a little easier at the time.

If you recall my index.php, it had the line "$app->bootstrap();" All that line does is loads all the service providers, routes, and configuration for the application.

The service providers are straight forward. The one thing I would like to point out is the application environment. One awesome thing you can do in Apache is set environment variables. In the vhosts file, you can add a line like this:

    DocumentRoot "path\to\htdocs"
    ServerName localhost
    SetEnv APP_ENV dev

On your production server, you can either leave that line out, or you can change the vhosts file to prod (for production). Then in PHP you can use the
getenv('APP_ENV')
to get the information that you set in the Apache file. You can do this in Nginx as well, but I don't have that code off hand. Bringing this full circle, I can have a different configuration that is automatically selected based on this environment variable and then I can use those options in my application. The ConfigServiceProvider is a 3rd party provider and will allow me to set configuration keys and values fairly easily. I can then access the variables like this:
$value = $app['key'];

Simple. In the configuration file I can specify debugging options. On a production server, you don't want to show Whoops error pages when there is a bug (hopefully there aren't any). You definitely don't want to display the webprofiler. On a local machine, you would want to see both of these things. Routes The routes are now being pulled from a file! How cool is that. Because Silex is using Symfony components (Routes), you can load routes using the RouteCollection provided by Symfony. I put the routes into the configuration folder in YAML files. Here is my routes.yml:

# config/routes.yml
home:
    path: /
    defaults: { _controller: MyApp\Controllers\IndexController::actionIndex }

hello:
    path: /hello/{name}
    defaults: { _controller: MyApp\Controllers\IndexController::actionHello }

auth:
    prefix: /auth
    resource: auth.yml

user:
    prefix: /user
    resource: user.yml

One thing to notice is that my routes have subroute resources (auth and user) that reference additional YAML files. The YAML file loader will load those routes as well.

Auth looks like this:

# config/auth.yml
auth.check:
    path: /check
    defaults: { _controller: MyApp\Controllers\AuthController::actionCheck}
    methods: [get]

auth.getLogin:
    path: /login
    defaults: { _controller: MyApp\Controllers\AuthController::actionGetLogin}
    methods: [get]

auth.postLogin:
    path: /login
    defaults: { _controller: MyApp\Controllers\AuthController::actionPostLogin}
    methods: [post]

auth.logout:
    path: /logout
    defaults: { _controller: MyApp\Controllers\AuthController::actionLogout}

And the User routes look like this:

# config/auth.yml
user.apps:
    path: /apps
    defaults: {_controller: MyApp\Controllers\UserController::actionGetApps}
    methods: [get]
    options: {_before_middlewares: [ MyApp\Middlewares\AuthMiddleware::beforeAuth ]}

Now to explain this. The routes.yml file has two defined routes: home and hello. Those are the route names and can be accessed that way anytime when doing redirects. The controller being used is in the defaults: object with the key "_controller". The two subroutes are auth and user. The auth routes are all named with an "auth." prefix and can again be accessed that way as well. The routes are all prefixed with "/auth" so auth.check has a path like "/auth/check". The other thing in the auth.yml file is that you can specify route methods. If you leave it blank, that route can be called via any method. In the auth file I have two separate routes, one for posting to /auth/login and another for getting the /auth/login. The call different controller methods and make it easier than using
$_SERVER['REQUEST_METHOD']
to see how things are being accessed. The user.yml routes have one additional parameter: options. Here you can specify middlewares that the route will use. The middleware object keys are "_before_middlewares" and "_after_middlewares", and both are arrays, so you can pass several middlewares to a single route.

 The routes are all converted to an array object from the YAML files, and are then loaded into the application using a RouteCollection. You can have multiple RouteCollections, and adding them will continue pushing the routes onto the $app['routes'], not overwriting them.

Wrap up 

I think this is long enough for now. We covered your Application.php setup and routing for your application. We have packed in a ton of routes using YAML files and the RouteCollection. We have configured our application for a specific development environment. I think tomorrow I am going to talk about caching and benefits using cache over a file system.

Needed Silex Packages

I am just going to throw out a list of packages/service providers I am using and explain why.

This is what my composer.json file looks like:


{
    "authors": [
        {
            "name": "Tim Withers",
            "email": "tim@opticip.com"
        }
    ],
    "require": {
        "silex/silex": "1.2.*",
        "silex/web-profiler":"1.0.*",
        "symfony/yaml": "2.7.*@dev",
        "filp/whoops": "1.1.*",
        "zendframework/zend-soap": "2.3.*@dev",
        "symfony/console": "2.5.*",
        "symfony/config":"v2.2.0",
        "mheap/Silex-Memcache": "@dev",
        "igorw/config-service-provider":"1.2.*"
    },
    "require-dev": {
        "phpunit/phpunit": "4.5.*"
    },
    "autoload": {
        "psr-0": { "TeacherManager": "src/" }
    }
}


Silex

Of course, how can you have a Silex app without Silex.

Silex Web Profiler

This is awesome.  It is actually a Symfony plugin.  I saw it and used it when developing in Symfony and wondered if there was a service provider for it.  Sure enough, there is.

When using the Web Profiler it will only work when you send a response to the browser.  Using "exit()" or returning a string only will not allow the web profiler to work.  You must return a response using Symfony Response or the Silex Application.

Yaml

This is just another way to write config files.  Similar to JSON, but easier as there are no quotes.

Remember, quotes not needed.  Avoid tabs at all costs.

Whoops!

Laravel showed me the greatness of Whoops and Silex has a provider for it.  After configuring it, every error will run through Whoops and display on your screen with incredible amounts of information.  It will provide all the request information, server information, session information, as well a stack trace with the line of code in questions highlighted.  If you haven't used it, use it.  It will make debugging a hell of a lot easier.

Zend Soap

Everyone hates SOAP.  But this made it usable.  Because of my application, I have to communicate with a 3rd party API server to store and retrieve information.  Don't hate.

Console

Although I haven't used it yet in my application, the Symfony Console will allow me to write quick commands and run them in shell/command prompt very easily.  I have written a Soap tester service that will allow me to plug in a WSDL url and pull up all the commands and make Soap calls.  It was my first experience with it, but it worked well.

If you are familiar with writing console commands in Laravel (Artisan commands), this will feel fairly natural.  Of course there is some configuration, either including the application, or just components, you will be able to get your job done quickly.

Config/Config service provider

This service provider allows you to specify config files and load up the configurations into your application.  I wrote dev.yml and prod.yml files and inject them into my application to load up settings differently.

Memcache

I don't use Redis, but I use Memcache fairly extensively.  I have move to storing PHP sessions in cache rather than the file system.  I saw huge performance increases by this move alone.  Memcache is not persistent, and will be deleted when the server is restarted, but it is blazingly fast.  A monitor on one of my servers shows that it takes an average of 0.00015 seconds to connect to the Memcache service and retrieve a key.  Since we are executing Soap requests to a remote server which take 0.01 seconds to get a result, utilizing Memcache to store those responses significantly speeds up the application.


My next article will be going over the initial configuration of the Silex application, and the tweaks I made and my reasons behind it.

Tuesday, March 31, 2015

Getting started with Angular and Silex

It has been a while, but I have been working these past few weeks on a new project which is essentially a re-write of a previous app.  I wanted to present a few things about the things I have learned.

The Plan


I have an existing, albeit old, Angular application.  I have been tasked with redesigning the application and helping prepare for the migration to Angular 2 in the upcoming year.  To tackle the problem, I started reading up on best practices regarding Angular and how to make the migration easier.  I kept on coming back to John Papa's Angular Style Guide (https://github.com/johnpapa/angular-styleguide) and read through it 2 or 3 times, taking in what I was reading.

The goal with the style guide is essentially to modularize Angular apps.  It's goal is to allow another developer to look at your application and code and be able to pick up where you left off without having to mess with spaghetti code.  There are several awesome points in the style guide. Put everything in it's own file.  Handle injections properly.  Getting rid of anonymous functions.  Shuffling your code to make it more readable.  Most importantly, to start removing the dependency of $scope inside your application.  Angular 2 will not have the all magical powers (and mess) that $scope has, and will rely on controllers/classes to power your application.

The Backend


The next part of the plan was deciding on how to handle my API/backend system.  When I wrote my original application about 2-2.5 years ago, I opted to use Yii.  Laravel was still in version 3, and was only starting to grow in popularity.  Yii was the big name at the time, and it sounded like a solid investment.  Composer was new, and didn't need to be used.  Node was a new language and not nearly as wide spreads as it is today.

So do I continue using Yii, using the exact same framework and API that I currently have or do I start over?  This is the question.  I have written several apps in Laravel, I have seen Yii 2.  I have used Symfony, and know a bit about Zend.  What do I choose?

Why I chose Silex


I chose Silex.  The reasons are pretty simple.  It is built on Symfony components (just like Laravel), and is not much more than a routing engine with the ability to add "Services" to it.  The reason I decided to go with Silex is because that was all I needed, routes.  My application doesn't send off emails, it does not use a database, it does not need to create forms, handle "grids", and the thousand other things frameworks do.  Silex is simple, its small, and very customizable.  In the future, if we do add databases to it, I can just add a new service provider.  If we decide to start using the app to send emails, then I can add the Swiftmailer service provider.  But I can add everything a piece at a time as I need it, rather than adding full functionality and only using a portion of it.

Now the problem arose when I had to figure out how to implement a Silex application with several dozen API routes.  I want to make it extensible, debuggable, testable, and easy to configure.  So that is what I am planning on for the next few days.  Going over how to take the simple Silex examples and develop a professional application that does everything it needs to, using the latest in technologies (composer, phpunit, bower, gulp) to power and improve your development.

Tuesday, July 30, 2013

Setting up Auth in an Angular App

So I was tasked to set up some type of simple authentication system using Angular.  The basis was that there be a login page, and a main page.  If a user navigates directly to the app, then they go to the login page.  If the user clicks an authorized link, they are logged in and taken to the main page.  So to get started I laid out my app with three routes:


var tmapp = angular.module('tmapp', [])
.config(['$routeProvider', function($routeProvider) {
    $routeProvider
        .when('/',{templateUrl: '/partials/admin/login.html',   controller: LoginController})
        .when('/main',{templateUrl:'/partials/admin/splash.html',controller:SplashController})
        .otherwise({redirectionTo:'/'});
}]);

Simple enough, the base route displays the login page, and the main route displays the splash page. Now for problem number one, if I type in /main, the I am able to visit that page. Obfuscating the code would make it harder, but definitely not too hard to see the routes my app has, so this isn't secure.

To solve the problem, we can add a .run function. This will get ran when the app is initialized. We can tie in some pretty important event bindings at this level to ensure that the proper authentication is happening.

tmapp.run(['$rootScope',function($rootScope){
}]);

Now inside, we can use the $rootScope as a $scope variable. Think of $rootScope as the parent of $scope, and all $scope variables inherit the properties of $rootScope. So if I make an event binding on $rootScope, then the child $scope also has that binding. For instance:

tmapp.run(['$rootScope',function($rootScope){
    $rootScope.$on('$routeChangeStart', function(event, next, current){
        //check if logged in
        //redirect if not
    }
}]);

Now every time a route is called, this function will be called. Of course, nothing yet exists in the function, but we could test it using console.log or alert and we would see that every time the route changed, the function was called.

If I want to check logged in status, where do I put it? Not in a controller, because those only handle functions specific for the route, and this function would need to be for ALL routes AND be persistent between routes. If I set $scope.myVar=1 with a button on my view that increments it by 1 each click, you would see the count increase. If I refreshed the route, the count would be back at 1 again, so $scope data is not persistent. The answer is to create a service. Services are static across all routes, so it would be a perfect place to check and store authentication. To set up a service, you use the factory method.

.factory('sessionHandler',['$http','$location',function($http,$location){
    var setLoggedIn=function(){
        s['auth']=true;
    };
    var redirectHome=function(){
        s={};
        $location.path('/',true);
    };
    var s={};
    return {
        set:function(key,val){
            s[key]=val;
        },
        get:function(key){
            if(!angular.isDefined(s[key])){
                return false;
            }
            return s[key];
        },
        logout:function(){
            $http.get('/admin/logout').success(function(){
                s={};
                window.location=window.location.origin+window.location.pathname;
            });
        },
        checkLoggedIn:function(){
            if(s['auth']){
                return true;
            }else{
                $http.get('/admin/authStatus').success(setLoggedIn).error(redirectHome);
            }
        }
    };
}]);

This is a big blob of code that I am now going to walk through and explain.  Again, the first line is the simple syntax to set up your service, injecting specific angular services into my service.  Then we move on to some private functions (setLoggedIn and redirectHome).  The first sets a variable called s['auth'] as true, which is the property we look for when the service starts to see if a user is logged in.  The second function essentially unsets our s variable and takes them to the login screen, so this is called when they are not logged in.  Finally, we return the service functions that interact with the private functions.  The functions set and get can be used to set/get data across the controllers, much like session variables can be used on the server.  The logout function sends the request to the server to log the user out, resets the s variable, and refreshes the app, which takes them back to the initial route (/).  The last function, checkLoggedIn, checks for s['auth'].  If it can't find it, it makes the request to the server to see if the user is logged in on the server.  If it gets a successful response, it sets the s['auth'] variable, otherwise it redirects them to the home and unsets any data they had.

So now to implement this, in our app.run function.

tmapp.run(['$rootScope','sessionHandler','$location',function($rootScope,sessionHandler,$location){
    $rootScope.$on('$routeChangeStart', function(event, next, current){
        if(sessionHandler.checkLoggedIn()&&($location.path()===''||$location.path()==='/')){
            $location.path('/main',true);    
        }
    }
}]);

Let's walk through this. First, notice the additional dependencies I have injected: my service (sessionHandler), and $location. Second, the if statement executes checkLoggedIn(). If it fails, it will automatically redirect the user to the home page because that is what checkLoggedIn does in the function. Next, it checks the current location. If they are on the login page, which has a blank or null route, then it will redirect them to the main page. Otherwise, the function just leaves the user alone.

If a user navigates to the main page without logging in, the service will catch that and redirect the user back to the login page. If they login, and then try navigating back to the login page, the check will catch that as well and redirect the user to the main page. That is it!

Now for some advanced material. Using sessionStorage. If a user refreshes the page, their data will be lost. s['auth'] will be undefined, and the check will need to take place again. Any other variables you set in the sessionHandler service will be wiped out. So to implement this, we need to modify the sessionHandler.

.factory('sessionHandler',['$http','$location',function($http,$location){
    var setLoggedIn=function(){
        s['auth']=true;
        sessionStorage.data=angular.toJson(s);
    };
    var redirectHome=function(){
        s={};
        sessionStorage.data=angular.toJson(s);
        $location.path('/',true);
    };
    if(angular.isDefined(sessionStorage.init)){
        var s=angular.fromJson(sessionStorage.data);
    }else{
        var s={};
        sessionStorage.init=true;
    }
    return {
        set:function(key,val){
            s[key]=val;
            sessionStorage.data=angular.toJson(s);
        },
        get:function(key){
            if(!angular.isDefined(s[key])){
                return false;
            }
            return s[key];
        },
        logout:function(){
            $http.get('/admin/logout').success(function(){
                s={};
                sessionStorage.data=angular.toJson(s);
                window.location=window.location.origin+window.location.pathname;
            });
        },
        checkLoggedIn:function(){
            if(s['auth']){
                return true;
            }else{
                $http.get('/admin/authStatus').success(setLoggedIn).error(redirectHome);
            }
        }
    };
}]);

You will notice just a few additional lines, but most notably, the if statement. What it does is check to see if a sessionStorage variable is currently set. If it is, then it grabs the data and parses it into our "s" variable. If not, it initializes the session storage and an empty "s" variable. All the other lines simply store data to the sessionStorage.data variable in JSON format. This is done to minimize the amount of variables stored by the browser, and hopefully help hide some of the data. The other reason I chose to do it this way is because sessionStorage does not store objects. Even simple objects without functions cannot be stored in sessionStorage, only strings. So by JSONifying the object, you can store it easily in sessionStorage. Everything else is pretty self explanatory. You can now use the service in your controllers and persist data AND logins across a page refresh.

That concludes my tutorial for now. It is very simple, and I didn't cover anything else except using the app.run and app.factory methods. I also didn't include data on the logout function, but I am sure you can add the route yourselves and call the service function. If there is any other questions or issues, let me know!

Tuesday, May 14, 2013

Working with Bower and Grunt

Even after getting everything up and running with Yeoman, Bower and Grunt, I realized there were still issues, especially when it came to utilizing Twitter Bootstrap in my project.  The problem arose from the Angular Generator, specifically in the fact that there were no images or javascript files included, only the stylesheet.

Solution #1


Yeoman has a separate generator (bootstrap-generator) that you can download:
npm install -g bootstrap-generator
After installation, you can then navigate to your app directory and run:
yo bootstrap:app
This will create a folder in your components directory with all your bootstrap needs.  You can then include the files as needed.

Problem #1

Bower also uses the components directory to add extra components.  Running the command:
bower list
or
bower update
causes errors to be thrown because that folder should only be used for Bower

Solution #2


Well, Yeoman works, but causes more errors than it is worth.  Bower was my next thought, and sure enough, you can get bootstrap installed with:
bower install bootstrap

Problem #2

Bower grabs the files from Twitter's GIT, and if you didn't already know, the files are all LESS, not CSS.  So if you are looking for vanilla CSS, this doesn't help.  Turns out however, that the files in the docs folder are the compiled version of Bootstrap, so that actually does work.  The only problem with that though is that Bower's simplistic nature (
<script src= 'components/COMPONENT_NAME/COMPONENT_NAME.js' > ) doesn't apply.

Final Solution


After doing some digging, Bower also has a package called Bootstrap.css, which is vanilla Bootstrap and Javascript files.  It is straightforward and contains all the images and everything needed to get you app up and running.

Final Problem


Trying to build with Grunt is a pain.  Grunt will only package up Javascript files located directly in the scripts folder, and image files located directly in the images folder.  Grunt will also find all your CSS files, and then package all those together in one single CSS file.  This is great, but can be a problem because your Bootstrap CSS points to ../img/glyphicons-halflings.png which wasn't moved to the image or img folder from the components folder.

Final Fix


The final solution is to edit your Gruntfile.js and add in a few things in your grunt.initConfig object.  All I did was changed the imagemin object to look like this:

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/img',
          src: '{,*/}*.{png,jpg,jpeg}',
          dest: '<%= yeoman.dist %>/img'
        },{
          expand: true,
          cwd: '<%= yeoman.app %>/components/bootstrap/img',
          src: '{,*/}*.{png,jpg,jpeg}',
          dest: '<%= yeoman.dist %>/img'
        }]
      }
    },
This solved all my issues and even put the images into the img folder instead of images for a cleaner transition and less editing of Bootstrap.


Saturday, May 11, 2013

Creating an Angular app with Yeoman, Bower, and Grunt on Windows

I will be brutally honest, as a Windows user I feel a little left out when it comes to terminal and the cool ability to install repositories by typing a few words.  I feel intimidated using SVN, GIT, Composer, PHP, Pecl, Pear, Phar, Node and any other program/script that seems to run with ease on Mac and Linux.  Over time things have become easier.  I have figured out the PATH file to let commands run a little easier in Windows Command Prompt.  I am not going to go over PATH and everything since I am assuming that is something you should probably already have figure out.  What I intend to go over is getting a basic Angular JS app up and running with tests and dependencies using Yeoman, Bower, and Grunt.

What is Yeoman, Bower, and Grunt


Yeoman (http://yeoman.io) is a scaffold manager/app generator.  The whole goal of apps is consistency.  Think of it as a template for your app with the index page created, script tags in place, css files loaded, an so on.  The whole point is just to make your life easier.  How often have you gone to a website, downloaded the *.zip file, created a directory, and then copied and pasted parts of the zip file into specific directories?  And for a single app, you probably do this a half a dozen times (angular, angular-resouces, jquery, jquery-ui, twitter bootstrap, angular-ui, etc).  Then you go to your index.html and go back and forth remembering to type the names correctly and put them in the right place.  Yeoman does this for you, so ultimately you save time and headaches.

Bower (http://bower.io) is a package/resource manager.  You want jQuery, you got jQuery.  You want angular-mocks, you got angular-mocks.  It seriously makes getting resources downloaded and put in the right directories super easy.  The upside to this is that any updates (lets say jQuery 2.1 comes out) can be done by running an update on Bower.  All the resources will be checked and the outdated ones will be updated to the latest version.  No more remembering weekly to visit all the resource sites.  Bower has around 2000 resources in their repository, so anything you can think of from HTML, to CSS, to Javascript, they got it.

Grunt (http://grunt.io) is the worker of the bunch.  With Grunt you can set up a mock server to see your app in action.  You can also run the Karma tests that you should be writing.  And finally you can compile, minify/uglify, and prep your code for production.  If you are writing tests, you now how hard it is to get the adapters to work in Windows the first time and then remembering the steps... this simplifies it completely.

Prerequisites


Node.  That's it.  Node is super easy to install, just visit http://nodejs.org and click the Install button.  On Windows you will get the *.msi file and in a few seconds the installer will be done.

**NOTE**
It has been a while since I installed Node, but if I am correct, using the MSI installer will also configure your PATH and allow you to use node and npm in Command Prompt.  For this tutorial you will need to use npm.

Installing the programs


First things first, install the three amigos, and then the generators Yeoman needs for the Angular scaffolding.
npm install -g yo bower grunt-cli
npm install -g generator-angular generator-karma
 That is it!

Creating your first Angular app


Using Command Prompt (because that is what we are doing), navigate to a directory to create your app.  Really anywhere is fine:

cd c:\
mkdir angular-apps
cd angular-apps
mkdir first-app
cd first-app

Inside the first-app directory, we now run Yeoman (yo) and create our scaffolding:

yo angular

Wait for a minute and then you will be presented with the following options:

Would you like to include Twitter Bootstrap? (Y/n) y
If so, would you like to use Twitter Bootstrap for Compass (as opposed to vanill
a CSS)? (Y/n) n
Would you like to include angular-resource.js? (Y/n) y
Would you like to include angular-cookies.js? (Y/n) y
Would you like to include angular-sanitize.js? (Y/n) y

After which it will download and organize your app entirely.

Now on to bower.  We need to actually install the components the make this app run and tests to work:

bower install angular
bower install angular-resource
bower install angular-cookies
bower install angular-sanitize
bower install angular-mocks

If you wanted jQuery or some other plugins, you can install those as well.  All these components are installed in the app/components folder and are very easy to tag:

<script type='text/javascript' src='components/angular/angular.js'></script>
<script type='text/javascript' src='components/angular-resource/angular-resource.js'></script>
 <script type='text/javascript' src='components/%BowerPackage%/%BowerPackage%.js'></script>

Well the good news is, we don't have to write those in our index.html file as Yeoman already did all that for us, along with the app.js and controller.js files.  So that brings us to the final category, running and testing with Grunt.  There are 3 commands (grunt server, grunt test, and grunt) that will be used most often, but they may or may not work by default.  Because of Yeoman's scaffolding generation, the Gruntfile.js has Compass dependencies, which in turn has Ruby dependencies.  This can be a headache if you aren't planning on using Compass.

**NOTE** 
Compass is a styling/scripting language for generating CSS files.  If you have heard about LESS or SASS, it is similar and much more user friendly.  Compass, in fact, takes what you have coded and converts it to SASS, which in turn is converted to CSS.

If you don't have Ruby installed and you aren't planning on using Compass, then you will need to edit your Gruntfile.js.  The easiest thing to do is search for "Compass" and delete those objects/values.  There are 5 places where Compass objects appear.  First in the grunt.initConfig.watch object, then in the main object of grunt.initConfig.  Finally in the three grunt.registerTask functions, there are 3 instances of Compass that need to be removed.  If you are coding Angular you should be able to figure out how to remove those objects from the file, so I am not going to post the entire Gruntfile here.

Finally, you can run grunt server and Chrome will pop open with your first app.  You can close that and run grunt test and your initial Karma test will run (Chrome will open, run the tests and close.  The output is in the Command Prompt).  And then you can build your app using grunt when it comes time for production.

I hope this helps get things going for you.  It took me a while to figure out what all this was about and how to get the 3 amigos to play nicely with each other on Windows.  If you have any questions, feel free to let me know.