Josh Bavari's Thoughts

Thoughts on technology and philosophy

In Depth Dive With Postgres Hstore and Rails 4

less than a 1 minute read

I had a unique little situation pop up that needed a little more flexibility when it came to storing data in the db. We use Postgres at RaiseMore because we respect ourselves, so naturally, I wanted to take a swing using some Hstore options for our ever changing data schema.

My intention is not to cover the basics of getting started with Rails 4 and Postgres HStore, so read at Honey Co or here at inopinatus to get started.

What I needed

Simple, to store a few bits of data about a communication – primarily the subject, message, date/time, and what networks it was sent out on. It may or may not have some of these fields, and the networks may or may not change in the future. This sounds perfect for HStore.

First I started with a simple schema:

1
2
3
4
5
CREATE TABLE comm_logs (
  id serial NOT NULL,
  user_id integer,
  history hstore
)

At first, I just wanted to store when a message was sent and on what networks. I figured I’d just have an AR model with one hstore column and everything would fit into that. It looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CommunicationLogging < ActiveRecord::Base
  def self.log(user_id, network, message, subject = nil)
      comm = CommunicationLogging.where('user_id = ?', user_id).first
      if comm.nil?
          comm = CommunicationLogging.new
          comm.user_id = user_id
          # Storing network as an array incase they decide to send to another network after this.
          comm.history = { 'message' => message, 'subject' => subject, 'network' => [network], 'time' => DateTime.now }}
      else 
          # Add history to array stored in network
          comm.history['network'].push(network)
      end
      comm.history_will_change!
      comm.save!
  end
end

Pretty easy right? I ran into some difficulties REAL fast. The first was from JSON serialization. When I did this:

1
2
3
# In Rails Console
CommunicationLogging.find(4).history['network']
=> "[\"facebook\", \"twitter\"]" 

It gave me a nice FAT serialized version of a Hash array. What good does a string do me? I want my object! BLEH!

We still have to get this thing working, so lets proceed anyway and just do some manual converions with PostgreSQL’s awesome to_json functionality.

Our result:

1
"{"c": "networks", "networks": "[\"facebook\", \"twitter\"]"}"

Cool, so its gonna be nasty still. Ok, how about just using Rails to_json method:

1
"{\"id\":11,\"event_user_id\":null,\"history\":null,\"networks\":{\"c\":\"networks\",\"networks\":\"[\\\"facebook\\\", \\\"twitter\\\"]\"}}"

Thanks, but no thanks. I’m pretty set on having an array of values instead of some manual labor on converting those values.

I was learning real fast that storing arrays in hstore was going to be a challenge. The next stab I wanted to take was to simplify the storage and retrieval as well as using natural arrays.

NOTE: I changed the way I attacked the problem here partly because I was doing it wrong. Hopefully you can learn from my mistakes? :–)

This was my next stab, altering the table structure just a bit:

1
2
3
4
5
6
CREATE TABLE communication_logs (
  id serial NOT NULL,
  user_id integer,
  history hstore[]
  networks hstore
)

What I did here was harness the awesome power of PostgreSQL’s arrays and hstore. Taking this thing to the limit. I did this for two reasons:

  • Constraints changed, we could send multiple communications out on multiple networks at any given time
  • I wanted a log of history with whatever pieces of data may or may not be there
  • Be able to quickly get the networks sent out per user

Now my AR model is decorated with store_accessor to give me model attributes for the networks. It also stores the log of messages in an array naturally through the model, and accessed as a real HSTORE with array values (instead of json, yay).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class CommunicationLogging < ActiveRecord::Base
  store_accessor :networks, :facebook, :twitter, :email, :sms, :push

  def set_network(network)
      # for brevity only one listed
      case network.downcase
      when 'twitter'
          self.twitter = true
      end
  end

  def self.log(user_id, network, message, subject = nil)
      comm = CommunicationLogging.where('user_id = ?', user_id).first
      if comm.nil?
          comm = CommunicationLogging.new
          comm.user_id = user_id
      end

      comm.set_network(network)

      history_log = { 'message' => message, 'subject' => subject, 'network' => network, 'time' => DateTime.now }
      comm.history.push(history_log)


      comm.networks_will_change!
      comm.history_will_change!
      comm.save!
  end
end

So what happens when I to_json my fields now in PostgreSQL?

1
2
3
4
5
// Networks
"{"twitter": "true"}"

// History
"[{"time": "2014-04-27T10:15:50-05:00", "message": "asf", "network": "twitter", "subject": "asd"}]"

