meteorite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []