meteor-motion 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.repl_history +0 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +166 -0
  7. data/Rakefile +17 -0
  8. data/app/app_delegate.rb +11 -0
  9. data/app/controllers/book_controller.rb +92 -0
  10. data/app/controllers/book_list_controller.rb +105 -0
  11. data/app/controllers/connection_controller.rb +83 -0
  12. data/app/controllers/login_controller.rb +35 -0
  13. data/lib/meteor-motion.rb +12 -0
  14. data/lib/meteor-motion/version.rb +3 -0
  15. data/meteor-motion.gemspec +27 -0
  16. data/motion/adapters/motion_model.rb +61 -0
  17. data/motion/client.rb +179 -0
  18. data/motion/collection.rb +50 -0
  19. data/motion/collections/default.rb +56 -0
  20. data/motion/collections/motion_model.rb +52 -0
  21. data/motion/ddp.rb +161 -0
  22. data/motion/srp/securerandom.rb +248 -0
  23. data/motion/srp/srp.rb +250 -0
  24. data/spec/adapters/motion_model_spec.rb +38 -0
  25. data/spec/client_spec.rb +104 -0
  26. data/spec/collection_spec.rb +63 -0
  27. data/spec/collections/default_spec.rb +46 -0
  28. data/spec/collections/motion_model_spec.rb +69 -0
  29. data/spec/ddp_spec.rb +123 -0
  30. data/spec/server/.meteor/.gitignore +1 -0
  31. data/spec/server/.meteor/packages +9 -0
  32. data/spec/server/.meteor/release +1 -0
  33. data/spec/server/collections/books.js +11 -0
  34. data/spec/server/server/fixtures.js +28 -0
  35. data/spec/server/server/publications.js +3 -0
  36. data/spec/server/smart.json +3 -0
  37. data/vendor/SocketRocket/NSData+SRB64Additions.h +24 -0
  38. data/vendor/SocketRocket/NSData+SRB64Additions.m +39 -0
  39. data/vendor/SocketRocket/SRWebSocket.h +114 -0
  40. data/vendor/SocketRocket/SRWebSocket.m +1757 -0
  41. data/vendor/SocketRocket/SocketRocket-Prefix.pch +27 -0
  42. data/vendor/SocketRocket/SocketRocket.bridgesupport +160 -0
  43. data/vendor/SocketRocket/base64.c +314 -0
  44. data/vendor/SocketRocket/base64.h +34 -0
  45. metadata +190 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a15bd2d5261b5c3cd00cf2892d0cb31e4fb3c72f
