{"id":18776571,"url":"https://github.com/onsi/cocktail","last_synced_at":"2025-05-16T02:10:10.412Z","repository":{"id":4046008,"uuid":"5148035","full_name":"onsi/cocktail","owner":"onsi","description":"Cocktail: DRY up your backbone code with mixins","archived":false,"fork":false,"pushed_at":"2022-02-09T02:59:45.000Z","size":304,"stargazers_count":405,"open_issues_count":1,"forks_count":48,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-08T13:15:43.926Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/onsi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-07-23T05:46:09.000Z","updated_at":"2024-11-25T13:55:30.000Z","dependencies_parsed_at":"2022-08-30T16:11:21.385Z","dependency_job_id":null,"html_url":"https://github.com/onsi/cocktail","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onsi%2Fcocktail","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onsi%2Fcocktail/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onsi%2Fcocktail/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onsi%2Fcocktail/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/onsi","download_url":"https://codeload.github.com/onsi/cocktail/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254453667,"owners_count":22073618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T19:46:44.241Z","updated_at":"2025-05-16T02:10:10.380Z","avatar_url":"https://github.com/onsi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cocktail [![npm](http://img.shields.io/npm/v/backbone.cocktail.svg)](https://npmjs.org/package/backbone.cocktail) [![npm](http://img.shields.io/npm/dm/backbone.cocktail.svg)](https://npmjs.org/package/backbone.cocktail)\n\n\nBreak out your shared [Backbone.js](http://backbonejs.org) model/collection/view behaviors into separate modules and mix them into your classes with Cocktail - an implementation of Backbone mixins.\n\n* `bower install cocktail`\n* `npm install backbone.cocktail`\n\n## Concocting Mixins\n\nMixins are simply bare-bones JavaScript objects that provide additional functionality to your Backbone objects.  Think of them as bags of methods that will get added to all instances of your objects.\n\nHere's an example mixin that implements selectability on a view based on a model's selection state:\n\n    window.MyMixins = {};\n\n    MyMixins.SelectMixin = {\n      initialize: function() {\n        this.model.on('change:selected', this.refreshSelect, this);\n      },\n\n      events: {\n        click: 'toggleSelect'\n      },\n\n      render: function() {\n        this.refreshSelect();\n      },\n\n      refreshSelect: function() {\n        this.$el.toggleClass('selected', this.model.get('selected'));\n      },\n\n      toggleSelect: function() {\n        this.model.set('selected', !this.model.get('selected'));\n      }\n    }\n\nAs you can see: nothing special, just a bag of functions.\n\n\u003e Obviously, the bit about `window.MyMixins` is just a suggested pattern for organizing your mixins!\n\n\u003e And, yes, having models know about view state like selection is often an anti-pattern... but it makes for a simple intelligible example!\n\n## Mixing Mixins In\n\nOnce you have your mixins defined including them in your Backbone object definitions is a one-liner:\n\n    var MyView = Backbone.View.extend({\n      events: {\n        'click .myChild': 'myCustomHandler'\n      }\n\n      initialize: function() {\n        ...\n      },\n\n      render: function() {\n        ...\n      },\n\n      etc...\n    });\n\n    Cocktail.mixin(MyView, MyMixins.SelectMixin, MyMixins.SomeOtherMixin);\n\nNow all instances of `MyView` will have the selection behavior defined in the `SelectMixin`:\n\n    var view = new MyView(...);\n    view.toggleSelect(); //works!\n\n\n**Alternatively**, you can lazily mix into your views/models like so:\n\n    var MyView = Backbone.View.extend({\n      events: {\n        'click .myChild': 'myCustomHandler'\n      }\n\n      initialize: function() {\n        Cocktail.mixin(this, MyMixins.SelectMixin, MyMixins.SomeOtherMixin);\n      },\n\n      render: function() {\n        ...\n      },\n\n      etc...\n    });\n\nThis looks a bit cleaner if you can't monkeypatch (described below). In addition, this syntax gives you the flexibility\nto mix in certain methods in particular states of your application. For example, maybe you have an interface that you'd like\nan object to assume on login/logout or in the presence of another object (like a Flash embed).\n\n### If you don't mind monkeypatching\n\nBy default, as of 0.2.0 Cocktail no longer messes with Backbone's built-in extend method.  However, if you don't mind some monkey patching then running:\n\n    Cocktail.patch(Backbone);\n\n*before* you define any classes will allow you to mixin code like this:\n\n    var MyView = Backbone.View.extend({\n      mixins: [MyMixins.SelectMixin, MyMixins.SomeOtherMixin],\n\n      etc...\n    });\n\nor like this for CoffeeScript users:\n\n    class MyView extends Backbone.View\n      @mixin MyMixins.SelectMixin\n\n\nwith the monkey-patch installed, mixins are just a convenient bit of configuration at the top of your class definitions. Note that the patch should only be applied once.\n\n### Named mixins\n\nWhether or not you're monkey patching Backbone, you can also use named mixins by registering them with Cocktail:\n\n    Cocktail.mixins = {\n      select: MyMixins.SelectMixin,\n      other: MyMixins.SomeOtherMixin\n    };\n\n    // Without monkey patching\n    Cocktail.mixin(MyView, 'select', 'other', MyMixins.yetAnotherMixin);\n\n    // With monkey patching\n    var MyView = Backbone.View.extend({\n      mixins: ['select', 'other', MyMixins.yetAnotherMixin],\n\n      etc...\n    });\n\n## But What About Collisions?\n\nIn the example above, both `MyView` and `SelectMixin` both defined `initialize`, and `render`.  What happens with these colliding methods?\n\nCocktail automatically ensures that methods defined in your mixins do not obliterate the corresponding methods in your classes.\nThis is accomplished by wrapping all colliding methods into a new method that is then assigned to the final composite object.\n\nNote: Cocktail will ensure that if you accidentally try to mix in the same method, it will not result in a collision and will do nothing.\n\n### How are colliding functions called?\n\nLet's take a concrete example.  Class **X** implements `render` and mixes in mixins **A**, **B**, and **C** (in that order).  Of these only **A** and **C** implement `render`.\n\nWhen `render` is called on instances of **X** the implementation of `render` in **X** is called first, followed by the implementation in **A** and then **C**.  In this way the original implementation is always called first, followed by the mixins.\n\n### What are the return values from colliding functions?\n\nThe return value of the composite function is the **last** non-`undefined` return value from the chain of colliding functions.\n\nTo be clear: let's say **X** mixes in **A** and **B**.  Say **X** implements a method `foo` that returns `bar`, **A** implements `foo` but returns nothing (i.e. `undefined` is implicitly returned) and **B** returns `baz`.  Then instances of **X** will return `baz` -- the last non-`undefined` return value from `foo`'s **X** \u0026rarr; **A** \u0026rarr; **B** collision chain.\n\n## And how about hashes?\n\nWhen both a mixin and the class define a hash, Cocktail will merge the hashes together.  In the case of a key collision, keys and values defined in the hash on the class take precedence followed the hash on the first mixin, then the second mixin, etc...\n\nNote that this includes the events hash.  As a result, mixins are allowed to add new event listeners.\n\n## And what about subclasses?\n\nSubclass hierarchies with mixins should work just fine.  If a super class mixes in a mixin, then all subclasses will inherit that mixin.  If those subclasses mixin additional mixins, those mixins will be folded in to the subclasses and collisions will be handled correctly, even collisions with methods further up the class hierarchy.\n\nHowever, if a subclass redefines a method that is provided by a mixin of the super class, the mixin's implementation will *not* be called.  This shouldn't be surprising: the subclass's method is further up in the prototype chain and is the method that gets evaluated.  In this circumstance, you *must* remember to call `SubClass.__super__.theMethod.apply(this)` to ensure that the mixin's method gets called.\n\n## Testing Mixins\n\nThe [example](https://github.com/onsi/cocktail/tree/master/example) directory includes an example mixin and its usage, and the accompanying [Jasmine](http://www.github.com/pivotal/jasmine) test.  It also includes a [readme](https://github.com/onsi/cocktail/tree/master/example) that walks through the testing pattern employed for testing mixins with Jasmine.\n\n## Dependencies, Installation, and Contributing\n\nCocktail requires:\n\n  - [Backbone](http://backbonejs.org) (duh) (tested with 1.1.0)\n  - [Underscore](http://underscorejs.org) (tested with 1.5.1)\n\nRunning Tests:\n\nOpen up `spec/SpecRunner.html` in your favorite browser.\n\nContributing:\n\nYou can contribute a new build by issuing the terminal command `grunt` within the root folder.\n\n\nFuture changes to backbone could break Cocktail or obviate its need.\nIf the latter happens - great!  If the former: let me know and I'll try to\nensure compatibility going forward.\n\n## If you like Cocktail...\n...check out [Coccyx](http://github.com/onsi/coccyx).  Coccyx helps you plug up backbone leaks with two things: named constructors and tear-downable view hierarchies.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonsi%2Fcocktail","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonsi%2Fcocktail","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonsi%2Fcocktail/lists"}