And rails?

1
2
3
4
5
# Networks
"{"twitter":\"true"}" 

# History
"[{"time":"2014-04-27T10:15:50-05:00","message":"asf","network":"twitter","subject":"asd"}]" 

Beautiful.

I had a lot of fun using PostgreSQL and Rails 4. I didn’t find a lot of in-depth knowledge on it so I wanted to shed some light on the topic and hope this would push someone else who might be considering to try it out to give it a go.

I’d like to follow up this post with another article on how to search these bad boys.

In closing:

  • I will definitely use hstore more in the future. I like the freedom to just store whatever I want
  • I need to reach out to the Rails team and see what I can do about improving the array support in Active Record
  • I hope you try it out as well

Additional Resources

Enabling Android Remote Debugging in Phonegap 2.9

less than a 1 minute read

Recently the Cordova (as well as Phonegap) team has introduced the ability to remote debug Android 4.4+ from this issue.

If you’re like us, you have a lot of invested technology into Phonegap 2.9, and are actively making the move to Cordova 3.4. In the real world, you aren’t always granted conveniences to move to the latest and greatest all at one time when you have released products. Raymond Camden did a survey and apparently there are still some out there utilizing Phonegap/Cordova 2.9.

I wanted to show how easy it was our team to backport some of the changes needed to enable Remote Debugging in Phonegap 2.9.

You’ll need to make a few changes to your MainActivity.java file. The first is an import statement, and the second is to make these changes near the bottom of the onCreate method:

1
2
3
4
5
6
7
8
9
10
11
// At the top near your other import statements:
import android.webkit.WebView;
// ..snip..!

public void onCreate(Bundle savedInstanceState) {
    // ..snip..
    // Enable web debugging
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
      WebView.setWebContentsDebuggingEnabled(true);
    }
}

One thing to note, this only works for Android versions 4.4 or greater! If you aren’t using Android API 19 or greater, you’ll have to keep using Weinre.

Now just start your app, enable USB debugging, open Chrome, go to about:inspect, and enjoy your remote debugging in Phonegap/Cordova 2.9! Cheers!

Apachecon 2014

less than a 1 minute read

This year I was fortunate enough to attend ApacheCon 2014 in Denver CO. It was a big adventure for me, partyly due to one of my main goals this year was to contribute more to open source. I’ve been actively contributing to the Apache Cordova project, so this was my opportunity to meet some of the Cordova contributors.

The conference kicked off first with Steve Gill’s (@stevesgill) talk on the Cordova CLI and the work flow with it. Steve is in charge of all the releases for Cordova, and we’ve worked together on the cordova plugins registry since the beginning of this year. Steve really pulls this team together, as he’s a stand up guy and really easy to talk to. I, for one, am very thankful to have him be a part of the Cordova team!

The next talk was by @doncoleman over Bluetooth LE and how to use it with the Cordova platform. This presentation really opened my eyes to what the Cordova project is capable of (not that I hadn’t known, but he sure opened up some ideas), and much more so with Don’s awesome Cordova Plugin – RFDuino, as well as his NFC Plugin that he presented later in the day. Not only can you write your Bluetooth/NFC items once, but they can run across platforms. Way awesome.

I was then lucky enough to see Andrew Grieve’s @GrieveAndrew talk over the Cordova Development Lifecycle – it amazes me that the Cordova project is composed of around 50+ repos, and releases can take days! Wow. Theres’s a lot that goes into the Cordova project.

Hazem Saleh then spoke about Javascript and Cordova – and how they fit together with the native bridges quite easily. He also quizzed the audience over some Javascript gotchas. Hazem also spoke to what it is like outside of the US – where there are other phones us American’s don’t really get to play with. Now that is global support, way to go Cordova!

Finally, Lisa DeLuca gave a very informative presentation on how her and the other open source members around the world translate all of the Cordova documenation in a ton of different languages. It’s interesting – first they take the English documents, upload to Crowdin – which then handles either human translation (preferred) and even gives the option for automated translation (not perfect).

Finally, there was the hackathon held by Red Hat and IBM. It was awesome getting to sit with 8 of the core Cordova team members and hacking on my own project as well as getting very familiar with the code base. It was a truly exciting time for me.

Hopefully I’ll get the opportunity to work with these fine folks more in the future! Cheers!

How I Use Npm Link

less than a 1 minute read

Throughout the last few months, I’ve began helping out the Cordova team with a few bug fixes that I come across and want to contribute back.

Cordova is a host of different components, many of which rely on node.js modules from npm.

