communicator 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -6,6 +6,7 @@ PATH
6
6
  httparty (>= 0.6.1)
7
7
  json (>= 1.4.0)
8
8
  sinatra (~> 1.1.0)
9
+ sinatra-basic-auth
9
10
 
10
11
  GEM
11
12
  remote: http://rubygems.org/
@@ -25,6 +26,8 @@ GEM
25
26
  sinatra (1.1.0)
26
27
  rack (~> 1.1)
27
28
  tilt (~> 1.1)
29
+ sinatra-basic-auth (0.1.0)
30
+ sinatra
28
31
  sqlite3-ruby (1.3.2)
29
32
  tilt (1.1)
30
33
 
@@ -41,4 +44,5 @@ DEPENDENCIES
41
44
  rack-test (>= 0.5.6)
42
45
  shoulda (= 2.10.3)
43
46
  sinatra (~> 1.1.0)
47
+ sinatra-basic-auth
44
48
  sqlite3-ruby (>= 1.3.0)
data/README.rdoc CHANGED
@@ -59,6 +59,11 @@ class receives updates from like this:
59
59
  class Post < ActiveRecord::Base
60
60
  receives_from :post
61
61
  end
62
+
63
+ If you want to skip some remote attributes and not let them progapate into local instances,
64
+ use `receives_from :post, :except => [:user_id]`.
65
+
66
+ Attributes that are not physically present in the local database will be skipped automatically.
62
67
 
63
68
  Use the automatically added `publish` instance method on your models to push changes
64
69
  to the other side. They will be enqueued in the local `outbound_messages` table and
data/Rakefile CHANGED
@@ -54,7 +54,10 @@ namespace :test_server do
54
54
  Thread.new do
55
55
  # Capture sinatra output (to hide it away...)
56
56
  require "open3"
57
- Open3.popen3("bundle exec rackup test/config.ru -p 20359 --pid=#{File.join(File.dirname(__FILE__), 'test', 'rack.pid')}")
57
+ rackup = "bundle exec rackup test/config.ru -p 20359 --pid=#{File.join(File.dirname(__FILE__), 'test', 'rack.pid')}"
58
+ Open3.popen3(rackup)
59
+ #`#{rackup}` # Uncomment to see actual server output
60
+
58
61
  end
59
62
  sleep 2.0
60
63
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/communicator.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{communicator}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Christoph Olszowka"]
@@ -39,11 +39,14 @@ Gem::Specification.new do |s|
39
39
  "test/config.ru",
40
40
  "test/factories.rb",
41
41
  "test/helper.rb",
42
+ "test/lib/comment.rb",
42
43
  "test/lib/post.rb",
44
+ "test/lib/test_server_database/comment.rb",
43
45
  "test/lib/test_server_database/inbound_message.rb",
44
46
  "test/lib/test_server_database/outbound_message.rb",
45
47
  "test/lib/test_server_database/post.rb",
46
48
  "test/migrate/20101101093519_create_posts.rb",
49
+ "test/migrate/20101103120519_create_comments.rb",
47
50
  "test/test_client.rb",
48
51
  "test/test_message_models.rb",
49
52
  "test/test_server.rb"
