Sunday, June 8, 2014

Needs More Dojo passing 10k downloads!

Recently Needs More Dojo passed 10,000 downloads on the JetBrains plugin repository. This consists of new users and of course users upgrading from previous versions. Still, it's a pretty exciting milestone for me personally. I'm very happy with the response that the NMD plugin has seen, and have received a ton of great feedback, feature requests, and bug reports.

I did go a very long time without updates (5 months or so?) other than an update for IDEA 13.x, but 0.7 did have features and bug-fixes in it that I was happy to finally release. Hopefully the next version will come a lot sooner. I haven't used Dojo as heavily as I'd like in the past few months, but that is going to change in the near future.

One thing that's been extremely useful to see is the distribution of downloads among the different JetBrains products (courtesy of their reporting tools):

I exclusively use IntelliJ IDEA, so that's where most of my testing has occurred. However this chart shows that the while it is a majority of downloads, PhpStorm and WebStorm are critical to test as well.

Thanks again to everyone who has used Needs More Dojo!





Sunday, October 20, 2013

Smarter support for dojo in IntelliJ/WebStorm with Needs More Dojo 0.6

The next version of Needs More Dojo makes many enhancements to IntelliJ IDEA, WebStorm, etc. to provide better integration with Dojo. Personally, I've really enjoyed working on this version and can't wait to get it out the door!

1. this.inherited resolution

If you've ever used Navigate ... Declaration (Ctrl+B) to see the super method of a Dojo module, you would be greeted with this:
Basically: a lot of this.inherited references
It's not really the IDE's fault, as that would require knowledge of Dojo's custom OO model which of course it doesn't have. Luckily, Needs More Dojo does have that knowledge. Now when you use Ctrl+B, Needs More Dojo will navigate the dependency graph in your declare(...) block and search for methods that match. Here's what you get instead:
Much better!
2. Method resolution

After adding support for this.inherited resolution, it was easy to add method resolution as well. IntelliJ does a surprisingly good job of resolving methods in JavaScript, but there are still cases it will miss. For example, if you try to resolve domStyle.set(...) you will get this:

A very incorrect list of set methods
Once again, not really the IDE's fault. With Needs More Dojo, you won't get a list at all. Instead, you will navigate to the set method of dom-style.js.

3. AMD module resolution

This is one of my favorite new features. Let's say you have imported a module and you want to jump to its source file. By default, if you Ctrl+Click on query for example in this block of code: 

define([
    'dojo/query'
], function(query) {
    var results = query('.foo');
});

You will get directed to to the "query" parameter. Not very useful. With Needs More Dojo, it will resolve query.js and send you there. Of course this works for your modules as well, and relative or absolute path syntax.

4. Require blocks and embedded scripts

In this version, you have all AMD management functionality available for require blocks, nested or otherwise. In addition, you have all Needs More Dojo functionality available in embedded scripts in HTML, jsp, php, etc. files. You can configure the list of supported file types in the settings dialog.

Refactoring has been updated to take require blocks into account as well.

5. Quick fixes

Finally, there are several quick-fixes for existing functionality that are now available. For example, if you have an unused import, you get three quick-fixes listed:

As I said, this version has been very exciting to work on. There are some other new changes I didn't add here, but you can see the full list of things that were added on the GitHub issue page. 

I expect to spend a couple of weeks testing this version, so expect it to be released in 2-3 weeks.



Wednesday, September 18, 2013

Needs More Dojo 0.5.1: Cyclic Dependency Detection

So 0.5.1 is a "minor" release. This means that it should be mostly bug-fixes and some minor enhancements. Well in this release I decided to add a pretty big feature (it was a feature request actually). The feature in question is cyclic dependency detection.

In dojo, cyclic dependencies present a problem. If you have a cyclic dependency, one of the modules involved will be resolved to {}, an empty object. Oops. This is to prevent further recursion as the AMD loader goes down the dependency graph. Unfortunately this can lead to subtle bugs and can be difficult to track down. I remember struggling when I was first learning dojo and ran into this problem, luckily a colleague was able to describe the issue.

