meteorite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3f52eccbd85f2408b28c8a2acb0dabdab2eb93b
4
+ data.tar.gz: d8ebb4b1153e0b8764ba678f08f7c2510ac138ef
5
+ SHA512:
6
+ metadata.gz: edc2773a003260faba62a75b9728afbb5de580aa2a1528302bcf48bba677c7105c23a773f7b94f9f5df1717955d157108fa44f8d2478381ffdc52ac0df9b1578
7
+ data.tar.gz: 32cb165eff72479b076842bf3057e2af5b0fa096a0c9c5dae96a1f671f3bcc2d85f91ae66a6ba3dfaf85774807b349cbbaa63fb38c0bbe128c20e5c9239e42a0
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in meteorite.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 llawlor
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,101 @@
1
+ # Meteorite
2
+
3
+ Meteorite enables you to add two-way data binding to your application with minimal effort.
4
+
5
+ ## Dependencies
6
+
7
+ You need Redis installed and running for Meteorite to work properly.
8
+
9
+ Install Redis server (on Ubuntu):
10
+
11
+ sudo apt-get install redis-server
12
+
13
+ Run Redis server:
14
+
15
+ nohup redis-server &
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ gem 'meteorite'
22
+
23
+ And then execute:
24
+
25
+ $ bundle install
26
+
27
+ Install required files:
28
+
29
+ $ rails generate meteorite:install
30
+
31
+ The generated JavaScript file needs to be added to the assets pipeline. In app/assets/javascripts/application.js, add this line:
32
+
33
+ //= require meteorite
34
+
35
+ Run the websocket daemon:
36
+
37
+ ruby daemons/websocket.rb
38
+
39
+ ## Example Usage in a Task Model
40
+
41
+ ### Create a task:
42
+ task_controller#create
43
+ ```ruby
44
+ # create the task
45
+ task = Task.create(task_params)
46
+ # render the partial to a string
47
+ task_string = render_to_string(partial: 'task', locals: { task: task })
48
+ # use the $redis.publish method to send your bind_key and task partial
49
+ $redis.publish(Meteorite.bind_key(Task.all), task_string)
50
+ ```
51
+
52
+ tasks/index.html.erb
53
+ ```html
54
+ <table class="meteorite" data-bind-key="<%= Meteorite.bind_key(@tasks) %>">
55
+ <% @tasks.each do |task| %>
56
+ <%= render partial: 'task', locals: { task: task } %>
57
+ <% end %>
58
+ </table>
59
+ ```
60
+
61
+ tasks/_task.html.erb
62
+ ```html
63
+ <%= form_for task do |f| %>
64
+ <label>
65
+ <%= f.check_box :checked, class: 'meteorite', data: { bind_key: Meteorite.bind_key(task), bind_attr: 'checked' } %>
66
+ <%= task.text %>
67
+ </label>
68
+ <% end %>
69
+ ```
70
+
71
+ ### Update a task:
72
+ tasks_controller#update
73
+ ```ruby
74
+ # use the $redis.publish method to send your bind_key and task as JSON
75
+ task = Task.find(params[:id])
76
+ $redis.publish(Meteorite.bind_key(task), task.to_json)
77
+ ```
78
+
79
+ ### Delete a task:
80
+ tasks_controller#destroy
81
+ ```ruby
82
+ # use the $redis.publish method to send your bind_key and 'delete' message
83
+ task = Task.find(params[:id])
84
+ $redis.publish(Meteorite.bind_key(task), 'delete')
85
+ ```
86
+
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
95
+
96
+ ### Items that contributors can help with:
97
+ 1. Tests
98
+ 2. Examples
99
+
100
+ Please discuss major feature changes with me first, to see if changes should be included in this gem or forked as your own.
101
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,103 @@
1
+ // meteorite namespace
2
+ var Meteorite = Meteorite || {};
3
+
4
+ // initialize websocket
5
+ Meteorite.websocket = new WebSocket("ws://192.168.56.101:8080/");
6
+
7
+ console.log('starting meteorite websockets. . .');
8
+
9
+ // when a new websocket message is received
10
+ Meteorite.websocket.onmessage = function(msg) {
11
+ // parse the json message
12
+ // todo: handle non-json messages
13
+ var json = JSON.parse(msg.data);
14
+
15
+ // log the data
16
+ console.log(json);
17
+
18
+ // if this is a delete
19
+ if (json.bind_data === 'delete') {
20
+ // remove the item
21
+ $('#' + json.bind_key).remove();
22
+ // halt further processing
23
+ return false;
24
+ }
25
+
26
+ // for each meteorite class
27
+ $('.meteorite').each(function() {
28
+ // if the bind keys match
29
+ if ($(this).data('bind-key') === json.bind_key) {
30
+ console.log('bind found');
31
+
32
+ // if there is no bind-attr present, this is a collection
33
+ if ($(this).data('bind-attr') === undefined) {
34
+ Meteorite.add(json, $(this));
35
+ // else update the element
36
+ } else {
37
+ Meteorite.update(json, $(this));
38
+ }
39
+
40
+ }
41
+ });
42
+ }
43
+
44
+ // add an item
45
+ Meteorite.add = function(json, $element) {
46
+ // add to the dom
47
+ $element.append(json.bind_data);
48
+ // listen to subscribe events
49
+ Meteorite.subscribe($(json.bind_data).find('.meteorite').data('bind-key'));
50
+ }
51
+
52
+ // update a single attribute
53
+ Meteorite.update = function(json, $element) {
54
+ // get the bind data
55
+ var bind_data = JSON.parse(json.bind_data);
56
+ // desired attribute
57
+ var bind_attr = $element.data('bind-attr');
58
+ // get the attribute data
59
+ var attr_data = bind_data[bind_attr];
60
+
61
+ // update the property
62
+ $element.prop('checked', attr_data);
63
+ // update the text
64
+ $element.text(attr_data);
65
+ // notify event handlers that a change has occurred
66
+ $element.trigger('change');
67
+ }
68
+
69
+ // subscribe to all bind keys of meteorite classes
70
+ Meteorite.subscribeAll = function() {
71
+ // for each meteorite class
72
+ $('.meteorite').each(function() {
73
+ // if there is a bind key
74
+ if ($(this).data('bind-key') !== undefined) {
75
+ // subscribe
76
+ Meteorite.subscribe($(this).data('bind-key'));
77
+ }
78
+ });
79
+ }
80
+
81
+ // wait for websocket to be ready, then subscribe all
82
+ Meteorite.subscribeAfterWebsocket = function() {
83
+ // set a 5ms timeout
84
+ setTimeout(function() {
85
+ // if the websocket is ready
86
+ if (Meteorite.websocket.readyState === 1) {
87
+ // send the subscribe notices
88
+ Meteorite.subscribeAll();
89
+ // try again if not ready
90
+ } else { subscribeAfterWebsocket(); }
91
+ }, 5);
92
+ }
93
+
94
+ // subscribe to events when the websocket is ready
95
+ Meteorite.subscribe = function(bind_key) {
96
+ // send the subscribe notice
97
+ Meteorite.websocket.send(JSON.stringify({ action: 'subscribe', key: bind_key }));
98
+ }
99
+
100
+ // when the document is ready
101
+ $(document).on('page:load ready', function() {
102
+ Meteorite.subscribeAfterWebsocket();
103
+ });
@@ -0,0 +1,21 @@
1
+ require 'rails/generators/base'
2
+
3
+ module Meteorite
4
+
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+
7
+ desc "This generator creates a websocket.rb file at daemons/ and an initializer file at config/initializers/meteorite.rb"
8
+
9
+ source_root File.expand_path("../../templates", __FILE__)
10
+
11
+ def copy_websocket_file
12
+ copy_file "websocket.rb", "daemons/websocket.rb"
13
+ end
14
+
15
+ def copy_initializer_file
16
+ copy_file "meteorite.rb", "config/initializers/meteorite.rb"
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,4 @@
1
+ require "redis"
2
+
3
+ # configure your Redis server
4
+ $redis = Redis.new(url: "redis://127.0.0.1:6379")
@@ -0,0 +1,56 @@
1
+ require 'em-websocket'
2
+ require 'em-hiredis'
3
+ require 'json'
4
+
5
+ $clients = []
6
+
7
+ EM.run do
8
+ puts "starting websocket server. . ."
9
+
10
+ redis = EM::Hiredis.connect #("redis://127.0.0.1:6379")
11
+ pubsub = redis.pubsub
12
+
13
+ # when a pubsub message is received
14
+ pubsub.on(:message) do |channel, message|
15
+ puts [:message, channel, message]
16
+ # for each client
17
+ $clients.each do |client|
18
+ # send a message
19
+ client.send({bind_key: channel, bind_data: message}.to_json)
20
+ end
21
+ end
22
+
23
+ EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
24
+ ws.onopen do |handshake|
25
+ puts "WebSocket connection open"
26
+
27
+ # add the client
28
+ $clients.push(ws)
29
+ # Access properties on the EM::WebSocket::Handshake object, e.g.
30
+ # path, query_string, origin, headers
31
+
32
+ # Publish message to the client
33
+ #ws.send "Hello Client, you connected to #{handshake.path}"
34
+ end
35
+
36
+ ws.onclose do
37
+ puts "Connection closed"
38
+ $clients.delete(ws)
39
+ end
40
+
41
+ ws.onmessage do |msg|
42
+ puts "Received message: #{msg}"
43
+ #ws.send "Pong: #{msg}"
44
+
45
+ json = JSON.parse(msg)
46
+
47
+ if json['action'] == 'subscribe'
48
+ # subscribe
49
+ pubsub.subscribe(json['key'])
50
+ puts "subscribe to: #{json['key']}"
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,7 @@
1
+ require 'meteorite/version'
2
+ require 'meteorite/assets'
3
+ require 'meteorite/bindings'
4
+
5
+
6
+
7
+
@@ -0,0 +1,7 @@
1
+ module Meteorite
2
+ # mount the Rails engine so that our JavaScript assets are included
3
+ module Rails
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module Meteorite
2
+ # get the unique bind key for an instance or collection of ActiveRecord models
3
+ def self.bind_key(object)
4
+
5
+ # if we're dealing with a collection
6
+ if object.class.parent == ActiveRecord
7
+ return "#{object.table_name}"
8
+ # else if we're dealing with an object
9
+ elsif object.class.parent == Object && object.respond_to?('id')
10
+ return "#{object.class.name}-#{object.id}"
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Meteorite
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'meteorite/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "meteorite"
8
+ spec.version = Meteorite::VERSION
9
+ spec.authors = ["llawlor"]
10
+ spec.email = ["primemod3@gmail.com"]
11
+ spec.description = %q{Uses websockets to update HTML pages automatically when the underlying model changes.}
12
+ spec.summary = %q{Two-way data binding for Rails}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_runtime_dependency "em-websocket"
24
+ spec.add_runtime_dependency "em-hiredis"
25
+ spec.add_runtime_dependency "redis"
26
+ spec.add_dependency "railties"
27
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meteorite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - llawlor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: em-websocket
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: em-hiredis
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redis
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: railties
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Uses websockets to update HTML pages automatically when the underlying
98
+ model changes.
99
+ email:
100
+ - primemod3@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - app/assets/javascripts/meteorite.js
111
+ - lib/generators/meteorite/install_generator.rb
112
+ - lib/generators/templates/meteorite.rb
113
+ - lib/generators/templates/websocket.rb
114
+ - lib/meteorite.rb
115
+ - lib/meteorite/assets.rb
116
+ - lib/meteorite/bindings.rb
117
+ - lib/meteorite/version.rb
118
+ - meteorite.gemspec
119
+ homepage: ''
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.4.6
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Two-way data binding for Rails
143
+ test_files: []