When I first started wantint go help, I didnt know how to make changes to the cordova code to use globally, and I didnt want to just go modify the code in the global node_modules/cordova-cli folder. This post covers on how to get started modifying existing node modules you may use from npm.

Why?

Recently, I came across a small bug in Cordova CLI 3.4. To begin to poke around the code to find a fix in the cordova-cli project.

When I first started, I had to figure out how to get access to the verion pulled from the repo on Github to use my code instead of the globally installed cordova.

How I use npm-link

  • Uninstalled the currently used cordova cli, npm uninstall -g cordova
  • Forked the cordova-cli repo from github
  • git clone git@github.com:jbavari/cordova-cli.git
  • Changed directory to the repo I just cloned
  • Ran npm link

After running npm link, I can now run cordova create HelloWorld from any directory, and it will use the code that I just npm link’d.

Now, when I make changes to that repo that I cloned on my machine, I can run it immediately instead of having to push or update the package in npm.

Hope this helps others in trying to get started contributing to any projects that they might use from npm! (Or contributing to the Cordova project!)

Properly Using Plugins With Variables in Phonegap / Cordova Applications

less than a 1 minute read

In the past week I’ve been working on upgrading our mobile application from PhoneGap 2.9 to Cordova 3.4. (There is no real reason for going from PhoneGap to Cordova, just that I’ve been actively committing to the Cordova project).

I’ve come in contact with a weird bug with the Cordova CLI when using plugins that require variables to install them.

Currently Holly Schinsky and Dan Moore recommend using the Cordova Hook after_platform_add to add in the plugins via a plugin list. While I too think this is a great idea, there is a small problem with it as of Phonegap/Cordova 3.4 when you use plugins with variables.

Before we go further, lets just get a quick intro to the platform hook calls that the Cordova CLI gives us. First it will fire off the scripts in the /app/hooks/before_platform_add (or /app/.cordova/hooks/before_platform_add if you’re on PhoneGap/Cordova <= 3.3) folder.

Next, Cordova CLI will go through some configs and eventually use a function named call_into_create that will do some housecleaning, but most importantly will re-install currently installed plugins.

Certain plugins will require variables for them to be installed. Take a look at the Phonegap Facebook Connect plugin, for example: cordova plugin add https://github.com/phonegap/phonegap-facebook-plugin.git --variable APP_ID='appId' --variable APP_NAME='appName'. Using Holly and Dan’s solution, you an add that inline as well, like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var pluginlist = [
    "https://github.com/phonegap/phonegap-facebook-plugin.git --variable APP_ID='" + appId + "' --variable APP_NAME='" + appName + "'",
    "org.apache.cordova.camera",
    "org.apache.cordova.console",
    "org.apache.cordova.contacts"];

var fs = require('fs');
var path = require('path');
var sys = require('sys')
var exec = require('child_process').exec;
 
function puts(error, stdout, stderr) {
    sys.puts(stdout)
}
 
pluginlist.forEach(function(plug) {
    exec("cordova plugin add " + plug, puts);
});

The problem is, when Cordova CLI goes through to reinstall the plugins, it isn’t passing in the variables and will fail to install them, but it will still add the folder to the plugins directory and json file. In fact, after adding the plugins, it will then call the hook (as seen above) and try to install plug-ins yet a second time. And by fail, I mean it will still fail with the facebook plugin – cordova CLI failed therefore it’s half installed, so the hook will fail. (I hope this is clear)

To recap, here’s the flow of how to reproduce the problem:

  • Create new project with cordova create
  • Add platform for ios
  • Add plugin with variables – cordova plugin add https://github.com/phonegap/phonegap-facebook-plugin.git --variable APP_ID='appId' --variable APP_NAME='appName'
  • Add platform for android

That’s when you’ll see:

1
2
3
4
5
6
7
8
9
Installing com.phonegap.plugins.facebookconnect (android)
Error: Variable(s) missing: APP_ID, APP_NAME
    at /usr/local/lib/node_modules/cordova/node_modules/plugman/src/install.js:252:29
    at _fulfilled (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:798:54)
    at self.promiseDispatch.done (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:827:30)
    at Promise.promise.promiseDispatch (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:760:13)
    at /usr/local/lib/node_modules/cordova/node_modules/q/q.js:574:44
    at flush (/usr/local/lib/node_modules/cordova/node_modules/q/q.js:108:17)
    at process._tickCallback (node.js:415:13)

You’re probably wondering – why not add both of the platforms first, then add the plugin? That’s what I recommend doing now.

