Author Archives: Dimitry

Missing server config for apollo-express graphql server

It’s amazing (in a bad way) how much time I spent on getting new apollo-express working. Here is missing code (quite small!):

// based on http://dev.apollodata.com/tools/apollo-server/migration.html
const fs = require('fs'),
    bodyParser = require('body-parser'),
    apolloExpress = require('apollo-server').apolloExpress,
    graphiqlExpress = require('apollo-server').graphiqlExpress,
    makeExecutableSchema = require('graphql-tools').makeExecutableSchema,
    schema = fs.readFileSync(__dirname + '/schema.graphqls', 'utf8'),
    resolvers = require('./resolvers'),
    executableSchema = makeExecutableSchema({
        typeDefs: schema,
        resolvers
    });

module.exports = function(app) {
    app.use('/graphql', bodyParser.json(), apolloExpress({
        schema: executableSchema
    }));

    app.use('/graphiql', graphiqlExpress({
        endpointURL: '/graphql',
    }));
};

Rails 4 + gulp + webpack + browsersync on Cloud9

I wanted super flawless experience for web app development so I chose:

  1. Rails 4 for fast back-end developing
  2. Webpack for compiling Coffeescript files with modules support and also for ability to dynamically load necessary javascript in runtime
  3. Gulp for LESS compiling (also with @import resolving), SVG icons sprites, CSS autoprefixing, source maps, minifying etc.
  4. Browsersync for page live reload on js or css changes while developing
  5. Also I’m using Cloud9 browser online IDE for developing my pet project. In fact Cloud9 is not only online IDE, but it’s complete VPS for all developer’s need. It’s very useful when you work on multiple computers and when you want to code in travel.

No time for reading? Try it with this  https://github.com/DimitryDushkin/rails-gulp-webpack-browsersync.

You can find a lot of rails’ project stubs with gulp, webpack etc., but there is no examples of browsersync integration with Rails and Cloud9.

It’s quite untrivial to make cloud9 work with browsersync, because Cloud9 accepts connections only on 80 port and also uses own proxy for verifying access rights to hosted web app.

First of all lets start rails server via “rails s -b 0.0.0.0“.

That’s what happens on running “gulp watch” command in my workflow:

  1. Browsersync starts its own http server on 8080 port.
    1. Cloud9 expects your server to run on 8080 port. Cloud9 uses it’s own proxy server which works on 80 port, it accepts all connections, making some authorization magic and then forward request to 8080 port, that’s why you need to configurate your web server on 8080.
    2. Browsersync can work as proxy server to include it’s javascript file in HTML that Rails responding. So we configurate Browsersync to be proxy server to 3000 port.
  2. Gulp watches for changes in LESS files and compiles it
  3. Gulp watch for changes in coffeescript files and runs webpack to compile them
  4. Browsersync watches for changes in compiled CSS and JS and reloads browser (or reloads just stylesheets)

This scheme with Cloud9 + browsersync + rails seems quite complicated so I made a little image of this solution:

Cloud9 + Browsersync + Rails 4

 

gulpfile.js looks like this:

/* jshint node: true */
var path = require('path'),

    gulp = require('gulp'),
    gulpif = require('gulp-if'),
    prefix = require('gulp-autoprefixer'),
    concat = require('gulp-concat'),
    minifyCSS = require('gulp-minify-css'),
    sourcemaps = require('gulp-sourcemaps'),
    util = require('gulp-util'),
    less = require('gulp-less'),
    svgstore = require('gulp-svgstore'),
    rename = require('gulp-rename'),

    broSync = require('browser-sync').create(),

    webpack = require('gulp-webpack'),
    webpackConfig = require('./webpack.config.js'),

    paths = {
        src: path.resolve(__dirname, './blocks'),
        dest: path.resolve(__dirname, '../back/app/assets')
    },

    production = util.env.stage === 'production' ? true : false;

gulp.task('js', function() {
    return gulp.src(paths.src + '/**/*.coffee')
        .pipe(webpack(webpackConfig))
        .pipe(gulp.dest(paths.dest + '/javascripts/'));
});

gulp.task('css', function() {

    return gulp
        .src([
            // starting point for all @imports
            paths.src + '/page/page.less'
        ])
        .pipe(sourcemaps.init())
        .pipe(less({
            // @include paths
            paths: [path.join(__dirname), 'node_modules']
        }))
        .pipe(concat('app.css'))

        // CSS autoprefixer
        .pipe(prefix([
            '> 7%',
            'Opera > 7%',
            'Safari > 7%',
            'Firefox > 7%',
            'Explorer > 7%',
            'Chrome > 7%'
        ], {
            cascade: true
        }))

        // minify for production
        .pipe(gulpif(production, minifyCSS()))

        // add sourcemaps for developing
        .pipe(gulpif(!production, sourcemaps.write()))
        .pipe(gulp.dest(paths.dest + '/stylesheets/'))

        // for changing CSS without page refresh
        .pipe(broSync.reload({ stream: true}));

});

// SVG icons for inlining in HTML
gulp.task('icons', function() {

    return gulp.src(paths.src + '/icons/*.svg')
        .pipe(rename({prefix: 'icon-'}))
        .pipe(svgstore({
            inlineSvg: true
        }))
        .pipe(rename('_icons.erb'))
        .pipe(gulp.dest(paths.dest + '/../views/layouts'));

});