4
+ data.tar.gz: cb93ae359474741e4d91781d56b353a762345e99
5
+ SHA512:
6
+ metadata.gz: 843330aa37a6cd2c95e903258bb199a75cdd5e75b97d344af3a89ac1c26dae39a5e2a9f98b25d2dd212458fc8de86fb105561065fceaf9d3f95ce1dc67aaaad2
7
+ data.tar.gz: 4d9c4a63b4ecb45c8456348a87e1a6154192ede3437209576a8101cda88c9fd4f0df270125558254854e5d90ea7097035ebc92a902e82f1ece68c3a3897447ac
@@ -0,0 +1,21 @@
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
18
+ build
19
+ private-docs
20
+ vendor/**/build*
21
+ **/.DS_Store
File without changes
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rm-digest', git: 'git://github.com/tmeinlschmidt/rm-digest.git'
4
+ gem 'formotion'
5
+ # Specify your gem's dependencies in meteor-motion.gemspec
6
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TODO: Write your name
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,166 @@
1
+ # MeteorMotion
2
+
3
+ Rubymotion Wrapper for communication with Meteor apps via DDP, with SRP authentication capabilities.
4
+
5
+ ## Installation
6
+
7
+ If using Bundler, add to your application's Gemfile:
8
+
9
+ gem 'meteor-motion'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install meteor-motion
18
+
19
+ And in your application's Rakefile add:
20
+
21
+ ```ruby
22
+ require 'meteor-motion'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Initialization
28
+
29
+ Create a new MeteorMotion client and connect to your Meteor app:
30
+
31
+ ```ruby
32
+ client = MeteorMotion::Client.new
33
+ client.connect('localhost', 3000, self.method(:on_connect) )
34
+
35
+ def on_connect status
36
+ # Handler for the connection attempt - optional
37
+ # status - either true or false
38
+ end
39
+
40
+ def error_handler code, reason, details
41
+ # Handler for general connection errors, malformed messages and failed subscriptions
42
+ end
43
+
44
+ client.on_error( self.method(:error_handler) )
45
+ ```
46
+
47
+ By default, it will expand the hostname to ```http://hostname:port/websocket``` per the current Meteor specifications.
48
+
49
+ ### Collections and subscriptions
50
+
51
+ In order to receive data, you need to first create a local collection to handle the data. You should add an observer to this collection, that will be called whenever the data in the collection is changed.
52
+
53
+ ```ruby
54
+ def collection_handler action, id
55
+ # action - will be one of [:added, :changed, :removed]
56
+ # id - the id of the element of the collection affected
57
+ end
58
+
59
+ collection = client.add_collection('collection_name')
60
+ collection.add_observer( self.method(:collection_handler) )
61
+ ```
62
+
63
+ To remove an observer, simply call ```collection.remove_observer( method )```. With a collection setup, you can subscribe/unsubscribe to data published on the server with:
64
+
65
+ ```ruby
66
+ client.subscribe('subscription_name', params)
67
+ client.unsubscribe('subscription_name')
68
+ ```
69
+
70
+ When there is data available, you can access objects on the collection:
71
+
72
+ ```ruby
73
+ object = collection.find(id)
74
+ att1 = object['att1']
75
+ att2 = object['att2']
76
+ ```
77
+
78
+ ### Model Adapters
79
+ Meteor-Motion supports, for the time being, and adapter to be used with the [MotionModel gem](https://github.com/sxross/MotionModel). After installing it, simply define your model classes as such:
80
+
81
+ ```ruby
82
+ class Objects
83
+ include MotionModel::Model
84
+ include MotionModel::ArrayModelAdapter
85
+ include MeteorMotion::Adapters::MotionModel
86
+
87
+ columns :id, :string
88
+ #...
89
+ end
90
+
91
+ client.add_collection(Objects, name='objects')
92
+
93
+ ```
94
+
95
+ The ```id```column is mandatory, so that MeteorMotion does not auto-generate and id column with an integer type, which is incompatible with Meteor standard. Also, take care that if you ommit the ```name``` parameter when adding the collection, the collection name will default to the downcased name of your class. After this setup, enjoy MotionModel as usual.
96
+
97
+ ### Method calls
98
+
99
+ ```ruby
100
+ def method_callback action, result
101
+ # action - one of the following [:result, :updated, :error]
102
+ # result - either the return value of the method (when action == :result)
103
+ # or error details (when action == :error)
104
+ end
105
+
106
+ client.call('some_method_name', self.method(:method_callback), params)
107
+ ```
108
+
109
+ ### Authentication
110
+
111
+ Right now, MeteorMotion provides support for authentication using Meteor's built-in SRP. To authenticate with a username/password combination use the following:
112
+
113
+ ```ruby
114
+ def login_handler action, details
115
+ # action - either :success or :error
116
+ # details - nil on :success, Hash with error details on :error
117
+ end
118
+
119
+ client.login_with_username('username', 'password', self.method(:login_handler))
120
+ ```
121
+
122
+ ## Testing
123
+
124
+ To run tests first start-up the included sample Meteor app:
125
+
126
+ ```bash
127
+ cd spec/server
128
+ meteor run
129
+ ```
130
+
131
+ Then run the tests with rake:
132
+
133
+ ```bash
134
+ rake spec
135
+ ```
136
+
137
+ ### Example app
138
+
139
+ You can find a working example in the ```app``` directory. Remember to run the meteor server prior to starting the application. To login, use the credentials ```user/pass```. To run the application in the simulator simply run:
140
+
141
+ ```bash
142
+ rake
143
+ ```
144
+
145
+ ## Credits
146
+ ### Developed by
147
+ <a href="http://whitesmith.co/">
148
+ <img src="http://www.whitesmith.co/assets/logo-whitesmith-4109176a79f86b9ca4b8022d6dcab3bb.png" alt="http://whitesmith.co/" height=100px />
149
+ </a>
150
+
151
+ Lead Developer: [mtavaresOS](https://github.com/mtavaresOS)
152
+
153
+ ### Sponsored by
154
+ <a href="http://www.revokom.com/">
155
+ <img src="http://www.revokom.com/img/logo_banner.png" alt="http://www.revokom.com/"/>
156
+ </a>
157
+
158
+
159
+
160
+ ## Contributing
161
+
162
+ 1. Fork it
163
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
164
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
165
+ 4. Push to the branch (`git push origin my-new-feature`)
166
+ 5. Create new Pull Request
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project/template/ios'
4
+ require 'bundler/gem_tasks'
5
+ require 'bubble-wrap/test'
6
+ require 'bubble-wrap/core'
7
+ require 'motion_model'
8
+ require 'motion-redgreen'
9
+ Bundler.setup
10
+ Bundler.require
11
+
12
+ Motion::Project::App.setup do |app|
13
+ # Use `rake config' to see complete project settings.
14
+ app.name = 'MeteorMotion Example'
15
+ app.delegate_class = "AppDelegate"
16
+ app.redgreen_style = :full
17
+ end
@@ -0,0 +1,11 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+ @window.makeKeyAndVisible
5
+
6
+ @window.rootViewController = ConnectionController.alloc.initWithNibName(nil, bundle: nil)
7
+
8
+ true
9
+ end
10
+
11
+ end
@@ -0,0 +1,92 @@
1
+ class BookController < Formotion::FormController
2
+ attr_accessor :meteor, :_id
3
+
4
+ def viewDidLoad
5
+ super
6
+ self.title = "Details"
7
+ end
8
+
9
+ def viewWillAppear animated
10
+ super
11
+ @books = @meteor.collections['books']
12
+ @books.add_observer( self.method(:update_form) )
13
+
14
+ self.form.on_submit do |form|
15
+ data = form.render
16
+ self.view.endEditing(true)
17
+
18
+ if @_id != nil
19
+ @meteor.call('/books/update', self.method(:handle_update), [{_id: @_id},{title: data[:title], author: data[:author],year: data[:year]}])
20
+ else
21
+ new_id = SecureRandom.hex
22
+ @meteor.call('/books/insert', self.method(:handle_update), [{_id: new_id,title: data[:title], author: data[:author],year: data[:year]}])
23
+ end
24
+
25
+ end
26
+
27
+ if @_id != nil
28
+ right_button = UIBarButtonItem.alloc.initWithTitle("Remove", style: UIBarButtonItemStyleDone, target:self, action:'remove')
29
+ self.navigationItem.rightBarButtonItem = right_button
30
+ end
31
+ end
32
+
33
+ def viewWillDisappear animated
34
+ super
35
+ @books.remove_observer( self.method(:update_form) )
36
+ end
37
+
38
+
39
+ def update_form action, id
40
+ if id == @_id
41
+ if action == :removed
42
+ self.navigationController.popViewControllerAnimated(true)
43
+ elsif action == :changed
44
+ book = @books.find(id)
45
+ self.form.row(:title).value = book['title']
46
+ self.form.row(:author).value = book['author']
47
+ self.form.row(:year).value = book['year']
48
+ end
49
+ end
50
+ end
51
+
52
+ def handle_update action, result
53
+ if action == :error
54
+ alert = UIAlertView.new
55
+ alert.message = "Error: #{result['reason'].to_s}"
56
+ alert.addButtonWithTitle "OK"
57
+ alert.show
58
+ elsif action == :result
59
+ alert = UIAlertView.new
60
+ if @_id != nil
61
+ alert.message = "Book updated sucessfully"
62
+ alert.addButtonWithTitle "OK"
63
+ alert.show
64
+ else
65
+ alert.message = "Book added sucessfully"
66
+ alert.addButtonWithTitle "OK"
67
+ alert.show
68
+ self.navigationController.popViewControllerAnimated(true)
69
+ end
70
+ end
71
+ end
72
+
73
+ def handle_remove action, result
74
+ if action == :error
75
+ alert = UIAlertView.new
76
+ alert.message = "Error: #{result['reason'].to_s}"
77
+ alert.addButtonWithTitle "OK"
78
+ alert.show
79
+ elsif action == :result
80
+ alert = UIAlertView.new
81
+ alert.message = "Book removed sucessfully"
82
+ alert.addButtonWithTitle "OK"
83
+ alert.show
84
+ end
85
+ end
86
+
87
+
88
+ def remove
89
+ @meteor.call('/books/remove', self.method(:handle_remove), [{_id: @_id}])
90
+ end
91
+
92
+ end
@@ -0,0 +1,105 @@
1
+ class BookListController < UIViewController
2
+ attr_accessor :meteor
3
+
4
+ def viewDidLoad
5
+ super
6
+ self.view.backgroundColor = UIColor.whiteColor
7
+ self.title = "Books"
8
+
9
+ @table = UITableView.alloc.initWithFrame(self.view.bounds)
10
+ self.view.addSubview @table
11
+
12
+ @table.dataSource = self
13
+ @table.delegate = self
14
+
15
+ @books = @meteor.collections['books']
16
+ @books.add_observer(@table.method(:reloadData))
17
+
18
+ right_button = UIBarButtonItem.alloc.initWithTitle("Add Book", style: UIBarButtonItemStyleDone, target:self, action:'add_book')
19
+ self.navigationItem.rightBarButtonItem = right_button
20
+ end
21
+
22
+ def add_book
23
+ book = {}
24
+ form = build_book_form( book )
25
+
26
+ bookController = BookController.alloc.initWithForm(form)
27
+ bookController.meteor = @meteor
28
+ bookController._id = nil
29
+
30
+ self.navigationController.pushViewController(bookController, animated: true)
31
+ end
32
+
33
+
34
+ def tableView(tableView, cellForRowAtIndexPath: indexPath)
35
+ @reuseIdentifier ||= "CELL_IDENTIFIER"
36
+
37
+ cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier) || begin
38
+ UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:@reuseIdentifier)
39
+ end
40
+
41
+ cell.textLabel.text = @books.all[indexPath.row]['title']
42
+
43
+ cell
44
+ end
45
+
46
+
47
+ def tableView(tableView, numberOfRowsInSection: section)
48
+ @books.size
49
+ end
50
+
51
+
52
+ def tableView(tableView, didSelectRowAtIndexPath:indexPath)
53
+ tableView.deselectRowAtIndexPath(indexPath, animated: true)
54
+
55
+ book = @books.all[indexPath.row]
56
+ form = build_book_form( book )
57
+
58
+ bookController = BookController.alloc.initWithForm(form)
59
+ bookController.meteor = @meteor
60
+ bookController._id = book[:_id]
61
+
62
+ self.navigationController.pushViewController(bookController, animated: true)
63
+ end
64
+
65
+
66
+ def build_book_form book
67
+ form = Formotion::Form.new
68
+
69
+ form.build_section do |section|
70
+ section.title = "Book Details"
71
+
72
+ section.build_row do |row|
73
+ row.title = "Title"
74
+ row.key = :title
75
+ row.type = :string
76
+ row.placeholder = 'Book title'
77
+ row.value = book['title']
78
+ end
79
+
80
+ section.build_row do |row|
81
+ row.title = "Author"
82
+ row.key = :author
83
+ row.type = :string
84
+ row.placeholder = 'Book author'
85
+ row.value = book['author']
86
+ end
87
+
88
+ section.build_row do |row|
89
+ row.title = "Year"
90
+ row.key = :year
91
+ row.type = :string
92
+ row.placeholder = 'Book year'
93
+ row.value = book['year']
94
+ end
95
+
96
+ section.build_row do |row|
97
+ row.title = 'Save Changes'
98
+ row.type = :submit
99
+ end
100
+ end
101
+
102
+ return form
103
+ end
104
+
105
+ end