However, going back to Holly and Dan’s post, I highly recommend if you have any plugins with variables that you do a little clean-up of plugin’s before adding them. And by clean-up, I mean, remove the plugins before adding them.

This should only be an issue until the awesome Cordova team fixes the issue of adding plugins correctly.

Hope this helps!

RethinkDB the Importing Json Process

less than a 1 minute read

I was playing around tonight with RethinkDB – and I wanted to import some data from a JSON file. It wasn’t very well documented, so I wanted to make sure I high-light that for others. The tutorial post to see RethinkDB’s import covers this command to fire off:

1
rethinkdb import -c localhost:28015 --table registry.plugins --pkey _id -f plugins.json --format json

It’s real easy – lets break it down:

  • The first part, -c localhost:28015 specifies the cluster host and port.
  • The next, --table registry.plugins specifies the database ‘registry’, and the table ‘plugins’.
  • Next up is specifying the primary index key of ‘_id’ with --pkey _id.
  • Finally, -f plugins.json --format json tells Rethink to pull in the file located in the CWD named plugins.json, in the jason formats!

Chances are, you might not have that Python driver, which gives you this message:

1
2
3
4
Error when launching rethinkdb-import: No such file or directory
The rethinkdb-import command depends on the RethinkDB Python driver, which must be installed.
Instructions for installing the RethinkDB Python driver are available here:
http://www.rethinkdb.com/docs/install-drivers/python/

If thats the case, run the command to install the rethinkdb-import Python driver:

1
sudo pip install rethinkdb

Boom! Now get back to work.

Nodejs Rails Subdomains - POW!

less than a 1 minute read

I wanted to do a quick write up of how I use Pow to run several rails/rack apps and provide subdomains for the rails / rack apps as well as node.js servers! I’ll even throw in a Livereload plug in Rails/rack.

The why

We have several rails projects – an API server, a queue’ing rails project running Sidekiq, an admin dashboard using Rails & angular, and a node.js server running a PhoneGap mobile app run in express.

I needed an easy domain set up, something like:

  • project.dev
  • my.project.dev
  • jobs.project.dev
  • m.project.dev

I also needed Livereload due to the time it saves, who doesn’t right? I do ok, quit asking questions. (But seriously please comment and ask)

What I did

After installing POW, I created a few symbolic links and a file containing the port that I would run the node.js server. As seen in the the POW documentation and listed in the same order as above.

1
2
3
4
5
6
ln -s ~/Dev/RM/web ~/.pow/project

ln -s ~/Dev/RM/my ~/.pow/my.project

ln -s ~/Dev/RM/jobs ~/.pow/jobs.project
echo 5000 > ~/.pow/m.project

Awesome. So now Pow got the rails/racks apps up. Just need to start my node server by simply running node app.js

Ensure its there

Now that I’ve got the servers all running, i’ll hit them up to check A-OK. Navigate to http://my.project.dev, http://m.project.dev, http://project.dev. Yep, all A-OK.

Now to set up Livereload.

Snag up a few gems to help Rails/rack do some livereloads automatically for me.

1
2
3
gem 'rack-livereload'
gem 'guard'
gem 'guard-livereload'

Two things to configure before livereload will work in the rails/rack apps – the first is a config setting in our development.rb file and our Guardfile with a host definition.

The first is development.rb:

1
2
3
4
5
MyProjectCom::Application.configure do
  #comments
  config.middleware.use Rack::LiveReload
  #the comments are a lie
end

Now the magic for the rails/rack apps is the Guardfile:

1
2
3
4
5
6
7
8
guard 'livereload', host: 'my.project.dev'do
  watch(%r{app/views/.+\.(erb|haml|slim)$})
  watch(%r{app/helpers/.+\.rb})
  watch(%r{public/.+\.(css|js|html)})
  watch(%r{config/locales/.+\.yml})
  # Rails Assets Pipeline
  watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" }
end

Boom! Now I’ve got Livereload being injected into each of the rails/rack apps I configured and handling all the other business. I just have to sit back and code.

Hope this helps any who may be wanting a similar set up with subdomains with rails/rack & node.js hosted apps.

How to Be a Good Imitator in PhoneGap/Cordova

less than a 1 minute read

If you found your way here, then you’re probably seeking knowlege about Phonegap/Cordova. I want to first remind you what the purpose of the PhoneGap / Cordova project is:

The ultimate purpose of PhoneGap is to cease to exist.

What this means is – you should really start getting ready to make Native-like apps regardless of what technology is used.

