dash-fu 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,6 +1,12 @@
1
- 1.0.1 on 2011-08-22
1
+ 1.1.0 on 2011-10-03
2
2
 
3
- Added require_paths to Gemspec.
3
+ Added DashFu.log method. This is experimental and will be nailed down by next
4
+ minor update.
5
+
6
+ Added multi_json as dependency, required for adding new activities.
7
+
8
+ Renamed dash-fu.rb to dash_fu.rb, since this is the more common convention. No
9
+ change to Gem name yet.
4
10
 
5
11
 
6
12
  1.0.0 on 2011-08-19
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.0
@@ -9,10 +9,10 @@ Gem::Specification.new do |spec|
9
9
  spec.summary = "Use me to push data to Dash-fu in real time"
10
10
  spec.description = ""
11
11
  spec.post_install_message = IO.read("README.md")
12
- spec.license = "Public domain"
12
+ spec.license = "Public domain"
13
13
 
14
14
  spec.files = Dir["{bin,lib,test}/**/*", "CHANGELOG", "VERSION", "README.md", "*.gemspec"]
15
- spec.require_paths = [ "lib" ]
16
15
 
17
16
  spec.required_ruby_version = '>= 1.8.7'
17
+ spec.add_dependency "multi_json"
18
18
  end
@@ -0,0 +1,187 @@
1
+ require "socket"
2
+ require 'multi_json'
3
+
4
+
5
+ # Use me to push data to Dash-fu in real time.
6
+ #
7
+ #
8
+ # The only configuration you need is setting the API key in
9
+ # config/environments/production.rb:
10
+ #
11
+ # DashFu.api_key = "<your API key>"
12
+ #
13
+ # You only want to push data in production, so make sure the API key is only set
14
+ # in production environment. Calls to created/active with no API key are
15
+ # simply ignored.
16
+ #
17
+ #
18
+ # You can send events by calling DashFu.push with UID and event, or using the
19
+ # specialized created and active methods.
20
+ #
21
+ # For example, to assign a user to a cohort, we're going to notify Dash-fu
22
+ # whenever an acount gets created:
23
+ #
24
+ # class User < ActiveRecord::Model
25
+ # after_create do
26
+ # DashFu.created id
27
+ # end
28
+ # end
29
+ #
30
+ # In this example, we consider a user active whenever they post a status update,
31
+ # and notify Dash-fu accordingly:
32
+ #
33
+ # class StatusUpdate < ActiveRecord::Model
34
+ # after_create do
35
+ # DashFu.active id
36
+ # end
37
+ # end
38
+ module DashFu
39
+ @mutex = Mutex.new
40
+
41
+ class << self
42
+ # DashFu.host is set by default, this is only useful for testing.
43
+ attr_accessor :host, :port
44
+
45
+ # Set the API key with:
46
+ # DashFu.api_key = "<your API key>"
47
+ attr_accessor :api_key
48
+
49
+ # Logs an activity. An activity has an actor (the user who performed the
50
+ # activity), the action performed (e.g. posted, commented) and the object of
51
+ # the action (e.g. the post).
52
+ #
53
+ # You can call this method with actor, action and optional object (two or
54
+ # three arguments), or with a Hash with the keys actor, action, object and
55
+ # timestamp.
56
+ #
57
+ # For example:
58
+ # DashFu.log user, 'posted', post.title
59
+ # DashFu.log actor: user, action: 'posted', object: { content: post.title }
60
+ #
61
+ #
62
+ # The actor argument is either the ID of a user, an object that responds to
63
+ # to_dashboard, or Hash with the following keys:
64
+ # - uid -- User identifier (must be unique in application, required)
65
+ # - created -- Date/time instance when user account was created
66
+ # - name -- Display name
67
+ # - email -- Email address
68
+ # - image -- URL for profile photo
69
+ # - url -- URL for profile
70
+ #
71
+ # UID is required, all other properties are optional.
72
+ #
73
+ # When using an object that responds to to_dashboard, the to_dashboard
74
+ # method must return a Hash with these keys.
75
+ #
76
+ #
77
+ # The action argument is any string, e.g. 'posted', 'commented'.
78
+ #
79
+ #
80
+ # The object argument is either a string containing HTML markup, an object
81
+ # that respobds to to_dashboard, or Hash with the following keys:
82
+ # - content -- HTML markup describing the object
83
+ #
84
+ # HTML markup may use semantic styling elements such as b, em, p,
85
+ # blockquote. Styles and scripts are stripped, and only links to HTTP/S
86
+ # URLs are preserved.
87
+ #
88
+ # When using an object that responds to to_dashboard, the to_dashboard
89
+ # method must return a Hash with these keys.
90
+ #
91
+ #
92
+ # The timestamp argument is the date/time instance the activity occurred.
93
+ def log(*args)
94
+ return unless @api_key # in test/dev mode, return quickly
95
+
96
+ if args.length == 1 && args[0].respond_to?(:values_at)
97
+ actor, action, object, timestamp = args[0].values_at(:actor, :action, :object, :timestamp)
98
+ elsif args.length == 2 || args.length == 3
99
+ actor, action, object = *args
100
+ else
101
+ raise ArgumentError.new("Expected Hash or actor, action, (object)")
102
+ end
103
+
104
+ if actor.respond_to?(:to_dashboard)
105
+ actor = actor.to_dashboard
106
+ elsif String === actor
107
+ actor = { uid: actor }
108
+ end
109
+
110
+ action = action.to_s
111
+
112
+ if object.respond_to?(:to_dashboard)
113
+ object = object.to_dashboard
114
+ elsif String === object
115
+ object = { content: object }
116
+ end
117
+
118
+
119
+ params = { actor: actor, action: action, object: object, timestamp: timestamp }
120
+ json = MultiJson.encode(params)
121
+ puts json
122
+ if socket = @socket
123
+ socket.sendmsg_nonblock "POST /v1/activity?api_key=#{@api_key} HTTP/1.1\r\nHost: #{@host}\r\nConnection: keep-alive\r\nContent-Type: application/json\r\nContent-Length: #{json.length}\r\n\r\n#{json}", 0
124
+ socket.recv_nonblock 512 rescue nil
125
+ else
126
+ Thread.new do
127
+ @mutex.synchronize do
128
+ @socket ||= TCPSocket.open(@host, @port || 80)
129
+ end
130
+ log params
131
+ end
132
+ end
133
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT
134
+ close
135
+ retry
136
+ rescue Errno::ECONNREFUSED, Errno::ENETUNREACH
137
+ # No @socket so we'll try to connect next time
138
+ end
139
+
140
+
141
+ # Records that a user has been active in the app.
142
+ def active(uid)
143
+ push uid, "active"
144
+ end
145
+
146
+ # Records that a new user account has been created (associate them with
147
+ # cohort).
148
+ def created(uid)
149
+ push uid, "created"
150
+ end
151
+
152
+ # Close connection. If you like crossing your t's you can call this when the
153
+ # app shutsdown.
154
+ def close
155
+ @mutex.synchronize do
156
+ socket, @socket = @socket, nil
157
+ socket.close if socket
158
+ end
159
+ rescue Exception
160
+ end
161
+
162
+ # Push update for the specified user ID and event. Or you can use
163
+ # active/created.
164
+ def push(uid, event)
165
+ return unless @api_key
166
+ if socket = @socket
167
+ socket.sendmsg_nonblock "POST /v1/push?api_key=#{@api_key}&uid=#{uid}&event=#{event} HTTP/1.1\r\nHost: #{@host}\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n", 0
168
+ socket.recv_nonblock 512 rescue nil
169
+ else
170
+ Thread.new do
171
+ @mutex.synchronize do
172
+ @socket ||= TCPSocket.open(@host, @port || 80)
173
+ end
174
+ push uid, event
175
+ end
176
+ end
177
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT
178
+ close
179
+ retry
180
+ rescue Errno::ECONNREFUSED, Errno::ENETUNREACH
181
+ # No @socket so we'll try to connect next time
182
+ end
183
+ end
184
+
185
+ # Default host name.
186
+ self.host = "beta.dash-fu.com"
187
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dash-fu
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,26 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-22 00:00:00.000000000Z
13
- dependencies: []
12
+ date: 2011-10-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multi_json
16
+ requirement: &70176105576660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70176105576660
14
25
  description: ''