@@ -56,11 +59,14 @@ Gem::Specification.new do |s|
56
59
  s.test_files = [
57
60
  "test/factories.rb",
58
61
  "test/helper.rb",
62
+ "test/lib/comment.rb",
59
63
  "test/lib/post.rb",
64
+ "test/lib/test_server_database/comment.rb",
60
65
  "test/lib/test_server_database/inbound_message.rb",
61
66
  "test/lib/test_server_database/outbound_message.rb",
62
67
  "test/lib/test_server_database/post.rb",
63
68
  "test/migrate/20101101093519_create_posts.rb",
69
+ "test/migrate/20101103120519_create_comments.rb",
64
70
  "test/test_client.rb",
65
71
  "test/test_message_models.rb",
66
72
  "test/test_server.rb"
@@ -8,8 +8,18 @@ module Communicator::ActiveRecordIntegration
8
8
  # receives_from :post
9
9
  # end
10
10
  #
11
- def receives_from(source)
12
- Communicator.register_receiver(self, source)
11
+ def receives_from(source, options={})
12
+ Communicator.register_receiver(self, source, options)
13
+ end
14
+
15
+ def skipped_remote_attributes
16
+ @skipped_remote_attributes ||= []
17
+ end
18
+
19
+ def skip_remote_attributes(*attr_names)
20
+ attr_names.each do |attr_name|
21
+ skipped_remote_attributes << attr_name.to_sym
22
+ end
13
23
  end
14
24
  end
15
25
 
@@ -24,11 +34,13 @@ module Communicator::ActiveRecordIntegration
24
34
  end
25
35
 
26
36
  # Processes the given message body by applying all contained attributes and their values
27
- # and saving
37
+ # and saving. When the setter instance method is missing on the local record, skip that attribute.
28
38
  def process_message(input)
29
39
  # When the input is still json, parse it. Otherwise we're assuming it's already a demarshalled hash
30
40
  input = JSON.parse(input) if input.kind_of?(String)
31
41
  input.each do |attr_name, value|
42
+ # Exclude skipped attributes
43
+ next if self.class.skipped_remote_attributes.include?(attr_name.to_sym) or !attributes.has_key?(attr_name)
32
44
  self.send("#{attr_name}=", value)
33
45
  end
34
46
  self.updated_from_message = true
@@ -16,12 +16,28 @@ class Communicator::Server < Sinatra::Base
16
16
  end
17
17
  end
18
18
 
19
- use Rack::Auth::Basic do |username, password|
20
- [username, password] == [Communicator::Server.username, Communicator::Server.password]
19
+ # use Rack::Auth::Basic do |username, password|
20
+ # [username, password] == [Communicator::Server.username, Communicator::Server.password]
21
+ # end
22
+
23
+ helpers do
24
+ def protected!
25
+ unless authorized?
26
+ response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
27
+ throw(:halt, [401, "Not authorized\n"])
28
+ end
29
+ end
30
+
31
+ def authorized?
32
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
33
+ @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == [Communicator::Server.username, Communicator::Server.password]
34
+ end
21
35
  end
36
+
22
37
 
23
38
  # PULL
24
39
  get '/messages.json' do
40
+ protected!
25
41
  # Require from_id attribute
26
42
  return [409, "Specify from_id!"] unless params[:from_id]
27
43
 
@@ -39,6 +55,8 @@ class Communicator::Server < Sinatra::Base
39
55
 
40
56
  # PUSH
41
57
  post '/messages.json' do
58
+ protected!
59
+
42
60
  body = request.body.read.strip
43
61
  # Make sure a message body is given!
44
62
  return [409, "No data given"] if body.length < 2
data/lib/communicator.rb CHANGED
@@ -20,15 +20,28 @@ module Communicator
20
20
  # Register a given class as a receiver from source (underscored name). Will then
21
21
  # mix in the instance methods from Communicator::ActiveRecord::InstanceMethods so
22
22
  # message processing and publishing functionality is included
23
- def register_receiver(target, source)
23
+ def register_receiver(target, source, options={})
24
24
  receivers[source] = target
25
25
  target.send(:include, Communicator::ActiveRecordIntegration::InstanceMethods)
26
+
27
+ target.skip_remote_attributes(*options[:except]) if options[:except]
28
+
29
+ target
26
30
  end
27
31
 
28
32
  # Tries to find the receiver for given source, raising Communicator::ReceiverUnknown
29
33
  # on failure
30
34
  def receiver_for(source)
31
35
  return receivers[source] if receivers[source]
36
+
37
+ # If not found in the first place, maybe the class just isn't loaded yet and
38
+ # thus hasn't registered - let's require all models and try again
39
+ if defined?(Rails)
40
+ Dir[File.join(Rails.root, 'app/models/**/*.rb')].each {|model| require model}
41
+ return receivers[source] if receivers[source]
42
+ end
43
+
44
+ # When everything else fails, just throw an exception...
32
45
  raise Communicator::ReceiverUnknown.new("No receiver registered for '#{source}'")
33
46
  end
34
47
  end
data/test/config.ru CHANGED
@@ -9,6 +9,7 @@ require 'communicator'
9
9
 
10
10
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "db/test_server.sqlite3")
11
11
  require 'lib/post'
12
+ require 'lib/comment'
12
13
 
13
14
  Communicator::Server.username = 'testuser'
14
15
  Communicator::Server.password = 'pwd'
data/test/helper.rb CHANGED
@@ -16,12 +16,14 @@ require 'factories'
16
16
  # Connect to client test database