gulp.task('watch', ['js', 'css', 'icons'], function() {

    broSync.init({
        proxy: '0.0.0.0:3000',   // rails server
        port: 8080               // cloud9 proxied port to 80
    });

    gulp.watch(paths.src + '/**/*.less', ['css']);

    gulp
        .watch(paths.src + '/**/*.coffee', ['js'])
        .on('change', broSync.reload);

    gulp
        .watch(paths.src + '/icons/*.svg', ['icons'])
        .on('change', broSync.reload);

});

gulp.task('default', ['css', 'icons', 'js']);

Project stub with all solutions available at https://github.com/DimitryDushkin/rails-gulp-webpack-browsersync.

This post and project were very helpful. Thanks to the author!

iOS Safari random images on page

If you have lots of static content on page from same domain like images and this domain works over HTTP (not HTTPS) you’ll probably face annoying issue on iOS Safari starting version 5.

The problem is Safari can mix images on page if there are a lot of them. For some reason Safari switches to HTTP pipelining mode when it is not closing connection to server and receives all resources via this connection. HTTP pipelining is the part of HTTP 1.1 standard, but it’s quite buggy and most of browsers vendors keep it turned off. Safari has some heuristic to turn it on in some cases.

Anyway it’s a bug, but it can be fixed in several ways:

  1. Use different domains to serve your static files. Seems like Safari not turning on pipelining in case for small amount of resource from same domain.
    or

  2. Serve your static over https. Seems like http pipelining is off for https.
    or

  3. Inline your static as base64 content. Avoid http requests at all 😉

This link helped a lot to understand what’s going on — http://tech.vg.no/2011/12/14/safari-on-ios-5-randomly-switches-images/

Hammer.JS, CSS touch-action and iOS Safari context menu

If you long press on image in iOS Safari (7, 8) after 500 ms you’ll get context menu (sometimes called “callout”). This menu give you “Save image” and “Copy image” actions.

I’ve faced some issues with this behaviour when implementing image viewer component with Hammer.JS 2.0.4

hammer.js rocks or raps...

hammer.js rocks or raps…

First of all turned out that Hammer.JS can set touch-action: none CSS rule on DOM node with specific recognizers (pinch, rotate) and uses polyfill for unsupported platforms such as iOS Safari 8.1.

touch-action: none; prevents showing of context menu. It’s not documented side effect of this style in iOS Safari 8.1 and Android Chrome 34. touch-action has no effect on context menu in most other browsers.

Second problem was that Hammer.JS also put -webkit-touch-callout: none on DOM node. The only purpose of this style is to prevent showing iOS Safari’s context menu.

I needed pinch recognizer for image size manipulation, but I also needed to show context menu on long tap on image. So that’s how I made this to work:

1. Remove setting of -webkit-touch-callout: none style on DOM node:

var mc = new Hammer.Manager(this.domElem[0], {
    cssProps: _.assign(Hammer.defaults.cssProps, { touchCallout: '' }),
    doNotPreventPress: true
});

2. Modified hammer.js sources to disable preventing touch event on long tap. Watch the commit in my fork of hammer.js.

Still my commit looks like a dirty hack so I’ll investigate in this problem in the future.

BetterSnapTool causes interface’s lag on Mac OS Yosemite

I had a bug that driving me crazy whole year that I upgraded to Mac OS Yosemite. During the working day my Macbook air’s interface became veeery laggy. Only restart helps.

Turned out that BetterSnapTool was the cause of the lag. I’ve closed it and suddenly everything works fine.

Still this program was very helpful, so I’m using now it’s free and open-source alternative — spectacle.

ModX on Linux + remote Microsoft SQL Server ? love

So I had to make such stack:

Linux (Debian) + Apache + PHP + ModX revolution with remote MS SQL Server

After several hours of googling I found out that this stack has no future for couple of reasons:

  1. Since ModX 2.1 uses Microsoft SQL driver (sqlsrv_* functions) which was released only for Windows platform.
  2. It’s possible to use workaround as FreeTDS, but all functions like “mssql_*” have to be mapped to”sqlsrv_*” functions. Some people tried this trick to run Drupal on Linux with external MSSQL, but it was quite unstable.

Good thing customer agreed to host application on Windows Azure, which offers IIS + MSSQL stack and modx works fine on it.

AngularJS: do not pass $index in ng-repeat if you use orderBy

If you pass $index in ng-repeat when you use orderBy you can perform some operation (“delete” for example) with wrong item. Examplanation below.

Incorrect

HTML:
<div ng-repeat="subtask in currentTask.subtasks | orderBy:'name'">
<button ng-click="remove($index)">Remove subtask</button>
</div>

JS:

$scope.remove = function(idx) {   //<--- idx - is index of subtask in sorted (!!!) array, it can be not the actual index of subtask
   // so you can remove not the subtasks you selected
   var st = $scope.currentTask.subtasks[idx];
   // remove from DB
   SubTask.remove({'subtaskId': st.id});
   // remove from local array
   $scope.subtasks.splice(idx,1);
}

Correct

HTML:

<div ng-repeat="subtask in currentTask.subtasks | orderBy:'name'">
<button ng-click="remove(subtask)">Remove subtask</button>
</div>

JS:

$scope.remove = function(subtask) {
    var idx = $scope.subtasks.indexOf(subtask);
    var st = $scope.currentTask.subtasks[idx];
    // remove from DB
    SubTask.remove({'subtaskId': subtask.id});
    // remove from local array
    $scope.subtasks.splice(idx,1)
}