15
26
  email: assaf@labnotes.org
16
27
  executables: []
17
28
  extensions: []
18
29
  extra_rdoc_files: []
19
30
  files:
20
- - lib/dash-fu.rb
21
- - test/test.rb
31
+ - lib/dash_fu.rb
22
32
  - CHANGELOG
23
33
  - VERSION
24
34
  - README.md
@@ -55,9 +65,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
65
  - - ! '>='
56
66
  - !ruby/object:Gem::Version
57
67
  version: '0'
68
+ segments:
69
+ - 0
70
+ hash: 780141375345856102
58
71
  requirements: []
59
72
  rubyforge_project:
60
- rubygems_version: 1.8.8
73
+ rubygems_version: 1.8.10
61
74
  signing_key:
62
75
  specification_version: 3
63
76
  summary: Use me to push data to Dash-fu in real time
@@ -1,94 +0,0 @@
1
- require "socket"
2
-
3
-
4
- # Use me to push data to Dash-fu in real time.
5
- #
6
- #
7
- # The only configuration you need is setting the API key in
8
- # config/environments/production.rb:
9
- #
10
- # DashFu.api_key = "<your API key>"
11
- #
12
- # You only want to push data in production, so make sure the API key is only set
13
- # in production environment. Calls to created/active with no API key are
14
- # simply ignored.
15
- #
16
- #
17
- # You can send events by calling DashFu.push with UID and event, or using the
18
- # specialized created and active methods.
19
- #
20
- # For example, to assign a user to a cohort, we're going to notify Dash-fu
21
- # whenever an acount gets created:
22
- #
23
- # class User < ActiveRecord::Model
24
- # after_create do
25
- # DashFu.created id
26
- # end
27
- # end
28
- #
29
- # In this example, we consider a user active whenever they post a status update,
30
- # and notify Dash-fu accordingly:
31
- #
32
- # class StatusUpdate < ActiveRecord::Model
33
- # after_create do
34
- # DashFu.active id
35
- # end
36
- # end
37
- module DashFu
38
- @mutex = Mutex.new
39
-
40
- class << self
41
- # DashFu.host is set by default, this is only useful for testing.
42
- attr_accessor :host, :port
43
-
44
- # Set the API key with:
45
- # DashFu.api_key = "<your API key>"
46
- attr_accessor :api_key
47
-
48
- # Records that a user has been active in the app.
49
- def active(uid)
50
- push uid, "active"
51
- end
52
-
53
- # Records that a new user account has been created (associate them with
54
- # cohort).
55
- def created(uid)
56
- push uid, "created"
57
- end
58
-
59
- # Close connection. If you like crossing your t's you can call this when the
60
- # app shutsdown.
61
- def close
62
- @mutex.synchronize do
63
- socket, @socket = @socket, nil
64
- socket.close if socket
65
- end
66
- rescue Exception
67
- end
68
-
69
- # Push update for the specified user ID and event. Or you can use
70
- # active/created.
71
- def push(uid, event)
72
- return unless @api_key
73
- if socket = @socket
74
- socket.sendmsg_nonblock "POST /v1/push?api_key=#{@api_key}&uid=#{uid}&event=#{event} HTTP/1.1\r\nHost: #{@host}\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n", 0
75
- socket.recv_nonblock 512 rescue nil
76
- else
77
- Thread.new do
78
- @mutex.synchronize do
79
- @socket ||= TCPSocket.open(@host, @port || 80)
80
- end
81
- push uid, event
82
- end
83
- end
84
- rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT
85
- close
86
- retry
87
- rescue Errno::ECONNREFUSED, Errno::ENETUNREACH
88
- # No @socket so we'll try to connect next time
89
- end
90
- end
91
-
92
- # Default host name.
93
- self.host = "beta.dash-fu.com"
94
- end
@@ -1,62 +0,0 @@
1
- require "test/unit"
2
- require "thread"
3
- require "cgi"
4
- require "webrick"
5
- $: << File.dirname(__FILE__) + "/../lib"
6
- $: << File.expand_path(File.dirname(__FILE__) + "/..")
7
- require "dash-fu"
8
-
9
-
10
- $ready = Queue.new
11
- $server = WEBrick::HTTPServer.new(:Port=>3001, :StartCallback=>lambda { puts "go"; $ready << "go" }, :Logger=>WEBrick::Log.new(nil, WEBrick::Log::FATAL))
12
- servlet = lambda do |request, response|
13
- $request = request
14
- response.status = 200
15
- end
16
- $server.mount "/v1/push", WEBrick::HTTPServlet::ProcHandler.new(servlet)
17
- trap "INT" do
18
- $server.shutdown
19
- end
20
- DashFu.host = "localhost"
21
- DashFu.port = 3001
22
-
23
-
24
- class Test::Unit::TestCase
25
-
26
- end
27
-
28
-
29
- # Test sending live data.
30
- class LiveTest < Test::Unit::TestCase
31
- def setup
32
- DashFu.api_key = "foobar"
33
- Thread.new { $server.start }
34
- $ready.pop
35
- $request = nil
36
- end
37
-
38
- def test_active_request
39
- DashFu.active "56789"
40
- sleep 0.2
41
- assert_equal "/v1/push", $request.path
42
- assert_equal "POST", $request.request_method
43
- assert $request.keep_alive?
44
- end
45
-
46
- def test_active_data
47
- DashFu.active "56789"
48
- sleep 0.2
49
- query = CGI.parse($request.query_string)
50
- assert_equal ["foobar"], query["api_key"]
51
- assert_equal ["active"], query["event"]
52
- assert_equal ["56789"], query["uid"]
53
- end
54
-
55
- def teardown
56
- $server.shutdown
57
- end
58
- end
59
-
60
- # Test Dashfu API when no API key set (e.g. test and development environments).
61
- class InactiveTest < Test::Unit::TestCase
62
- end