0.0
No commit activity in last 3 years
No release in over 3 years
MagicCarpet renders any view with stub data from your front-end test suite (ie. jasmine) so you can test your js against the real world.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

>= 3.2
 Project Readme

Magic Carpet

Magic Carpet takes your templates on a magical trip to your JavaScript tests.

This ensures that if your templates change, your JavaScript tests will break too. No more stubbed markup and clicking around in the browser to make sure your JavaScript works in "real life."

Magic Carpet can:

  • render templates and partials in the context of any of your controllers
  • set the state of any of those controllers (params, instance variables, etc)
  • set local variables for the templates
  • pretty much do whatever you want with your controller's render method
  • append your templates to the DOM for you
  • be synchronous or asynchronous

It also doesn't monkeypatch anything, so nothing weird will start happening during your Ruby tests or development.

Installation

Add this line to your application's Gemfile (probably in the test and development groups):

gem "magic_carpet"

And then:

$ bundle

Or install it yourself like this:

$ gem install magic_carpet

Setup

Add this to your routes file:

# config/routes.rb
mount MagicCarpet::Engine => "/magic_carpet" if defined?(MagicCarpet)

Pulling in the companion JavaScript

Magic Carpet Js Dependencies: jQuery (only for MagicCarpet.request; also load order doesn't matter).

Anyway, you can throw this:

//= require magic_carpet/magic_carpet

wherever the Asset Pipeline will pick up on it. If you're using jasmine-rails, you can put it at the top of any file in spec/javascripts/helpers. Or you can drop the this file into wherever your test framework of choice will pick up on it.

Without the companion JavaScript

Just make a get request to /magic_carpet with whatever paremeters you need to set the proper state on the controller to prepare the template.

Usage

Just request the template you want with the specified state on the controller like so:

MagicCarpet.request({
  controller_name: "Wishes",
  action_name: "index",
  instance_variables: {
    wishes: [
      { id: 1, model: "Wish", text: "more wishes" },
      { id: 2, model: "Wish", text: "more genies" },
      { id: 3, model: "Wish", text: "more wishes and genies" }
    ]
  },
  locals: {
    truncate_text: "true"
  }
});

Here we're just grabbing the index template from the WishesController and making sure that the @wishes is set to a collection of Wish objects so the template doesn't blow up when it's rendered. We're also setting a local variable called truncate_text to true. action_name and template are completely interchangeable:

MagicCarpet.request({
  controller_name: "Wishes",
  template: "plain"
});

Also note that Magic Carpet never runs any controller actions or hits any of your routes. It just munges options and sets state before calling render.

You can also grab partials:

MagicCarpet.request({
  controller_name: "Wishes",
  partial: "some_partial"
});

You can also have partial rendered with a collection and even pass it the as option (sometimes the as option is necessary):

MagicCarpet.request({
  controller_name: "Wishes",
  partial: "wish",
  collection: [
    { id: 1, model: "Wish", text: "wish text" },
    { id: 2, model: "Wish", text: "boring wish text" },
    { id: 3, model: "Wish", text: "last of the boring wish text" }
  ],
  as: "wish"
});

Magic Carpet can also handle associations:

MagicCarpet.request({
  controller_name: "Wishes",
  action_name: "show",
  instance_variables: {
    wish: {
      id: 1,
      model: "Wish",
      text: "more wishes",
      user: { id: 3, name: "Al" }
    }
  }
});

The type coercion is recursive, so feel free to nest associations and data structures as deeply as you like (although, if you need to do too much of that, your template might be a bit too complicated).

You can also set the params, flash, session, and cookies the template sees like so:

MagicCarpet.request({
  controller_name: "Wishes",
  template: "upon_a_start",
  flash: {
    success: "Woohoo!"
  },
  cookies: {
    user_id: 5
  },
  params: {
    wish_id: 10
  },
  session: {
    token: "TOP SECRET"
  }
});

Synchronous and Async

By default MagicCarpet.request is synchronous, but you can make it asynchronous by changing MagicCarpet.asyc to true. MagicCarpet.asyncComplete will be true when the request is finished.

Your Template and the DOM

By default, MagicCarpet appends a div with an id of magic-carpet to the body of the page and puts the requested template in there. If you'd rather have your template put somewhere else, just change MagicCarpet.sandbox to the html node that you want to contain the template.

Clean Up

You can empty the div that MagicCarpet puts templates in by calling MagicCarpet.emptySandbox().

Caching Responses

Magic Carpet will cache responses so if you request the same template the same way a bunch of times it will skip unnecessary extra calls to the server. If you want to empty the cache for whatever reason, just set MagicCarpet.cache to an empty object.

All Options

Here's a list of all the options you can pass MagicCarpet.request:

  • controller_name
  • action_name
  • template
  • partial
  • locals
  • instance_variables
  • collection
  • as
  • params
  • session
  • flash
  • cookies
  • layout

Type Coercion

Booleans and Nil

Booleans and nil can be coerced by simply sending a string of their name, ie "true", "false", and "nil".

"Models"

Models is in quotes above because the object doesn't necessarily need to be and ActiveRecord object or ActiveModel at all. Magic Carpet grabs the specified 'model' class and passes the rest of the hash into the model class's new method. Like this:

// You send an object that looks like this:
{
  model: "Wish",
  id: 4,
  text: "howdy",
  some_other_attribute: "value"
}

And Magic Carpet essentially does this:

Wish.new({
  id: 4,
  text: "howdy",
  some_other_attribute: "value"
})

OpenStruct is a good candidate when you need a stubbed object.

Numbers

If you need a number to actually be a number (ie not a String), just tell Magic Carpet:

// any of these
{ number: "5.5" }
{ number: 5.5 }
{
  number: 5.5,
  integer: true
}
// or these
{
  number: 5.5,
  integer: false // doesn't matter what's here, if the integer key is present, you get an integer.
}
// or these
{
  number: 5.5,
  integer: "anything"
}

Without the integer option, it will coerce the type via to_f, with the integer option present it will use to_i.

Dates, Times, and DateTimes

Dates, Times, and DateTimes will all be parsed according to there respsective parse methods. Just use date, time, or datetime as a key that points to a parsable string (MagicCarpet.request will toString() whatever you give it, so raw Date objects or moment objects are fine too).

Times also have a utc option you can pass them (it doesn't matter what the key points to, its presence will trigger the behavior). It just calls utc on the Time object.

// any of these
{ date: "Sat Apr 12 2014 20:44:24 GMT-0500 (CDT)" }
{ datetime: "Sat Apr 12 2014 20:44:24 GMT-0500 (CDT)" }
{
  time: "Sat Apr 12 2014 20:44:24 GMT-0500 (CDT)",
  utc: true
}
// or these
{ date: new Date() }
{ datetime: new Date() }
{
  time: new Date(),
  utc: false
}
// or these
{ date: moment() }
{ datetime: moment() }
{
  time: moment(),
  utc: "anything"
}

Errors

MagicCarpet will throw descriptive errors in the JavaScript and in your server logs if something breaks in your template, if you've attempted to render a template that doesn't exist, you've sent an unparseable date string, or if something else goes wrong. If you read the errors it should be easy to figure out what's wrong. If it's not, create a new issue here or make a pull request.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request