How can we give native apps a better feel from PhoneGap / Cordova? Or stated another way, what are some tricks we can use to make the application feel more native?

The answer then, is to be a better imitator, you must first understand what you are imitating.

The chameleons change color to their surroundings

Simply stated – you adapt to what your surroundings are.

Our surroundings for PhoneGap / Cordova are quite wide:

  • iOS
  • Android
  • Windows Phone
  • BlackBerry
  • Firefox OS

That is a lot to adapt to. It may take a little time to familiarize yourself with each platform. If you do though, your users will thank you.

Some suggestions

  • Put navigation items near the top in Android, for iOS put them near the bottom. Users are used to having their navigation in the same place.
  • In Android / Windows Phone – mind the Back button! Users use their phones natively, and if you try to change this, they will hate your app.
  • Dont use the same button styles for each platform! Buttons on iOS do not look like buttons on Android – if you try this though, your users will notice and find it odd. See these great examples from Ratchet.js (look for the Base/iOS/Android buttons near the top)
  • Use Native-like loading animations. iOS generally has a small circle – if you try using this instead of your own custom loading icon, users will most likely blame their phone over your app. See this study by Facebook for loading animations
  • Test on actual devices. You cant get a native feel from clicking on a simulator.

References:

Performance UX Considerations for successful PhoneGap apps

Tips for getting that native feel

Its Not the Tools - Its You

less than a 1 minute read

Reminder – you write the code, not the tools

Duh, right?

There’s a lot of debate I see about people saying you have to use a certain language or tool to write good software or create apps. This is simply not the fact.

Case and point – if you write an app in iOS and it works great, the user simply does not care if it was written in Objective-C, PhoneGap, or some other hybrid tool. (NOTE: unless the user is a troll)

I personally do a lot of work lately with Cordova/PhoneGap. I actively contribute to Cordova as well. When I hear purists come down on the technology, I really wonder why.

Certainly, there can be some bad apps created with it. The same can be said for Objective-C! Whatever the tool/language is, it can always be used in the wrong or inefficient way.

The truth is, there are some awesome technologies coming out from all kinds of directions. What makes the tech better? The tools/languages/software used, or the overall look/feel/usage?

Why am I writing this?

Lately, I’ve been trying to grow as a developer and really fine tune my craft. I’m writing this to remind myself & hopefully remind others, any products/apps we make are only as good as we can make them. NOT the tools we use to make them. (NOTE: sometimes a tool can improve performance, or some other aspect, but its still up to you to learn and adapt)

If your app/product sucks, its your fault

That’s right – true responsibility.

Think about this – these days developers can make awesome, usable softare with only a text editor and some command line tools.

Now, given some additional IDEs, SDKs, Frameworks, ETC., the same developer could make more software, generally quicker. Was it the tools that made the finalized product awesome?

That being said, if you are using a tool/system/framework, and it truly does suck – guess what? You are free to change it!

The weakness is you

What makes someone great at anything is when they know what they are weak at and actively improve on those weaknesses.

One thing I wanted to focus fire this year was hitting my weak points.

It’s always difficult to take a true honest look at yourself and evaluate what actions you should take. However, if you really want to improve and get ahead, there’s no other way.

Some of my weaknesses

  • Writing tests for my code & taking a TDD approach
  • Actively testing my product (thoroughly)
  • Reading and keeping up to date with knowledge (blogs/books/etc)
  • Communicating and expressing my ideas

What I’m trying

  • Expecting small improvements.
  • Iterate quickly.
  • Evaluate often.
  • Always be learning.

What I’m doing

  • Starting to write tests for older code & refactoring when needed, especially in Javascript & Ruby
  • Getting together active test runners for iOS, Android, and the Web (CasperJS).
  • Evaluating weekly my tests I’m writing – Using Code climate to evaluate the quality/complexity of our code
  • Looking at other open source projects and how they write tests
  • Reading some BDD/TDD books to learn

The big picture

Next time you are getting frustrated with your tools, programming language, etc – remember, it’s up to you to improve it, or improve yourself. Cheers.

Grunt.js Orchestration & Organization of Applescript Actions

less than a 1 minute read

Lately I’ve learned a little about AppleScript. If you’re not familiar, I suggest reading the AppleScript Language Guide to get a little more background information about it. In a nutshell, however, its basically a scripting language developed by Apple to do Inter-application communication using AppleEvents (yes, I used the language from the Wiki page).

