dash-fu 1.0.1 → 1.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.
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