Anyway, in Needs More Dojo I've added the ability to scan for cyclic dependencies. So now you can run a scan on your project sources, and you get a tool window like this:

Scanning for cyclic dependencies.
All it's doing is navigating the dependency graph and checking for cycles. It also counts the number of times a particular module has been involved in a cycle and displays the count next to it. In my simple example, all of the modules listed appear once. However, if you had one module appearing more than another one, it's more likely the culprit causing the cycle.

This appears as an action in the code menu that you can run. I've also added an inspection that will run in the background and highlight modules that are involved in cyclic dependency graphs like this:

Cyclic dependencies flagged as errors
In this example, the inspection has detected a cyclic dependency and displays it as an error. You can also see that the dependency in question (ProjectData) is flagged as unused. So there's an unnecessary cycle in this project.

The inspection is disabled by default. I did this because I didn't want large projects getting performance hits, as the inspection has to build a graph of dependencies for each file.

Expect 0.5.1 to be released in a couple of weeks.

Saturday, September 14, 2013

Needs More Dojo v0.5: Refactoring Support

For the past couple of months I've been working on an IntelliJ IDEA plugin called Needs More Dojo. It's basically a plugin that makes IntelliJ/WebStorm/etc. aware of conventions and structure of dojo applications. I use dojo at my day job, so it started out of things I noticed could be improved.

I'm very excited about the newest version, 0.5, that was released about a week ago. Although you might not do major refactoring every day, it's painful when you have to do it. Sadly, IntelliJ does not handle dojo's AMD system very well and so when moving files you still have to manually update them.

So Needs More Dojo complements IntelliJ's features by being aware of the AMD system and your project's package structure. Due to this, it will successfully update your import statements with the correct locations. It's taken awhile to build up the code necessary to support refactoring, but I'm glad it's here now.

The next release will be a minor release (0.5.1). For this release I have a few minor enhancements planned so far based on some user feedback and my own roadmap. I'm going to try to space the releases out on a more defined interval instead of sporadic (0.5 took forever to be released for example).

If you have any feature suggestions, head over to the issues page on github and add them.

Saturday, January 19, 2013

JavaScript is not the problem

I've been reading about different new languages in the works that compile to JavaScript. Some examples: Microsoft's TypeScript, Google's Dart, CoffeeScript, etc. They all 'fix' issues in JavaScript that we all know and dislike. Stuff like the 'this' behavior, no block scope, and a lack of even optional static typing. 

I agree that there are plenty of things in JavaScript the language that are annoying and should be fixed. Now apparently everyone thinks that if we just fix JavaScript the web will be a better place.

I've spent the last seven months or so working on a web application. This particular application looks like it should be on the desktop. It is single-page, has a very traditional desktop layout, and can be safely identified as 'rich'.

Out of all the pain that has arisen while developing, the least of my pain comes from JavaScript. In fact, I enjoy using JavaScript and paid the price of learning its quirks and problems. There was an initial learning curve yes, but I'm comfortable with it and can avoid its flaws.

My real problem is not with JavaScript, and I don't think the solution to turning the browser into a 'real' application platform is fixing JavaScript. 99% of my painful, time-consuming tasks are from the DOM (and to an extent, browser inconsistencies).

The DOM is just awful. It's not meant for applications that you would normally write natively (if it wasn't for that good ol' write once, run anywhere). I've been working with the Dojo Toolkit, and it's been a fairly positive experience. This particular framework solves a lot of issues cited with JavaScript, such as the lack of modules and traditional class-based inheritance.

However, my time is spent figuring out how to get my tab containers to fit their content, or resize in a modal dialog, or render charts correctly, or making sure text fields look ok when they're localized, whatever.  Some of these are component issues, but the underlying issue is that the DOM is terrible for real applications. It's not built for components, it's built for documents like the name suggests.