In the last few months, I’ve been absolutely obsessed with automating the boring tasks that wear me out. This is just another platform to add automation events. Imagine making a script that will open all your dev programs, set them up how you want them, launch a build process, focus a device simulator, and then begin clicking around that simulator. That is my vision for now.

In this post, I’d like to address some ways I’m leveraging AppleScript to automate some of my boring tasks. I won’t focus much on the syntax, or how to navigate the language, but rather the methods I’ve used to learn this knowledge. I’ll be using Grunt.js to handle all my orchestration of applescript actions.

Requirements

You must be using an Apple device such as a Mac. That’s about it

A Handy Tip

You might want to ensure you have Accessibility options turned on. You can learn about how to do that here

Getting started

It’s really easy folks, just pop open your favorite text editor or use the native AppleScript Editor and start cranking it out.

Let’s look an easy script that will do the following:

  • Execute XCode run command
  • Open Safari
  • Run the Web Inspector for the Simulator running
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
on open_developer_window()
  try
      tell application "Safari"
          activate
      end tell
      tell application "System Events"
          tell process "Safari"
              tell menu bar 1
                  tell menu bar item 8
                      tell menu 1
                          tell menu item 4
                              tell menu 1
                                  click menu item 2
                              end tell
                          end tell
                      end tell
                  end tell
              end tell
          end tell
      end tell
      return "Yo good job"
  on error error_message
      return error_message
  end try
end open_developer_window

The problem

After finding out how powerful AppleScript is, I began cranking out scripts for all my boring tasks. It became pretty nasty to manage having hundreds of lines into a single script.

The good news is, AppleScript has a way to import other scripts to use their methods, just by calling load script <name>.

The bad news is, it gets pretty sloppy trying to do the applescript ‘load script’ command, as it became pretty nasty pretty quick.

Thats where Grunt came in. I figured instead of having a few include scripts – make the applescript files have just the functionality I wanted

Using Grunt Shell

I chose to use the Grunt shell plugin to execute these scripts. You can easily do it over shell by executing osascript <name_of_script>.

What I did was create a nice layout for my applescript tasks as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Applescripts
|
+---+--iPhone simulator tasks
  |  |
  |  +-- reset_simulator.applescript
  |
  +--safari tasks
  |  |
  |  +-- start_safari_dev_console.applescript
  |  +-- dev_console_test_runner.applescript
  |
  +--XCode tasks
     |
     +-- open_ios_project_file.applescript
     +-- run_simulator.applescript
     +-- select_simulator_6.applescript

Then, I created Grunt tasks for each applescript task as I wanted, as such:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
grunt.initConfig({
  shell: {
      start_safari_dev_console: {
          command: ['osascript ./applescripts/safari_tasks/start_safari_dev_console.applescript'].join("&&")
      },
      dev_console_test_runner: {
          command: ['osascript ./applescripts/safari_tasks/dev_console_test_runner.applescript'].join("&&")
      },
      open_ios_project_file: {
          command: ['osascript ./applescripts/xcode_tasks/open_ios_project_file.applescript'].join("&&")
      },
      run_simulator: {
          command: ['osascript ./applescripts/xcode_tasks/run_simulator.applescript'].join("&&")
      },
      simulator_6: {
          command: ['osascript ./applescripts/xcode_tasks/simulator_6.applescript'].join("&&")
      },
      reset_simulator: {
          command: ['osascript ./applescripts/simulator_tasks/reset_simulator.applescript'].join("&&")
      }
  }
});

Followed by a few set of tasks:

1
2
3
4
5
grunt.registerTask('run_sim', ['shell:run_simulator', 'shell:start_safari_dev_console']);

grunt.registerTask('start_project', ['shell:open_ios_project_file', 'shell:simulator_6', 'run_sim']);

grunt.registerTask('restart_sim', ['shell:reset_simulator', 'run_sim']);

Useful tools

After learning the basic structure of AppleScript, it’s easy to see how the structures are laid out. The hard part, is figuring out how they are actually laid out. There’s an awesome built in tool, called “Accessibility Inspector”. This tool will tell you the control you are focusing on an application.

The second tool that is EXTREMELY helpful is a tool called UI Browser. This is a tool that you can select an application, hover over that application, and UI Browser will tell you that controls. It even has a drop down of common actions, like clicking, filling in text, and other common uses.

Overall results

Altogether, I’m pretty happy with how my little Applescript trials went. It’s very easy now to chain together applescript tasks using Grunt.

On a side note.. Apparently applescript is already used in some grunt plugins, as I’ve found tucked away in some projects code.

Happy coding.