meteor-motion 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.
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