The browser environment in general sucks. Why can't I show the print preview dialog instead of jumping straight to the print dialog? Memory leaks due to the abstraction of the DOM. CSS file limits in IE. Trying to copy something to the clipboard. No, the browser was not designed for this. I understand that, but there are no good alternatives. Flash has its own problems, no other plugins have taken off (Silverlight? Java applets? Ha!). Going native is looking more attractive.

It's not a fun experience, and none of these issues are solved by sticking another language on top of JavaScript.


Monday, March 26, 2012

Quick update

Hey everyone,

Been super busy, sorry for the lack of updates. Finishing up my senior design project, continuing another project, and starting a new job soon. Expect some updates at some point though! Looks like I'll be leaving Microsoft land, as I installed Ubuntu over my Windows installation so no more .Net stuff for awhile.

See you next time.

Friday, February 17, 2012

Simple example of a Sencha ExtJS 4 login form using MVC

Hey guys.

For my senior design project I've been working with Sencha's ExtJS 4 to develop a web application. Now, I wouldn't normally try and write a beginner tutorial for something like this because I believe there are plenty of badly written beginner's tutorials all over the place that you could find much easier. But I'm going to post what I've been doing with this framework for two reasons:
  • Most tutorials I see seem contrived and don't employ any kind of separation. I'm using an MVC architecture and had trouble finding examples. 
  • Some references in Sencha's documentation seem to be wrong. Apparently their API has gone through several major revisions. 
And I'd like to go ahead and state that this is how I got it working, it might not be the best way to do so. In fact, if you know a better way to do something, feel free to let me know.

The goal here is simple: A user should be able to log in, see some indication that they've been logged in, and log out. Here's what that looks like:
Not the most amazing login system...
Pretty crude, but the important part is getting it functional. Here you see that the user can log in our out. There's also an entry below the form that displays the currently logged in user. Normally if they're logged in the "login" button doesn't show, but this is from an earlier version.

So, to the right I've given you the directory structure so you can see what it looks like -- as you can see it's a pretty straight forward MVC layout.

A short disclaimer: I've put together this example by ripping off various examples in Sencha's documentation, so if you see some dangling reference to them that's why.

Alright, let's start with the store and model. First, here's the model:

Ext.define('AM.model.User', {
    extend: 'Ext.data.Model',
    fields: ['username', 'isAdmin', 'authenticated', 'loggedOut']
});


Nothing too exciting, this represents the current user. Here's the corresponding store:

Ext.define('AM.store.Users', {
    extend: 'Ext.data.Store',
    model: 'AM.model.User',

autoLoad: true,

proxy: {
    type: 'ajax',
    url: 'login?view=sencha&json=true',
    method: 'GET',
    reader: {
        type: 'json',
        root: 'model',
        successProperty: 'model.success'
    }
}

});



As you can see, the store is configured to ask the server for the user data.

Ok, now the views. The first view is the actual form:

Ext.define('AM.view.login.Form' ,{
    extend: 'Ext.form.FormPanel',
    alias : 'widget.loginform',

    name: 'loginform',
    frame: true,
    title: 'Password Verification',
    bodyPadding: '5px 5px 0',
    width: 350,
    height: 150,
    fieldDefaults: {
        labelWidth: 125,
        msgTarget: 'side',
        autoFitErrors: false
    },
    defaults: {
        width: 300,
        inputType: 'password'
    },
    defaultType: 'textfield',
   
    initComponent: function() {
        this.buttons = [
        {
        name: 'loginButton',
            text: 'Login',
            action: 'login'
        },
        {
        name: 'logoutButton',
        text: 'Logout',
        action: 'logout',
        visible: false
        }
        ];
       
        this.items = [
        {
            fieldLabel: 'Username',
            name: 'username',
            id: 'username',
            inputType: 'text'
        },
        {
            fieldLabel: 'Password',
            name: 'password'
        }
        ];
       
        this.callParent(arguments);
    }
});



