• Stars
    star
    1,184
  • Rank 39,512 (Top 0.8 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 11 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rails inspired REST-API ORM for Angular

Angular Restmod

Angular Restmod is open source software kindly sponsored by:

Supporter

(We need a sponsor for this library, please write to [email protected] if you want to help.)

![Gitter](https://badges.gitter.im/Join Chat.svg)

Build Status Code Climate Stories in Ready Bower version

Restmod creates objects that you can use from within Angular to interact with your RESTful API.

Saving bikes on your serverside database would be as easy as:

var newBike = Bike.$build({ brand: 'Trek' });
newBike.model = '5200';
newBike.$save(); // bike is persisted sending a POST to /bikes

It also supports collections, relations, lifecycle hooks, attribute renaming, side data loading and much more. Continue reading for a quick start, check this presentation for an overview or check the API Reference for more: http://platanus.github.io/angular-restmod

If you are working with Ruby on Rails, we recommend active_model_serializers for seamless integration.

Why Restmod?

Restmod brings Rails ActiveRecord's ease of use to the Angular Framework. It succesfuly combines Angular's encapsulated design with Active Record's opinionated style. There are other alternatives available though:

  • $resource: Might be enough for small projects, included as an Angular opt-in. It only provides a basic model type layer, with limited features.
  • Restangular: very complete library, but does not propose a model layer and does not support linked resource responses as seen on jsonapi.org.
  • angular-activerecord: Nice alternative to $resource, still very limited in its functionality.
  • ModelCore: Inspired in angular-activerecord, provides a more complete set of features but lacks testing.

Restmod its thoroughly tested against the same platforms as AngularJS using SauceLabs awesome OpenSauce service!

Getting Started

1. Get the code

You can get it straight from the repository

git clone [email protected]:platanus/angular-restmod.git

but we recommend you to use bower to retrieve the Restmod package

bower install angular-restmod --save

or if you prefer, a npm package is also available

npm install angular-restmod -d

2. Include it on your project

Make sure the restmod source is required in your code.

<script type="text/javascript" src="js/angular-restmod-bundle.min.js"></script>

Next, include angular module as one of your app's dependencies

module = angular.module('MyApp', ['restmod'])

REST API Integration

Restmod comes bundled with various (well, just one for now) predefined API-style-mixins to choose from depending on your backend configuration.

Check out the Style listing for more information. We are looking for contributors on this!!

If you dont set a base style a 'No API style base was included' warning will be generated, see the link above for more information.

If you still need to change some behaviour or if you want to create your own api style, the following configurations are available:

  • Common url prefix configuration
  • Primary key name configuration
  • Json root property configuration
  • Json metadata extraction
  • Json side data resolving for jsonapi.org style APIs (for apis using 'links')
  • Request customization
  • Url formatting options

Make sure you read the Api Integration FAQ before starting your API integration!

Basic usage

You begin by creating a new model using the restmod.model method. We recommend you to put each model on a separate factory. The first argument for model is the resource URL.

module.factory('Bike', function(restmod) {
	return restmod.model('/bikes');
});

The generated model type provides basic CRUD operations to interact with the API:

To retrieve an object by ID use $find, the returned object will be filled with the response data when the server response is received.

Let's say you have a REST API that responds JSON to a GET REQUEST on /bikes/1

{
	"id": 1,
	"brand": "Trek",
	"created_at": "2014-05-23"
}

Then, on your code you would call

bike = Bike.$find(1);

Right after this line executes, the bike object is an empty object. The bike object will be populated as soon as the API returns some data. This works great with Angular's way. Nevertheless, you can use $then to do something when data becomes available.

bike.$then(function() {
	expect(bike.brand).toBeDefined();
});

If you need to pass additional parameters to $find, you can use the second function argument.

bike = Bike.$find(1, { includeParts: true });

To reload an object use $fetch. WARNING: This will overwrite modified properties.

bike.$fetch();

If you only want to retrieve an object data if it hasn't been retrieved yet, use $resolve instead of $fetch:

bike.$resolve();

To mark an object as unresolved call $reset. You can hook to the before-resolve event to add some expiration logic for resolved objects, just call $reset inside the hook to force the object to be retrieved.

Remember to use $resolve().$asPromise() if you are returning inside a resolve function.

To retrieve an object collection $collection or $search can be used.

bikes = Bike.$search({ category: 'enduro' });
// same as
bikes = Bike.$collection({ category: 'enduro' }); // server request not yet sent
bikes.$refresh();

To reload a collection use $refresh. To append more results use $fetch.

bikes = Bike.$collection({ category: 'enduro' });
bikes.$refresh({ page: 1 }); // clear collection and load page 1
bikes.$fetch({ page: 2 }); // page 2 is appended to page 1, usefull for infinite scrolls...
bikes.$refresh({ page: 3 }); // collection is reset, page 3 is loaded on response

To update an object, just modify the properties and call $save.

bike = Bike.$find(1);
bike.brand = 'Trek';
bike.$save();

To create a new object use $build and then call $save. This will send a POST request to the server.

var newBike = Bike.$build({ brand: 'Comencal' });
newBike.model = 'Meta';
newBike.$save(); // bike is persisted

To patch an object, just modify the properties and call $save passing an array of properties to be patched as first argument.

bike = Bike.$find(1);
bike.brand = 'Trek';
bike.model = 'Slash';
bike.dim = { width: 10.0, height: 10.0 };
bike.$save(['brand', 'dim']); // will only send brand and dim (every sub property)

To specify a single subproperty to be sent in patch, use dot notation:

bike = Bike.$find(1);
bike.brand = 'Trek';
bike.model = 'Slash';
bike.dim = { width: 10.0, height: 10.0 };
bike.$save(['dim.height']); // will only send dim.height

Or use $create

var newBike = Bike.$create({ brand: 'Comencal', model: 'Meta' });

If called on a collection, $build and $create will return a collection-bound object that will be added when saved successfully.

newBike = bikes.$create({ brand: 'Comencal', model: 'Meta' });
// after server returns, the 'bikes' collection will contain 'newBike'.

To show a non saved object on the bound collection use $reveal

var newBike = bikes.$create({ brand: 'Comencal', model: 'Meta' }).$reveal();
// 'newBike' is inmediatelly available at 'bikes'

Finally, to destroy an object just call $destroy.

bike.$destroy();

As with $create, calling $destroy on a record bound to a collection will also remove it from the collection on server response.

All REST operations described above use $q promises that are fulfilled when the operation succeeds or fails. Take a look at the promises guide for more details on this.

Customizing model behaviour

When defining a model, you can pass a definition object

Bike = restmod.model('api/bikes').mix(
// This is the definition object:
{
	createdAt: { encode: 'date' },
	owner: { belongsTo: 'User' }
}
);

The definition object allows you to:

  • Define relations between models
  • Customize an attribute's serialization and default values
  • Set model configuration variables.
  • Add custom methods
  • Add lifecycle hooks

Relations

Relations are defined like this:

Bike = restmod.model('/bikes').mix({
	parts: { hasMany: 'Part' },
	owner: { belongsTo: 'User' }
});

There are four types of relations:

HasMany

Let's say you have the following 'Part' model:

module.factory('Part', function() {
	return restmod.model('/parts');
});

The HasMany relation allows you to access parts of a specific bike directly from a bike object. In other words, HasMany is a hierarchical relation between a model instance (bike) and a model collection (parts).

Bike = restmod.model('/bikes').mix({
	parts: { hasMany: 'Part' }
});

bike = Bike.$new(1); 			// no request are made to the server yet.
parts = bike.parts.$fetch(); 	// sends a GET to /bikes/1/parts

Note: this is not necessarily great modeling ... for instance, if you destroy a bike's part bike.parts[0].$destroy();, you will be sending a DELETE to the resource in /parts/:id. If this is not what you want, you should consider working with a resource that represents the relation between a bike and a Part.

Later on, after 'parts' has already been resolved,

parts[0].$fetch(); // updates the part at index 0. This will do a GET /parts/:id

Calling $create on the collection will POST to the collection nested url.

var part = bike.parts.$create({ serialNo: 'XX123', category: 'wheels' }); // sends POST /bikes/1/parts

If the child collection model is nested then all CRUD routes for the collection items are bound to the parent.

So if 'Part' was defined like:

restmod.model();

The example above would behave like this:

console.log(bike.parts[0].$url())
bike.parts[0].$fetch();

Will send GET to /bikes/1/parts/:id instead of /parts/:id

HasMany Options

The has many relation provides the following options for you to customize its behaviour:

  • path: will change the relative path used to fetch/create the records. Ex: { hasMany: 'Part', path: 'pieces' }
  • inverseOf: adds a property on the child object that points to the parent. Ex: { hasMany: 'Part', inverseOf: 'bike' }.
  • params: optional query string parameters to be used when fetching collection. Ex: { hasMany: 'Part', params: { foo: 'bar' } }.
  • hooks: you can also define hasMany relation hooks. Check the hooks advanced documentation for more information.

HasOne

This is a hierarchical relation between one model's instance and another model's instance. The child instance url is bound to the parent url. The child instance is created at the same time as the parent, so its available even if the parent is not resolved.

Let's say you have the following 'User' model:

module.factory('User', function() {
	return restmod.model('/users');
});

That relates to a 'Bike' through a hasOne relation:

Bike = restmod.model('/bikes').mix({
	owner: { hasOne: 'User' }
});

Then a bike's owner data can then be retrieved just by knowing the bike primary key (id):

owner = Bike.$new(1).owner.$fetch();

will send GET /bikes/1/owner

Since the user resource has its own resource url defined:

owner.name = 'User';
owner.$save();

will send PUT /user/X.

If 'User' was to be defined like a nested resource:

module.factory('User', function() {
	return restmod.model();
});

Then calling:

owner.name = 'User';
owner.$save();

will send a PUT to /bikes/1/owner

HasOne Options

The has many relation provides the following options for you to customize its behaviour:

  • path: will change the relative path used to fetch/create the records. Ex: { hasOne: 'Part', path: 'pieces' }
  • inverseOf: adds a property on the child object that points to the parent. Ex: { hasOne: 'Part', inverseOf: 'bike' }.
  • hooks: you can also define hasOne relation hooks. Check the hooks advanced documentation for more information.

BelongsTo

This relation should be used in the following scenarios:

  1. The api resource references another resource by id:
{
	name: '...',
	brand: '...',
	owner_id: 20
}
  1. The api resource contanis another resource as an inline property and does not provide the same object as a nested url:
{
	name: '...',
	brand: '...',
	owner: {
		id: 20,
		user: 'extreme_rider_99'
	}
}

When applied, the referenced instance is not bound to the host's scope and is generated after server responds to a parent's $fetch.

Let's say you have the same 'User' model as before:

module.factory('User', function() {
	return restmod.model('/users');
});

That relates to a 'Bike' through a belongsTo relation this time:

Bike = restmod.model('/bikes').mix({
	owner: { belongsTo: 'User', key: 'last_owner_id' } // default key would be 'owner_id'
});

Also you have the following bike resource:

GET /bikes/1

{
	id: 1,
	brand: 'Transition',
	last_owner_id: 2
}

Then retrieving the resource:

bike = Bike.$find(1);

Will produce a bike object with its owner property initialized to a user with id=2, the owner property will only be available AFTER server response arrives.

Then calling

bike.owner.$fetch();

Will send a GET to /users/2 and populate the owner property with the user data.

This relation also support the child object data to come inlined in the parent object data. The inline property name can be optionally selected using the map attribute.

Lets redefine the Bike model as:

var Bike = restmod.model('/bikes').mix({
	owner: { belongsTo: 'User', map: 'last_owner' } // map would default to *owner*
});

And suppose that the last bike resource looks like:

GET /bikes/1

{
	id: 1,
	brand: 'Transition',
	last_owner: {
		id: 2
		name: 'Juanito'
	}
}

Then retrieving the bike resource:

var bike = Bike.$find(1);

Will produce a bike object with its owner property initialized to a user with id=2 and name=Juanito. As before, the owner property will only be available AFTER server response arrives.

Whenever the host object is saved, the reference primary key will be sent in the request using the selected foreign key.

So given the previous model definition, doing:

var bike = Bike.$create({ last_owner: User.$find(20) });

Will generate the following request:

POST /bikes

{
	owner_id: 20
}

BelongsToMany

This relation should be used in the following scenarios:

  1. The api resource references another resource by id:
{
	name: '...',
	brand: '...',
	parts_ids: [1,2]
}
  1. The api resource contains another resource as an inline property and does not provide the same object as a nested url:
{
	name: '...',
	brand: '...',
	parts: [
		{ id: 1, user: 'handlebar' },
		{ id: 2, user: 'wheel' }
	]
}

When retrieved, the referenced instances will not be bound to the host's scope.

Let's say you have the following 'Part' definition:

module.factory('Part', function() {
	return restmod.model('/parts');
});

That relates to a 'Bike' through a belongsToMany relation this time:

Bike = restmod.model('/bikes').mix({
	parts: { belongsToMany: 'Part', keys: 'part_keys' } // default key would be 'parts_ids'
});

Also you have the following bike resource:

GET /bikes/1

{
	id: 1,
	brand: 'Transition',
	parts_keys: [1, 2]
}

Then retrieving the resource:

bike = Bike.$find(1);

Will produce a bike object with the parts property containing two Part objects with $pks set to 1 and 2 (but empty).

This relation also support the childs object data to come inlined in the hosts object data. The inline property name can be optionally selected using the map attribute.

Given the same Bike model as before, lets suppose now that the bike API resource looks like this:

And suppose that the last bike resource looks like:

GET /bikes/1

{
	id: 1,
	brand: 'Transition',
	parts: [
		{ id: 1, user: 'handlebar' },
		{ id: 2, user: 'wheel' }
	]
}

Then retrieving the bike resource:

var bike = Bike.$find(1);

Will produce a bike object with the parts property containing two populated Part objects with $pks set to 1 and 2.

Whenever the host object is saved, the references primary keys will be sent in the request using the selected key.

So given the previous model definition, doing:

var bike = Bike.$create({ parts: [Part.$find(1), Part.$find(2)] });

Will generate the following request:

POST /bikes

{
	parts_keys: [1, 2] // remember we changed the keys property name before!
}

Serialization, masking and default values.

When you communicate with an API, some attribute types require special treatment (like a date, for instance)

Decode

You can specify a way of decoding an attribute when it arrives from the server.

Let's say you have defined a filter like this:

Angular.factory('DateParseFilter', function() {
	return function(_value) {
		date = new Date();
		date.setTime(Date.parse(_value));
		return date;
	}
})

then you use it as a standard decoder like this:

var Bike = restmod.model('/bikes').mix({
	createdAt: {decode: 'date_parse' }
});

Encode

To specify a way of encoding an attribute before you send it back to the server: Just as with the previous example (decode), you use an Angular Filter. In this example we use the built in 'date' filter.

var Bike = restmod.model('/bikes').mix({
	createdAt: {encode:'date', param:'yyyy-MM-dd'}
});

On both encode and decode you can use an inline function instead of the filter's name. It is also possible to bundle an encoder and decoder together using a Serializer object, check the API Reference for more.

Attribute masking

Following the Angular conventions, attributes that start with a '$' symbol are considered private and never sent to the server. Furthermore, you can define a mask that allows you to specify a more advanced behaviour for other attributes:

var Bike = restmod.model('/bikes').mix({
	createdAt: { ignore: 'CU' }, // won't send on Create or Update
	viewCount: { ignore: 'R' }, // won't load on Read (fetch)
	opened: { ignore: true }, // will ignore in every request and response
});

Default value

You can define default values for your attributes, both static and dynamic. Dynamic defaults are defined using a function that will be called on record creation.

var Bike = restmod.model('/bikes').mix({
	wheels: { init: 2 }, // every new bike will have 2 wheels by default
	createdAt: { init: function() {
	 return new Date();
	}}
});

Explicit attribute mapping

You can explicitly tell restmod to map a given server attribute to one of the model's attributes:

var Bike = restmod.model('/bikes').mix({
	created: { map: 'stats.created_at' }
});

Volatile attributes

You can define volatile attributes, volatile attributes will be deleted from record instance after being sent to server, this is usefull for things like passwords.

var User = restmod.model('/users').mix({
	password: { volatile: true } // make password volatile
});

Nested properties

Sometimes you will need to specify behaviour for nested properties, this is done the same ways as with regular properties using the . symbol.

Given the following json response:

{
	"id": 1,
	"serialNo": {
		"issued": "2014-05-05"
	}
}

You can add a date decoder for the issued property using:

var Bike = restmod.model('/bikes').mix({
	'serialNo.issued': { decode: 'date_parse' }
});

If the nested property is inside an array, you can reffer to it using the [] symbols.

So if the json response looks like this:

{
	"id": 1,
	"tags": [
		{ "name": "endurow", "weight": 20 },
		{ "name": "offroad", "weight": 5 }
	]
}

You can add a mapping for the weight property to the size property using:

var Bike = restmod.model('/bikes').mix({
	'tags[].size': { map: 'weight' }
});

Custom methods

A restmod object is composed of three main APIs, the Model static API, the record API and the collection API.

Each one of these APis can be extended using the $extend block in the object definition:

For example, the following will add the pedal method to every record.

var Bike = restmod.model('/bikes').mix({
	$extend: {
		Record: {
			pedal: function() {
			 	this.strokes += 1;
			}
		}
	}
});

Even though the $extend block is the preferred method to extend a model, for small models it is also possible to directly define the method in the definition object:

var Bike = restmod.model('/bikes').mix({
	'Record.pedal': function() {
	 	this.strokes += 1;
	}
});

In the last example the 'Record.' prefix could be omitted because by default methods are added to the record api.

The following API's are available for extension:

  • Model: the static api.
  • Record: model instances api.
  • Collection: model collection instance api.
  • Scope: Same as extending Model and Collection
  • Resource: Same as extending Record and+ Collection
  • List: special api implemented by any record list, including collections

So, to add a static method we would use:

var Bike = restmod.model('/bikes').mix({
	$extend: {
		Model: {
			$searchByTeam: function(_team) {
			 	return this.$search({ team: _team });
			}
		}
	}
});

It is also posible to override an existing method, to refer to the overriden function use this.$super:

var Bike = restmod.model('/bikes').mix({
	$extend: {
		Scope: {
			$search: function(_params) {
			 	return this.$super(angular.extend({ time: SomeService.currentTime() }, _params);
			}
		}
	}
});

Custom methods and Lists

A List namespace is provided for collections and lists, this enables the creation of chainable list methods.

For example, lets say you need to be able to filter a collection of records and then do something with the resulting list:

var Part = restmod.model('/parts').mix({
	$extend: {
		List: {
			filterByCategory: function(_category) {
				return this.$asList(function(_parts) {
					return _.filter(_parts, function(_part) {
						return _part.category == _category;
					});
				});
			},
			filterByBrand: function(_brand) {
				return this.$asList(function(_parts) {
					return _.filter(_parts, function(_part) {
						return _part.brand == _brand;
					});
				});
			},
			getTotalWeight: function(_category) {
				return _.reduce(this, function(sum, _part) {
					return sum + _part.weight;
				};
			}
		}
	}
});

Now, since List methods are shared by both collections and lists, you can do:

Part.$search().filterByCategory('wheels').filterByBrand('SRAM').$then(function() {
	// use $then because the $asList method will honor promises.
	scope.weight = this.getTotalWeight();
});

Hooks (callbacks)

Just like you do with ActiveRecord, you can add hooks on certain steps of the object lifecycle, hooks are added in the $hooks block of the object definition.

var Bike = restmod.model('/bikes').mix({
	$hooks: {
		'before-save': function() {
			this.partCount = this.parts.length;
		}
	}
});

Note that a hook can be defined for a type, a collection or a record. Also, hooks can also be defined for a given execution context using $decorate. Check the hooks advanced documentation.

Mixins

To ease up the definition of models, and keep things DRY, Restmod provides you with mixin capabilities. For example, say you already defined a Vehicle model as a factory:

Angular.factory('Vehicle', function() {
	return restmod.model('/vehicle').mix({
		createdAt: {encode:'date', param:'yyyy-MM-dd'}
	});
})

You can then define your Bike model that inherits from the Vehicle model, and also sets additional functionality.

var Bike = restmod.model('/bikes').mix('Vehicle', {
	pedal: function (){
		alert('pedaling')
	}
});

Some links:

REST api designs guidelines: https://github.com/interagent/http-api-design REST json api standard: http://jsonapi.org

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

Check CONTRIBUTORS for more details

Credits

Thank you contributors!

Platanus

angular-restmod is maintained by platanus.

License

Angular Restmod is ยฉ 2015 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.

More Repositories

1

activeadmin_addons

Extends ActiveAdmin to enable a set of great optional UX improving add-ons
Ruby
731
star
2

potassium

A Rails application generator by Platanus, inspired by Suspenders
Ruby
232
star
3

pincers

Web automation DSL on top of webdriver (and nokogiri)
Ruby
90
star
4

capistrano3-nginx

Ruby
79
star
5

capistrano-bower

Ruby
59
star
6

angular-rut

JavaScript
57
star
7

capistrano3-delayed-job

Ruby
50
star
8

power_api

Set of other gems and configurations designed to build incredible APIs
Ruby
45
star
9

activeadmin_jobs

It's a Rails engine that allows you to play nice with Active Job in Active Admin providing user feedback
Ruby
33
star
10

vue-dni

Collection of utilities to check and format RUT (Chilean DNI) for Vue
JavaScript
30
star
11

la-guia

Guia para todos los acuerdos y reglas en platanus
30
star
12

ng2-rut

Angular 2 library with several components to handle Chilean RUT validation, cleaning and formatting.
TypeScript
29
star
13

capistrano-lets-encrypt

Ruby
29
star
14

power-types

Rails pattern enforcing types used by the Platanus team
Ruby
27
star
15

guides

guides!
HTML
27
star
16

fastlane-cordova

Ruby
26
star
17

terraform-provider-rancher

Go
25
star
18

crabfarm-gem

Crabfarm crawler creation framework
JavaScript
21
star
19

negroku

Opinionated collection of recipes for capistrano.
Ruby
19
star
20

rut-helpers

Simple helpers to validate and format strings to RUT (Chilean DNI)
JavaScript
19
star
21

active_job_log

Rails engine to register jobs history, adding: job state, error feedback, duration, etc.
Ruby
17
star
22

job_notifier

Rails engine to persist job results and notify job status changes
Ruby
16
star
23

ledgerizer

A double-entry accounting system for Rails applications
Ruby
16
star
24

human_attributes

Gem to generate human readable ActiveRecord attributes
Ruby
14
star
25

angular-validate

Angular validation framework
JavaScript
13
star
26

crabtrap

an http proxy that can capture and replay requests
JavaScript
13
star
27

angular-push

Dead-easy push notifications for Cordova Angular apps
JavaScript
12
star
28

open-legal-source

Documentos legales abiertos para lograr una cooperaciรณn en el รกrea jurรญdica
11
star
29

admin-invitable

Gem to use devise invitable with ActiveAdmin.
Ruby
10
star
30

angular-pretty-load

JavaScript
8
star
31

angular-keep-values

Keep your input values in your ngModels.
JavaScript
8
star
32

fonts

Las fonts que usamos en Platanus
CSS
8
star
33

bank-api-gem

Ruby
7
star
34

cavendish

A React Native + Expo project generator by Platanus
Ruby
7
star
35

banano

Vue Component Library
Vue
7
star
36

legacy_migration

A rails tool to migrate old (and maybe crappy) databases into new ones
Ruby
7
star
37

blog

Jekyll based blog
CSS
7
star
38

dialers

Api Wrappers for Ruby
Ruby
7
star
39

cangrejo-gem

Crabfarm ruby client
Ruby
6
star
40

elastic-rails

Elasticsearch integration for Ruby on Rails
Ruby
6
star
41

our-boxen

Ruby
6
star
42

lacatan

Ruby
6
star
43

bitrise-step-fastlane-match

Go
5
star
44

froggo

Ruby
5
star
45

angular-rope

Angular Promise Chaining
JavaScript
5
star
46

punto_pagos_rails

Ruby
5
star
47

rails_pallet

Rails engine to save paperclip attachments asynchronously
Ruby
5
star
48

queue-it

Ruby
5
star
49

cordova-plugin-flavors

JavaScript
5
star
50

cordova-gigya

Cordova plugin that handles Gigya integration for mobile (iOS and Android) apps.
Objective-C
4
star
51

metagem

Metagem generates the structure and configuration necessary to have plugins (gems and engines) inside your Rails project.
Ruby
4
star
52

banco-santander-empresas-crawler

A crabfarm.io scraper for Banco Santander
Ruby
4
star
53

surbtc-bitbar-plugin

A Bitbar plugin that shows SurBTC cryptocurrency prices and volume.
Shell
4
star
54

angular-auth

JavaScript
4
star
55

generator-platanus-ionic

Yeoman generator for Ionic projects in Platanus.
JavaScript
3
star
56

parxer

ruby gem with a nice DSL to parse xls, xlsx and csv files.
Ruby
3
star
57

restspec

REST test api framework built in top of RSpec for robust and mantainable api tests
Ruby
3
star
58

send_grid_mailer

Action Mailer adapter for using SendGrid
Ruby
3
star
59

capistrano-nodenv

Ruby
3
star
60

open-source-latam

Lista de herramientas open source hechas en Latinoamรฉrcia
CSS
3
star
61

gift-recommender

Recommendation engine/service for the gifting app
Python
3
star
62

platanews-ghost-theme

SCSS
3
star
63

gemaker

CLI to simplify Platanus gems creation
HTML
3
star
64

agent-contract

Agent Representation Ethereum Contract
JavaScript
3
star
65

paperclip_attributes

Rails Engine built on top of Paperclip gem to add extra attributes to your attachments
Ruby
3
star
66

heroku-buildpack-ruby-version

Shell
3
star
67

local_resource

Rails engine to treat external resources as if they were local
Ruby
3
star
68

heroku-stage

Ruby
2
star
69

rubocop-platanus

A RuboCop extension for enforcing Platanus best practices and code style.
Ruby
2
star
70

surbtc_dashboard

SurBTC Dashing Dashboard
JavaScript
2
star
71

power_client

Ruby
2
star
72

web-replay-chrome

WebReplay file generator for chrome
JavaScript
2
star
73

lightning-prometheus-exporter

Go
2
star
74

docker-ruby

Dockerfile
2
star
75

lita_time_tracker

Get time tracking information in your Slack
Ruby
2
star
76

camaron

Crabfarm nodejs client
JavaScript
2
star
77

docker-rails

2
star
78

heroku-buildpack-bower

Shell
2
star
79

platanus-rails

Platanus Ruby/Rails Toolbelt
Ruby
2
star
80

openface-api

Openface service for platanus proyects
Python
2
star
81

hound-cli

Ruby
2
star
82

ionic-contact-picker

JavaScript
2
star
83

jspm-on-rails

Ruby
2
star
84

capistrano-route53

Ruby
2
star
85

bitsplit-mobile

JavaScript
2
star
86

angular-doc-preview

directive that allows you to show a file preview from a given url
JavaScript
2
star
87

docker-rbenv

1
star
88

simple-restmod-demo

A simple demo to show how to include angular-restmod on a project.
JavaScript
1
star
89

angular-pallet-bundle

Wrapper directive of: platanus angular-upload, angular-progress and angular-doc-preview
JavaScript
1
star
90

selenium_surfer

Selenium surfer robots!
Ruby
1
star
91

geo-app-demo

Demo app to manage spatial data
Ruby
1
star
92

angular-form-utils

Couple of helpers that help handling form states
JavaScript
1
star
93

ham-mobile

Ham ๐Ÿ“ฑ
TypeScript
1
star
94

banco-chile-empresas-crawler

A crabfarm.io scraper for Banco de Chile
HTML
1
star
95

puppet-toggl

Shell
1
star
96

starting-ionic-template

Java
1
star
97

babel-react-dashboard

JavaScript
1
star
98

angular-skip-filter

JavaScript
1
star
99

bencinachile

C#
1
star
100

37s_cookbooks

Chef Cookbooks in use at 37signals
Ruby
1
star