17
17
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "db/test_client.sqlite3")
18
18
  require 'lib/post'
19
+ require 'lib/comment'
19
20
 
20
21
  # Connect to server database too so we can peek into what's happening over there
21
22
  class TestServerDatabase < ActiveRecord::Base
22
23
  establish_connection(:adapter => 'sqlite3', :database => "db/test_server.sqlite3")
23
24
  end
24
25
  require 'lib/test_server_database/post'
26
+ require 'lib/test_server_database/comment'
25
27
  require 'lib/test_server_database/inbound_message'
26
28
  require 'lib/test_server_database/outbound_message'
27
29
 
@@ -39,9 +41,11 @@ class Test::Unit::TestCase
39
41
  Communicator::InboundMessage.delete_all
40
42
  Communicator::OutboundMessage.delete_all
41
43
  Post.delete_all
44
+ Comment.delete_all
42
45
 
43
46
  TestServerDatabase::InboundMessage.delete_all
44
47
  TestServerDatabase::OutboundMessage.delete_all
45
48
  TestServerDatabase::Post.delete_all
49
+ TestServerDatabase::Comment.delete_all
46
50
  end
47
51
  end
@@ -0,0 +1,7 @@
1
+ # A simple class for publishing and receiving messages
2
+ class Comment < ActiveRecord::Base
3
+ receives_from :comment, :except => [:title]
4
+ after_save do |r|
5
+ r.publish unless r.updated_from_message
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # Basic class for introspection of what is happening on the server side
2
+ class TestServerDatabase::Comment < TestServerDatabase
3
+ set_table_name "comments"
4
+ end
@@ -0,0 +1,13 @@
1
+ class CreateComments < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :comments do |t|
4
+ t.string :title, :default => nil
5
+ t.text :body, :null => false
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :comments
12
+ end
13
+ end
data/test/test_client.rb CHANGED
@@ -121,6 +121,22 @@ class TestClient < Test::Unit::TestCase
121
121
  end
122
122
  end
123
123
 
124
+ # Make sure attributes skipped with :except are not saved at remote
125
+ context "PUSHing a new comment" do
126
+ setup do
127
+ assert @comment = Comment.create(:title => 'my comment', :body => 'some comment')
128
+ Communicator::Client.push
129
+ end
130
+
131
+ should "have created the comment at remote" do
132
+ assert_equal 'some comment', TestServerDatabase::Comment.first.body
133
+ end
134
+
135
+ should "have skipped permissions attribute when processing at remote" do
136
+ assert_nil TestServerDatabase::Comment.first.title
137
+ end
138
+ end
139
+
124
140
  context "when an update message is created at remote" do
125
141
  setup do
126
142
  @remote_msg = TestServerDatabase::OutboundMessage.create!(:body => {:post => {:id => 25, :title => 'new title', :body => 'remote body'}}.to_json)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: communicator
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Christoph Olszowka
@@ -194,11 +194,14 @@ files:
194
194
  - test/config.ru
195
195
  - test/factories.rb
196
196
  - test/helper.rb
197
+ - test/lib/comment.rb
197
198
  - test/lib/post.rb
199
+ - test/lib/test_server_database/comment.rb
198
200
  - test/lib/test_server_database/inbound_message.rb
199
201
  - test/lib/test_server_database/outbound_message.rb
200
202
  - test/lib/test_server_database/post.rb
201
203
  - test/migrate/20101101093519_create_posts.rb
204
+ - test/migrate/20101103120519_create_comments.rb
202
205
  - test/test_client.rb
203
206
  - test/test_message_models.rb
204
207
  - test/test_server.rb
@@ -239,11 +242,14 @@ summary: Data push/pull between apps with local inbound/outbound queue and easy
239
242
  test_files:
240
243
  - test/factories.rb
241
244
  - test/helper.rb
245
+ - test/lib/comment.rb
242
246
  - test/lib/post.rb
247
+ - test/lib/test_server_database/comment.rb
243
248
  - test/lib/test_server_database/inbound_message.rb
244
249
  - test/lib/test_server_database/outbound_message.rb
245
250
  - test/lib/test_server_database/post.rb
246
251
  - test/migrate/20101101093519_create_posts.rb
252
+ - test/migrate/20101103120519_create_comments.rb
247
253
  - test/test_client.rb
248
254
  - test/test_message_models.rb
249
255
  - test/test_server.rb