By the way, I ran across a tutorial in the documentation referencing Ext.FormPanel, but Ext.form.FormPanel seems to be correct.

Here's the other view that just lists the currently logged in user:

Ext.define('AM.view.login.Display' ,{
    extend: 'Ext.grid.Panel',
    alias : 'widget.logindisplay',

    title : 'Users',
   
    initComponent: function() {
        this.store = 'Users',

        this.columns = [
            {header: 'username',  dataIndex: 'username',  flex: 1},
            {header: 'authenticated',  dataIndex: 'authenticated',  flex: 1}
        ];
  
        this.callParent(arguments);
    }
});



Alright, now the fun part: the controller. First, here it is:


Ext.define('AM.controller.Login', {
    extend: 'Ext.app.Controller',

    models: ['User'],
    stores: ['Users'],
    views: [ 'login.Form', 'login.Display'],
   
    refs: [
           {
               ref: 'loginForm',
               selector: 'form'
           },
           {
           ref: 'loginButton',
           selector: 'loginform button[action=login]'
           },
           {
           ref: 'logoutButton',
           selector: 'loginform button[action=logout]'
           }
       ],
   
    init: function() {
        this.control({
        'loginform button[action=logout]': {
        click: function(button)
        {
           var store = this.getUsersStore();
           var logoutButton = button;
           var loginButton = this.getLoginButton();
        
           this.getLoginForm().form.submit({
                        waitMsg:'Loading...',
                        url: 'login',
                        method: 'POST',
                        success: function(form,action) {                        
                           Ext.MessageBox.alert('Logged out', 'You have been logged out');
                           logoutButton.setVisible(false);
                           store.load();
                           store.sync();
                           loginButton.setVisible(true);
                        },
                        params:
                        {
                           view: 'sencha',
                           json: true,
                           logout: true
                        }
              });
            }
        },
        
        'loginform button[action=login]': {
            click: function(button)
            {
            var store = this.getUsersStore();             
            var loginButton = button;
            var logoutButton = this.getLogoutButton();
            
            this.getLoginForm().form.submit({
                        waitMsg:'Loading...',
                        url: 'login',
                        method: 'POST',
                        success: function(form,action) {                        
                            store.load();
                            store.sync();
                            loginButton.setVisible(false);
                            logoutButton.setVisible(true);
                        },
                        failure: function(form,action){
                            Ext.MessageBox.alert('Error', "Invalid username/password");
                        },
                        params:
                        {
                           view: 'sencha',
                           json: true
                        }
               });
            }
            }
        });
    }
});


I didn't have much success on finding information about referencing views from the controller. Most examples mix the logic with the view so it's not an issue. In the end I ended up using refs to obtain the form and its buttons.

There's not much going on here actually. When the user clicks 'Login,' we pass the information to the server and then synchronize the store. If the user is logged in, the server will pass back the user's information. To log out, the parameter is passed to log out and the server clears the user. When the store is synchronized, the user will no longer be in it.

Oh, and here's app.js: 



Ext.Loader.setConfig({enabled:true});

Ext.application
({
    name: 'AM',

    appFolder: 'js/sencha/app',  /* this appears to NOT be a relative path */    

    controllers: ['Login'],   
   
    launch: function() {    
    console.log("init");
    
        Ext.create('Ext.Panel', {
            layout: 'fit',
            renderTo: 'login',
            items: [
                {
                    xtype:'loginform'                 
                },
                {
                    xtype:'logindisplay'
                }
            ]
        });
    }
});

So that's pretty much it. Again, I'm sticking this up here because I had problems finding examples that suited my needs. Most of them mixed the view/controller logic which made referencing components a lot easier. For this project however, we're using an MVC architecture so that didn't help.

See you next time.