modularity-coffee-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # Modularity::Rails [![Build Status](https://secure.travis-ci.org/kevgo/modularity-rails.png)](http://travis-ci.org/#!/kevgo/modularity-rails) <a href="https://codeclimate.com/github/kevgo/modularity-rails" target="_blank"><img src="https://codeclimate.com/badge.png" /></a>
2
+
3
+ Makes the [Modularity CoffeeScript](http://github.com/kevgo/modularity-coffeescript) library and related modules available to
4
+ Rails 3.1 applications.
5
+
6
+ Modularity is a framework for lightweight component-oriented CoffeeScript.
7
+ It allows to compose functionally rich web pages in a clean, intuitive, and testable way
8
+ out of well structured and reusable components. It scales very well with complexity.
9
+
10
+
11
+ ## Authors
12
+ * [Kevin Goslar](https://github.com/kevgo) (kevin.goslar@gmail.com)
13
+ * [Alex David](https://github.com/alexdavid)
14
+
15
+
16
+ # Installation
17
+
18
+ Load modularity in your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'modularity-rails'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ ```bash
27
+ $ bundle
28
+ ```
29
+
30
+ Finally, you have to load the modularity file into your application's javascript.
31
+ The easiest way is to add it to `application.coffee`:
32
+
33
+ ```coffeescript
34
+ # require jquery
35
+ # require modularity
36
+ ```
37
+
38
+
39
+ # Usage
40
+
41
+ Modularity is a lightweight framework for building powerful AJAX applications.
42
+ Modularity avoids magic and heavyness. It focusses on providing a pragmatic and interoperable foundation
43
+ for clean hand-written code bases.
44
+ Modularity provides practices to create code bases of incredible complexity that are still
45
+ nicely manageable and perform very well.
46
+
47
+
48
+ ## Modules
49
+
50
+ Modules are native CoffeeScript classes that are specialized for doing what most JavaScript running in browsers does:
51
+ managing a UI consisting of DOM elements, reacting to events that happen within that section,
52
+ representing application logic specific to that section, and providing high-level APIs for others to interact with the section.
53
+
54
+ Each module has a container. The container is the outermost DOM element of a section.
55
+ Everything the module does must happen inside this container.
56
+ The module is responsible for managing the inner DOM-structure of the container.
57
+
58
+
59
+ ## Mixins
60
+
61
+ Similar to Ruby mixins, mixins in Modularity allow to include orthogonal functional aspects defined in separate objects into a class.
62
+
63
+ ```coffeescript
64
+ myMixin =
65
+
66
+ # This will be called when an instance of a class that includes this mixin is created.
67
+ constructor: ->
68
+
69
+ # This method will be available in every class that includes
70
+ myMethod: ->
71
+
72
+
73
+ class MyModule extends Module
74
+
75
+ @mixin myMixin
76
+
77
+ constructor: (container) ->
78
+
79
+ # The super constructor will call the mixin constructors here.
80
+ super
81
+
82
+ # ...
83
+ ```
84
+
85
+ ## Hooks
86
+
87
+ Hooks are a more direct and easier way to interact with mixins. They are methods with predefined names that mixing modules can implement to hook into events of their mixins
88
+ without the need to wire up event handlers, and with the ability to interact with the workflow of the mixins.
89
+
90
+
91
+ # Reusable example modules and mixins.
92
+
93
+ Modularity comes bundled with a bunch of example modules and mixins that can be used in production code.
94
+ The example modules are located in `vendor/assets/javascripts/modules/` and _vendor/assets/javascripts/mixins_
95
+ and must be explicitly required in your Rails files using the `require` commands of the asset pipeline.
96
+
97
+
98
+ ## Modules
99
+
100
+ * __button.coffee__: A simple button. Fires the `clicked` event when anything inside the container is clicked. Uses the `clickable` mixin.
101
+ * __counter_button.coffee__: Similar to button, but includes the click count as data payload in the fired event.
102
+
103
+
104
+ ## Mixins
105
+
106
+ ###clickable
107
+ Including this mixins adds a 'clickable' aspect to your module, i.e. turns it into a button. Clicking anywhere inside the container makes it fire the 'clicked' event.
108
+
109
+ ###closable
110
+ Including this mixin makes a module closable. The mixin searches for an embedded DOM element with the class 'CloseButton'. When it is clicked, the following things happen:
111
+
112
+ * The _closable_closing_ hook of the closable class is called.
113
+ This hook could be used to display confirmation dialogs (and abort the close process) or to fire custom events that depend on the DOM still being present.
114
+ If this method returns a falsy value, the closing process is aborted.
115
+ * The closable module fires a local 'closing' event (with the DOM still present).
116
+ * The whole module including its container is removed from the DOM.
117
+ * The _closable_closed_ hook of the closable class is called.
118
+
119
+
120
+ ## Tools
121
+
122
+ ### Loader
123
+ A generic cached loader for parallel and repeated GET requests.
124
+ Prevents duplicate requests, caches the responses.
125
+
126
+ The first request triggers the ajax request. Subsequent requests while the resquest is running are accumulated without causing new requests.
127
+ Once the response arrives, all currently requesting clients are answered. Subsequent requests are answered immediately using the cached data.
128
+
129
+ ```coffeescript
130
+ Module.loader.get '/test.json', (data) ->
131
+ # Use data here.
132
+ ```
133
+
134
+ # Development
135
+
136
+ ## Contributing
137
+
138
+ 1. Fork it
139
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
140
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
141
+ 4. Push to the branch (`git push origin my-new-feature`)
142
+ 5. Create new Pull Request
143
+
144
+
145
+ ## Running the unit tests
146
+
147
+ ```bash
148
+ $ evergreen run
149
+ ```
150
+
151
+
152
+ ## Automatically refreshing the browser during development.
153
+
154
+ Modularity-Rails comes with support for [LifeReload](https://github.com/mockko/livereload) via [Guard](https://github.com/guard/guard).
155
+
156
+ * Install the LiveReload browser extension: [Chrome](https://chrome.google.com/webstore/detail/jnihajbhpnppcggbcgedagnkighmdlei)
157
+ * Run the evergreen server: ```$ evergreen run```
158
+ * Run the guard server: ``` $ bundle exec guard ```
159
+ * Start the LiveReload plugin in Chrome (button in address bar).
160
+ * Navigate to the test page that you want to observe.
161
+ * Change and save code and see the browser reload.
162
+
163
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ModularityCoffeeRails'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new :spec
30
+ task :default => :spec
31
+
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require ./modularity
15
+ //= require_tree .
@@ -0,0 +1,77 @@
1
+ #= require modularity/data/cache
2
+
3
+
4
+ # A generic ajax loader for parallel GET requests.
5
+ # Prevents duplicate requests, caches the responses
6
+ # to answer subsequent requests immediately
7
+ # without additional server requests.
8
+ #
9
+ # Warning: Caches the responses, so once a request is cached,
10
+ # any new content on the same URL will not be visible!
11
+ class window.modularity.AjaxLoader
12
+
13
+ constructor: (params = {}) ->
14
+
15
+ # Caches the callbacks.
16
+ @cb_cache = new modularity.Cache()
17
+
18
+ # Whether to perform caching of data.
19
+ # Default: no.
20
+ @caching = !!params.caching
21
+
22
+ # Caches the data loaded.
23
+ @data_cache = new modularity.Cache()
24
+
25
+ # The number of currently running loading operations.
26
+ @loader_count = 0
27
+
28
+
29
+ # The different events that this class can fire.
30
+ @events =
31
+ AJAX_LOADING: 'AJAX_LOADING'
32
+ AJAX_LOADED: 'AJAX_LOADED'
33
+
34
+
35
+ # Loads the data from the given URL asynchronously.
36
+ get: (url, callback) ->
37
+
38
+ # Return immediately if we have cached data.
39
+ if @caching
40
+ cached_data = @data_cache.get url
41
+ return callback(cached_data) if cached_data?
42
+
43
+ # Here, we have no cached data. Check
44
+
45
+ # If a GET call to this URL is already in progress -->
46
+ # add the given callback to the list of waiting callbacks.
47
+ request_in_progress = @cb_cache.get url
48
+ return request_in_progress.push(callback) if request_in_progress?
49
+
50
+ # Here, no request is currently in progress --> start a new one.
51
+
52
+ # Add callback to callback list.
53
+ @cb_cache.add url, [callback]
54
+
55
+ # Fire 'loading' event if this is the start of a loading operation.
56
+ @loader_count++
57
+ if @loader_count == 1
58
+ window.modularity.fire_global_event window.modularity.AjaxLoader.events.AJAX_LOADING
59
+
60
+ # Perform the request.
61
+ jQuery.get url, (data) =>
62
+
63
+ # Add result to cache.
64
+ if @caching
65
+ @data_cache.add url, data
66
+
67
+ # Call callbacks.
68
+ cb(data) for cb in @cb_cache.get url
69
+
70
+ # Remove request from callback list.
71
+ @cb_cache.remove url
72
+
73
+ # Fire 'loaded' event.
74
+ @loader_count--
75
+ if @loader_count == 0
76
+ window.modularity.fire_global_event window.modularity.AjaxLoader.events.AJAX_LOADED
77
+
@@ -0,0 +1,63 @@
1
+ #= require modularity/tools/object_tools
2
+
3
+ # A generic cache.
4
+ # Stores key-value pairs.
5
+ class window.modularity.Cache
6
+
7
+ constructor: ->
8
+ @cache = {}
9
+
10
+
11
+ # Adds the given entry to the cache.
12
+ # Overwrites existing entries.
13
+ add: (key, value) ->
14
+ @cache[key] = value
15
+
16
+
17
+ # Returns the entry with the given key from the cache, or NULL if no entry exists.
18
+ get: (key) ->
19
+ @cache[key]
20
+
21
+
22
+ # Looks up several entries at once.
23
+ # Returns a hash of found entries, and a list of missing entries.
24
+ get_many: (keys) ->
25
+ result = { found: {}, missing: [] }
26
+ for key in keys
27
+ do (key) =>
28
+ value = @cache[key]
29
+ if value
30
+ result.found[key] = value
31
+ else
32
+ result.missing.push key
33
+ result
34
+ getMany: Cache::get_many
35
+
36
+
37
+ # Returns the number of cached objects.
38
+ length: () ->
39
+ modularity.object_length @cache
40
+
41
+
42
+ # Removes the entry with the given key.
43
+ remove: (key) =>
44
+ delete @cache[key]
45
+
46
+
47
+ # Removes all entries with the given keys.
48
+ remove_many: (keys) ->
49
+ @remove(key) for key in keys
50
+
51
+
52
+ # Replaces the cache with the given data.
53
+ # When 'key' is given, treats 'data' as an array of objects, and indexes each element by the given key.
54
+ # When 'key' is not given, treats 'data' as an already indexed hash object.
55
+ replace_all: (data, key) ->
56
+ if key
57
+ # Key given --> index the data array.
58
+ @add(entry[key], entry) for entry in data
59
+ else
60
+ # Key not given --> use data as the new cache.
61
+ @cache = data
62
+ replaceAll: Cache::replace_all
63
+
@@ -0,0 +1,32 @@
1
+ #= require modularity/data/cache
2
+
3
+ # Provides fast and convenient retrieval of hash objects
4
+ # by indexing them on a given key column.
5
+ class modularity.IndexedCache
6
+
7
+ constructor: (@key) ->
8
+ @cache = new modularity.Cache
9
+
10
+
11
+ add: (entry) ->
12
+ @cache.add entry[@key], entry
13
+
14
+
15
+ add_all: (entries) ->
16
+ @add(entry) for entry in entries
17
+
18
+
19
+ remove: (entry) ->
20
+ @cache.remove entry[@key]
21
+
22
+
23
+ remove_many: (entries) ->
24
+ @cache.remove_many entries
25
+
26
+
27
+ get: (key) ->
28
+ @cache.get key
29
+
30
+
31
+ length: => @cache.length()
32
+
@@ -0,0 +1,166 @@
1
+ #= require modularity/data/ajax_loader
2
+ #= require modularity/data/indexed_cache
3
+ #= require modularity/tools/object_tools
4
+
5
+ # Provides persistence services for data models.
6
+ class modularity.PersistenceManager
7
+
8
+ constructor: (params) ->
9
+
10
+ # Copy of the data as it is on the server.
11
+ @server_data = new modularity.IndexedCache 'id'
12
+
13
+ # Copy of the data as it is on the client.
14
+ @client_data = new modularity.IndexedCache 'id'
15
+
16
+ # The base url on the server. Expected to be a fully RESTful API.
17
+ @base_url = params.url
18
+
19
+ @key = params.key or 'id'
20
+
21
+ # For handling parallel requests to the server.
22
+ @loader = new modularity.AjaxLoader { cache: no }
23
+
24
+
25
+ # Adds the given data objects to the server cache.
26
+ add_all: (data) ->
27
+ @server_data.add_all data
28
+
29
+
30
+ # Returns the URL to access the collection of objects.
31
+ collection_url: ->
32
+ "#{@base_url}.json"
33
+
34
+
35
+ # Creates the given object on the server.
36
+ create: (obj, callback) ->
37
+ jQuery.ajax
38
+ url: @collection_url()
39
+ type: 'POST'
40
+ data: obj
41
+ success: (server_obj) =>
42
+ @server_data.add server_obj
43
+ callback server_obj
44
+
45
+
46
+ delete: (obj, callback) ->
47
+ @client_data.remove obj
48
+ @server_data.remove obj
49
+ jQuery.ajax
50
+ url: @entry_url(obj)
51
+ type: 'DELETE'
52
+ success: ->
53
+ callback() if callback?
54
+
55
+
56
+ delete_many: (objects, callback) ->
57
+ @client_data.remove_many obj
58
+ @server_data.remove_many obj
59
+ jQuery.ajax
60
+ url: @base_url
61
+ type: 'DELETE'
62
+ data: (obj.id for obj in objects)
63
+ success: ->
64
+ callback() if callback?
65
+
66
+
67
+ # Returns the url to access a single entry.
68
+ entry_url: (entry) ->
69
+ "#{@base_url}/#{entry[@key]}.json"
70
+
71
+
72
+ # Returns the cached data object, or undefined.
73
+ get_cached: (key) ->
74
+
75
+ # Try to use client_data cache.
76
+ client_obj = @client_data.get key
77
+ return client_obj if client_obj
78
+
79
+ # No data in client cache --> try to use server cache.
80
+ server_obj = @server_data.get key
81
+ if server_obj
82
+ client_obj = modularity.clone_hash server_obj
83
+ @client_data.add client_obj
84
+ return client_obj
85
+
86
+ # Object not found in client or server cache.
87
+ return undefined
88
+
89
+
90
+ # Returns the entry with the given key.
91
+ load: (key, callback) ->
92
+
93
+ # Try to load from cache.
94
+ return callback(snippet) if snippet = @get_cached key
95
+
96
+ # No data on client at all --> load data from server.
97
+ @loader.get "#{@base_url}/#{key}", (server_entry) =>
98
+ @server_data.add server_entry
99
+ client_entry = modularity.clone_hash server_entry
100
+ @client_data.add client_entry
101
+ callback client_entry
102
+
103
+
104
+ # Loads all objects from the server.
105
+ # Provides the given params as parameters to the GET request.
106
+ load_all: (callback, params) ->
107
+ jQuery.ajax
108
+ url: @collection_url()
109
+ cache: no
110
+ data: params
111
+ success: (data) =>
112
+ @server_data.add_all data
113
+ callback()
114
+
115
+
116
+ # Loads all snippets with the given ids.
117
+ load_many: (ids, callback) ->
118
+ missing_ids = []
119
+ snippets = []
120
+ $.each ids, (pos, id) =>
121
+ snippet = @get_cached id
122
+ if snippet
123
+ snippets.push snippet
124
+ else
125
+ missing_ids.push id
126
+
127
+ if missing_ids.length == 0
128
+ return callback(snippets)
129
+
130
+ alert "uncached snippets found: #{missing_ids}"
131
+
132
+
133
+ # Saves the given object.
134
+ # Does the right thing (create or update) dependent on
135
+ # whether the object already has a key parameter.
136
+ save: (obj, callback) ->
137
+ if obj[@key]?
138
+ @update obj, callback
139
+ else
140
+ @create obj, callback
141
+
142
+
143
+ # Updates the given object.
144
+ # The given object must exist on the server already,
145
+ # and have a proper value in the key attribute.
146
+ update: (obj, callback) ->
147
+
148
+ # Create a new hash, containing only the changed attributes between obj and it's replica in @server_data.
149
+ diff_obj = modularity.object_diff @server_data.get(obj[@key]), obj
150
+ return if modularity.object_length(diff_obj) == 0
151
+
152
+ # Add key attribute.
153
+ diff_obj[@key] = obj[@key]
154
+
155
+ # Update server_data version.
156
+ @server_data.add obj
157
+
158
+ # Send to server
159
+ jQuery.ajax
160
+ url: @entry_url(obj)
161
+ type: 'PUT'
162
+ data: diff_obj
163
+ success: (server_obj) =>
164
+ @server_data.add server_obj
165
+ callback server_obj if callback
166
+
@@ -0,0 +1,21 @@
1
+ # This mixin adds a 'clickable' aspect to modules.
2
+ # This means clicking anywhere on the module fires the 'clicked' event.
3
+ window.modularity.clickable =
4
+
5
+ constructor: ->
6
+ @container.click @container_clicked
7
+
8
+
9
+ # Events that are fired by this mixin.
10
+ events:
11
+ clicked: 'clicked'
12
+
13
+
14
+ # Programmatically click this clickable element.
15
+ # For testing and scripting.
16
+ click: -> @container.click()
17
+
18
+
19
+ # Event handler for clicks on this clickable element.
20
+ container_clicked: -> @fire_event modularity.clickable.events.clicked
21
+
@@ -0,0 +1,21 @@
1
+ window.modularity.closable =
2
+
3
+ constructor: (container) ->
4
+ close_button = @container.find('.CloseButton')
5
+ unless close_button?.length > 0
6
+ window.alert 'Error: Close button not found'
7
+ close_button.click => @close_button_clicked()
8
+
9
+
10
+ # The events that can be fired by closable objects.
11
+ events:
12
+ closed: 'closed'
13
+
14
+
15
+ # Called when the button got clicked by the user or programmatically.
16
+ close_button_clicked: ->
17
+ if @closable_closing
18
+ return unless @closable_closing()
19
+ @fire_event 'closed'
20
+ @container.remove()
21
+ @closable_closed() if @closable_closed
@@ -0,0 +1,128 @@
1
+ # The Modularity framework written specificially for CoffeeScript.
2
+ #
3
+ # Use UglifyJS (http://github.com/mishoo/UglifyJS) for compression.
4
+ #
5
+ # Please see https://github.com/kevgo/modularity for more information on Modularity,
6
+ # and https://github.com/kevgo/modularity-rails for Rails integration.
7
+
8
+
9
+ window.modularity = {
10
+
11
+ # Checks whether the given condition is true.
12
+ # Shows an alert with the given message if not.
13
+ assert: (condition, message) ->
14
+ if !condition or condition?.length == 0
15
+ alert message
16
+ return false
17
+ true
18
+
19
+
20
+ # GLOBAL EVENTS.
21
+
22
+ # Subscribes to the given global event,
23
+ # i.e. calls the given function when the given global event type happens.
24
+ bind_global_event: (event_type, callback) ->
25
+ return unless modularity.assert typeof event_type == 'string', "modularity.bind_global_event: parameter 'event_type' is empty"
26
+ return alert "modularity.bind_global_event: parameter 'callback' must be a function, #{callback} (#{typeof callback}) given." unless typeof callback == 'function'
27
+ modularity.global_event_container().bind event_type, callback
28
+
29
+ # Fires the given global event with the given data payload.
30
+ fire_global_event: (event_type, data) ->
31
+ modularity.assert event_type, 'Module.fire_global_event: You must provide the event type to fire.'
32
+ return alert("Module.fire_global_event: Event type must be a string, #{event_type} (#{typeof event_type}) given.") unless typeof event_type == 'string'
33
+ modularity.global_event_container().trigger event_type, data ?= []
34
+
35
+ # Returns the DOM object that is used to fire global events on.
36
+ global_event_container: -> modularity.global_event_container_cache or= $(window)
37
+ }
38
+ window.modularity.bindGlobalEvent = window.modularity.bind_global_event
39
+ window.modularity.fireGlobalEvent = window.modularity.fire_global_event
40
+
41
+
42
+ class window.modularity.Module
43
+
44
+ # The container variable is required. Provide 'testing' in tests.
45
+ constructor: (container) ->
46
+ container = $(container) if (typeof container == 'string') and container != 'testing'
47
+ @container = container
48
+
49
+ return alert 'Error in Module constructor: No container given.' unless @container?
50
+ if container != 'testing'
51
+ return alert 'Error in Module constructor: The given container must be a jQuery object.' unless typeof container.jquery == 'string'
52
+ return alert "Error in Module constructor: The given container ('#{container.selector}') is empty." unless container? and container.length > 0
53
+ return alert "Error in Module constructor: The given container ('#{container.selector}') has more than one element." unless container? and container.length == 1
54
+
55
+
56
+ # Attach mixins.
57
+ if @mixins?
58
+ for mixin_data in @mixins
59
+
60
+ # Attach all properties from mixin to the prototype.
61
+ for methodName, method of mixin_data.mixin
62
+ do (methodName, method) => unless @[methodName]
63
+ @[methodName] = => method.apply(@, arguments)
64
+
65
+ # Call constructor function from mixin.
66
+ mixin_data.mixin?.constructor?.apply(@, arguments)
67
+
68
+
69
+ # Runs the given query within the container element.
70
+ $: (query) ->
71
+ @container.find query
72
+
73
+
74
+ # MODULE EVENTS.
75
+
76
+ # Calls the given function when this widget fires the given local event.
77
+ bind_event: (event_type, callback) =>
78
+ return unless modularity.assert typeof event_type == 'string', "Module.bind_event: parameter 'event_type' is empty"
79
+ return alert "Module.bind_event: parameter 'callback' must be a function, #{callback} (#{typeof callback}) given." unless typeof callback == 'function'
80
+ @container.bind event_type, callback
81
+ bindEvent: Module::bind_event
82
+
83
+ # Fires the given local event with the given data payload.
84
+ fire_event: (event_type, data) =>
85
+ modularity.assert event_type, 'Module.fire_event: You must provide the event type to fire.'
86
+ return alert("Module.fire_event: Event type must be a string, #{event_type} (#{typeof event_type}) given.") unless typeof event_type == 'string'
87
+ @container.trigger event_type, data ?= {}
88
+ fireEvent: Module::fire_event
89
+
90
+
91
+ # Hides this module.
92
+ hide: ->
93
+ @container.hide()
94
+
95
+
96
+ # mixin = constructor of Draggable
97
+ # self = Card
98
+ @mixin: (mixin, p...) ->
99
+ alert("mixin not found") unless mixin
100
+ @prototype.mixins or= []
101
+ @prototype.mixins.push({mixin: mixin, params: p})
102
+
103
+
104
+ # Shows this module.
105
+ show: ->
106
+ @container.show()
107
+
108
+
109
+ # jQuery integration for creating Modules.
110
+ #
111
+ # Call like this: myModule = $('...').module(MyModuleClass)
112
+ #
113
+ # Parameters:
114
+ # * klass: the class of the Module to instantiate
115
+ # * any additional parameters are forwarded to the Module constructor.
116
+ # Returns the created module instance.
117
+ #
118
+ # Messages errors in alert boxes.
119
+ #
120
+ jQuery.fn.module = (klass, args...) ->
121
+
122
+ # Check parameters.
123
+ if typeof klass != 'function'
124
+ return alert "ERROR!\n\nYou must provide the Module class when calling $.module().\n\nExample: $('...').module(MyModuleClass)\n\nYou provided: #{klass} (#{typeof klass})"
125
+
126
+ # Instantiate the class and return the instance.
127
+ new klass(this, args...)
128
+
@@ -0,0 +1,32 @@
1
+ # Autogrowing textarea.
2
+ class window.modularity.AutogrowTextArea extends modularity.Module
3
+
4
+ constructor: (container) ->
5
+ super
6
+ @textarea = @container[0]
7
+ @textarea.style.height = "auto"
8
+ @textarea.style.overflow = "hidden"
9
+ @container.keyup @grow
10
+ @container.focus @grow
11
+ @container.blur @grow
12
+
13
+ # How many characters per line.
14
+ @characters_per_line = this.textarea.cols
15
+
16
+ # The initial (minimal) number of rows.
17
+ @min_rows = this.textarea.rows
18
+
19
+ @grow()
20
+
21
+
22
+ # Sets the height of the textarea according to the content.
23
+ grow: =>
24
+ @textarea.rows = Math.max modularity.AutogrowTextArea.lines(@characters_per_line, @textarea.value), @min_rows
25
+
26
+ # Returns the number of lines
27
+ @lines: (width, text) ->
28
+ lines_count = 0
29
+ lines = text.split(/\n/)
30
+ lines_count += Math.floor((line.length / width) + 1) for line in lines
31
+ lines_count
32
+
@@ -0,0 +1,8 @@
1
+ #= require modularity/mixins/clickable
2
+
3
+ # A button module.
4
+ # Responds to clicks on the container element.
5
+ # The container element is expected to already be populated.
6
+ class window.modularity.Button extends window.modularity.Module
7
+ @mixin window.modularity.clickable
8
+
@@ -0,0 +1,6 @@
1
+ # Returns a copy of the given array with duplicates removed.
2
+ window.modularity.array_unique = (array) ->
3
+ output = {}
4
+ output[array[i]] = array[i] for i in [0...array.length]
5
+ value for key, value of output
6
+
@@ -0,0 +1,25 @@
1
+ # Returns a replica of the given hash.
2
+ # Don't use this method for real objects with superclasses, prototypes, and stuff.
3
+ modularity.clone_hash = (obj) ->
4
+ result = {}
5
+ result[key] = value for own key, value of obj
6
+ result
7
+
8
+
9
+ # Returns an object that contains only the attributes
10
+ # that are different between obj_1 and obj_2.
11
+ # Only looks for changed attributes, not missing attributes.
12
+ modularity.object_diff = (obj_1, obj_2) ->
13
+ result = {}
14
+ for own key, value_2 of obj_2
15
+ do (key, value_2) ->
16
+ value_1 = obj_1[key]
17
+ result[key] = value_2 if value_1 != value_2
18
+ result
19
+
20
+
21
+ # Returns the number of attributes of the given object.
22
+ # NOTE(KG): This doesn't work in IE8.
23
+ modularity.object_length = (obj) ->
24
+ Object.keys(obj).length
25
+
@@ -0,0 +1,4 @@
1
+ module ModularityCoffeeRails
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ModularityCoffeeRails
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ModularityCoffeeRails</title>
5
+ <%= stylesheet_link_tag "modularity-coffee-rails/application", :media => "all" %>
6
+ <%= javascript_include_tag "modularity-coffee-rails/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ ModularityCoffeeRails::Engine.routes.draw do
2
+ end
@@ -0,0 +1,4 @@
1
+ require "modularity-coffee-rails/engine"
2
+
3
+ module ModularityCoffeeRails
4
+ end
@@ -0,0 +1,5 @@
1
+ module ModularityCoffeeRails
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ModularityCoffeeRails
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module ModularityCoffeeRails
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :modularity-coffee-rails do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: modularity-coffee-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kevin Goslar
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.12
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.12
30
+ - !ruby/object:Gem::Dependency
31
+ name: coffee-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: jquery-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: konacha
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec-rails
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: sqlite3
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Makes the modularity-coffee framework available to Ruby on Rails applications.
111
+ email:
112
+ - kevin.goslar@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - app/assets/javascripts/modularity/application.js
118
+ - app/assets/javascripts/modularity/data/ajax_loader.coffee
119
+ - app/assets/javascripts/modularity/data/cache.coffee
120
+ - app/assets/javascripts/modularity/data/indexed_cache.coffee
121
+ - app/assets/javascripts/modularity/data/persistence_manager.coffee
122
+ - app/assets/javascripts/modularity/mixins/clickable.coffee
123
+ - app/assets/javascripts/modularity/mixins/closable.coffee
124
+ - app/assets/javascripts/modularity/modularity.coffee
125
+ - app/assets/javascripts/modularity/modules/autogrow_textarea.coffee
126
+ - app/assets/javascripts/modularity/modules/button.coffee
127
+ - app/assets/javascripts/modularity/tools/array_tools.coffee
128
+ - app/assets/javascripts/modularity/tools/object_tools.coffee
129
+ - app/controllers/modularity-coffee-rails/application_controller.rb
130
+ - app/helpers/modularity-coffee-rails/application_helper.rb
131
+ - app/views/layouts/modularity-coffee-rails/application.html.erb
132
+ - config/routes.rb
133
+ - lib/modularity-coffee-rails/engine.rb
134
+ - lib/modularity-coffee-rails/version.rb
135
+ - lib/modularity-coffee-rails.rb
136
+ - lib/tasks/modularity-coffee-rails_tasks.rake
137
+ - MIT-LICENSE
138
+ - Rakefile
139
+ - README.md
140
+ homepage: http://github.com/kevgo/modularity-rails
141
+ licenses: []
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 1.8.23
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: A lightweight, object- and component-oriented CoffeeScript framework for
164
+ Ruby on Rails applications.
165
+ test_files: []