mixpal 0.0.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +24 -0
- data/.travis.yml +7 -0
- data/Dockerfile +6 -0
- data/README.md +51 -11
- data/Rakefile +10 -1
- data/docker-compose.yml +14 -0
- data/lib/mixpal.rb +27 -7
- data/lib/mixpal/event.rb +4 -4
- data/lib/mixpal/integration.rb +12 -8
- data/lib/mixpal/revenue.rb +32 -0
- data/lib/mixpal/tracker.rb +44 -27
- data/lib/mixpal/user.rb +9 -7
- data/lib/mixpal/util.rb +3 -3
- data/lib/mixpal/version.rb +1 -1
- data/mixpanel_assistant.gemspec +8 -7
- data/spec/lib/mixpal/event_spec.rb +20 -18
- data/spec/lib/mixpal/revenue_spec.rb +60 -0
- data/spec/lib/mixpal/tracker_spec.rb +181 -153
- data/spec/lib/mixpal/user_spec.rb +37 -31
- data/spec/lib/mixpal/util_spec.rb +15 -14
- data/spec/lib/mixpal_spec.rb +13 -2
- data/spec/spec_helper.rb +4 -7
- data/spec/support/custom_events_module.rb +5 -0
- data/spec/support/matchers/element_matchers.rb +2 -2
- data/test_app/.gitignore +18 -0
- data/test_app/Gemfile +11 -0
- data/test_app/README.rdoc +28 -0
- data/test_app/Rakefile +6 -0
- data/test_app/app/assets/images/.keep +0 -0
- data/test_app/app/assets/javascripts/application.js +16 -0
- data/test_app/app/assets/stylesheets/application.css +15 -0
- data/test_app/app/controllers/application_controller.rb +12 -0
- data/test_app/app/controllers/concerns/.keep +0 -0
- data/test_app/app/controllers/redirects_controller.rb +10 -0
- data/test_app/app/helpers/application_helper.rb +2 -0
- data/test_app/app/mailers/.keep +0 -0
- data/test_app/app/models/.keep +0 -0
- data/test_app/app/models/concerns/.keep +0 -0
- data/test_app/app/views/layouts/application.html.erb +16 -0
- data/test_app/app/views/redirects/new.html.erb +1 -0
- data/test_app/bin/bundle +3 -0
- data/test_app/bin/rails +8 -0
- data/test_app/bin/rake +8 -0
- data/test_app/bin/spring +18 -0
- data/test_app/config.ru +4 -0
- data/test_app/config/application.rb +30 -0
- data/test_app/config/boot.rb +4 -0
- data/test_app/config/environment.rb +5 -0
- data/test_app/config/environments/development.rb +37 -0
- data/test_app/config/environments/production.rb +82 -0
- data/test_app/config/environments/test.rb +39 -0
- data/test_app/config/initializers/assets.rb +8 -0
- data/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test_app/config/initializers/cookies_serializer.rb +3 -0
- data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/test_app/config/initializers/inflections.rb +16 -0
- data/test_app/config/initializers/mime_types.rb +4 -0
- data/test_app/config/initializers/session_store.rb +3 -0
- data/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/test_app/config/locales/en.yml +23 -0
- data/test_app/config/routes.rb +4 -0
- data/test_app/config/secrets.yml +22 -0
- data/test_app/db/seeds.rb +7 -0
- data/test_app/lib/assets/.keep +0 -0
- data/test_app/lib/tasks/.keep +0 -0
- data/test_app/log/.keep +0 -0
- data/test_app/public/404.html +67 -0
- data/test_app/public/422.html +67 -0
- data/test_app/public/500.html +66 -0
- data/test_app/public/favicon.ico +0 -0
- data/test_app/public/robots.txt +5 -0
- data/test_app/vendor/assets/javascripts/.keep +0 -0
- data/test_app/vendor/assets/stylesheets/.keep +0 -0
- metadata +112 -44
- data/spec/support/mock_rails.rb +0 -6
- data/spec/support/mock_storage.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 70923329c62f76ee4d6b0796547cdd2e367038940c30d8f5ccb10b044f506d36
|
4
|
+
data.tar.gz: 492801097fa1ac2fb85f4600273b2db3fc6e0e8383c9b17112e0302100fc9020
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8dbd5bd3051e8edf9ab7b130c0cd6a5af876d52c8fb6992e82c4ae3b22b58eeb726591bffc67368c9cc3c40e60faa2a1f5d5090a3392fe5fc96f374dde1af6e
|
7
|
+
data.tar.gz: e32c5e8e487ea31dc875df217e26f962c634978cd07b1d036b97a10f3e7eaa0fb8d446b0e8c00d89c4a5eacce591012400d01e3b541bd98238114a4ea1785aaf
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Encoding:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Documentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
ClassAndModuleChildren:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
ClassLength:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
MethodLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
DoubleNegation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
AllCops:
|
20
|
+
NewCops: enable
|
21
|
+
Include:
|
22
|
+
- "Rakefile"
|
23
|
+
Exclude:
|
24
|
+
- "test_app/**/*"
|
data/.travis.yml
ADDED
data/Dockerfile
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Mixpal
|
1
|
+
# Mixpal [![Build Status](https://travis-ci.org/philosophie/mixpal.svg?branch=master)](https://travis-ci.org/philosophie/mixpal)
|
2
2
|
|
3
3
|
As the JavaScript library is Mixpanel's preferred method of usage,
|
4
4
|
Mixpal aims to make it easier to work with from your Rails backend.
|
@@ -78,9 +78,41 @@ mixpanel.update_user email: "mynewemail@example.com"
|
|
78
78
|
|
79
79
|
As with `register_user`, this method will also identify "special properties".
|
80
80
|
|
81
|
+
### Custom Events
|
82
|
+
|
83
|
+
Mixpal allows you to define custom mixpal methods to use in your controllers/views
|
84
|
+
|
85
|
+
1. create a custom module and define your mixpal events
|
86
|
+
```ruby
|
87
|
+
module YourCustomEventsModule
|
88
|
+
def sign_up(user)
|
89
|
+
register_user user.attributes.slice('name', 'email')
|
90
|
+
track 'User signed up'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
2. create a mixpal.rb initializer and configure mixpal to use your module
|
96
|
+
```ruby
|
97
|
+
Mixpal.configure do |config|
|
98
|
+
config.helper_module = YourCustomEventsModule
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
3. use in controllers/views
|
103
|
+
```ruby
|
104
|
+
class UserController < ActionController::Base
|
105
|
+
def create
|
106
|
+
# ... do cool stuff ...
|
107
|
+
mixpal.sign_up(user)
|
108
|
+
redirect_to root_path
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
81
113
|
### Persistance Across Redirects
|
82
114
|
|
83
|
-
Mixpal stores any tracked events or user data in
|
115
|
+
Mixpal stores any tracked events or user data in the session when
|
84
116
|
it detects a redirect so it can output the appropriate Mixpanel JS integration
|
85
117
|
code to the client on the following render. This enables us to do cool things
|
86
118
|
like:
|
@@ -104,16 +136,12 @@ class UsersController < ActionController::Base
|
|
104
136
|
end
|
105
137
|
```
|
106
138
|
|
107
|
-
####
|
108
|
-
|
109
|
-
You can specify a custom persistence storage adapter like so:
|
110
|
-
|
111
|
-
```ruby
|
112
|
-
Mixpal::Tracker.storage = MyCustomAdapter.new
|
113
|
-
```
|
139
|
+
#### A note about `CookieStore` size limit
|
114
140
|
|
115
|
-
|
116
|
-
|
141
|
+
When using Rails' default `ActionDispatch::Session::CookieStore`, a 4K cookie
|
142
|
+
size limit is enforced. This cookie is shared by anything using the session.
|
143
|
+
If you anticipate tracking many events or large data sets to Mixpal,
|
144
|
+
[consider a different session store](http://guides.rubyonrails.org/action_controller_overview.html#session).
|
117
145
|
|
118
146
|
## Contributing
|
119
147
|
|
@@ -124,3 +152,15 @@ Storage adapters must implement the following API: `write(key, value)`,
|
|
124
152
|
1. Commit your changes (`git commit -am 'Add some feature'`)
|
125
153
|
1. Push to the branch (`git push origin feature/my-new-feature`)
|
126
154
|
1. Create new Pull Request
|
155
|
+
|
156
|
+
## Releasing
|
157
|
+
|
158
|
+
Bump `lib/mixpal/version.rb` then build + release with docker-compose. If you
|
159
|
+
prefer local development, inspect the Dockerfile to get your local env built.
|
160
|
+
|
161
|
+
```
|
162
|
+
docker-compose build \
|
163
|
+
--build-arg USER_ID=$(id -u) \
|
164
|
+
--build-arg GROUP_ID=$(id -g)
|
165
|
+
docker-compose run rake release
|
166
|
+
```
|
data/Rakefile
CHANGED
data/docker-compose.yml
ADDED
data/lib/mixpal.rb
CHANGED
@@ -1,10 +1,30 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'mixpal/version'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext'
|
3
4
|
|
4
5
|
module Mixpal
|
5
|
-
autoload :Util,
|
6
|
-
autoload :Tracker,
|
7
|
-
autoload :Event,
|
8
|
-
autoload :User,
|
9
|
-
autoload :
|
6
|
+
autoload :Util, 'mixpal/util'
|
7
|
+
autoload :Tracker, 'mixpal/tracker'
|
8
|
+
autoload :Event, 'mixpal/event'
|
9
|
+
autoload :User, 'mixpal/user'
|
10
|
+
autoload :Revenue, 'mixpal/revenue'
|
11
|
+
autoload :Integration, 'mixpal/integration'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def configuration
|
15
|
+
@configuration ||= Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def configure
|
19
|
+
yield(configuration)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Configuration
|
24
|
+
attr_writer :helper_module
|
25
|
+
|
26
|
+
def helper_module
|
27
|
+
@helper_module ||= Module.new
|
28
|
+
end
|
29
|
+
end
|
10
30
|
end
|
data/lib/mixpal/event.rb
CHANGED
@@ -14,13 +14,13 @@ module Mixpal
|
|
14
14
|
|
15
15
|
def to_store
|
16
16
|
{
|
17
|
-
name
|
18
|
-
properties
|
17
|
+
'name' => name,
|
18
|
+
'properties' => properties
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.from_store(data)
|
23
|
-
new(data[
|
23
|
+
new(data['name'], data['properties'])
|
24
24
|
end
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
data/lib/mixpal/integration.rb
CHANGED
@@ -4,31 +4,35 @@ module Mixpal
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
helper_method :mixpanel
|
7
|
-
|
7
|
+
if Rails::VERSION::MAJOR >= 4
|
8
|
+
after_action :store_mixpanel_if_redirecting
|
9
|
+
else
|
10
|
+
after_filter :store_mixpanel_if_redirecting
|
11
|
+
end
|
8
12
|
|
9
13
|
class_attribute :mixpanel_identity_data
|
10
14
|
def self.mixpanel_identity(object_method, attribute_method)
|
11
15
|
self.mixpanel_identity_data = {
|
12
16
|
object_method: object_method,
|
13
|
-
attribute_method: attribute_method
|
17
|
+
attribute_method: attribute_method
|
14
18
|
}
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
def mixpanel
|
19
23
|
@mixpanel ||= begin
|
20
|
-
identity = if data = self.class.mixpanel_identity_data
|
21
|
-
|
22
|
-
|
24
|
+
identity = if (data = self.class.mixpanel_identity_data)
|
25
|
+
send(data[:object_method]).try(data[:attribute_method])
|
26
|
+
end
|
23
27
|
|
24
|
-
Mixpal::Tracker.new(identity: identity)
|
28
|
+
Mixpal::Tracker.new(identity: identity).tap { |t| t.restore!(session) }
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
32
|
private
|
29
33
|
|
30
34
|
def store_mixpanel_if_redirecting
|
31
|
-
mixpanel.store! if status == 302
|
35
|
+
mixpanel.store!(session) if status == 302
|
32
36
|
end
|
33
37
|
end
|
34
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mixpal
|
2
|
+
class Revenue
|
3
|
+
attr_reader :amount, :properties
|
4
|
+
|
5
|
+
def initialize(amount, properties)
|
6
|
+
@amount = amount
|
7
|
+
@properties = properties
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
args = "#{amount}, #{properties_as_js_object_for_mixpanel}"
|
12
|
+
"mixpanel.people.track_charge(#{args});".html_safe
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_store
|
16
|
+
{
|
17
|
+
'amount' => amount,
|
18
|
+
'properties' => properties
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_store(data)
|
23
|
+
new(data['amount'], data['properties'])
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def properties_as_js_object_for_mixpanel
|
29
|
+
Mixpal::Util.hash_to_js_object_string(properties)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mixpal/tracker.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
module Mixpal
|
2
2
|
class Tracker
|
3
|
-
attr_reader :events, :user_updates, :identity, :alias_user
|
3
|
+
attr_reader :events, :user_updates, :revenue_updates, :identity, :alias_user
|
4
4
|
|
5
|
-
STORAGE_KEY =
|
6
|
-
|
7
|
-
|
5
|
+
STORAGE_KEY = 'mixpal'
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
extend Mixpal.configuration.helper_module
|
8
9
|
|
9
|
-
def initialize(args={})
|
10
10
|
@events = []
|
11
11
|
@user_updates = []
|
12
|
-
|
13
|
-
restore!
|
12
|
+
@revenue_updates = []
|
14
13
|
|
15
14
|
@identity = args[:identity]
|
16
15
|
end
|
@@ -24,44 +23,62 @@ module Mixpal
|
|
24
23
|
user_updates << Mixpal::User.new(properties)
|
25
24
|
end
|
26
25
|
|
27
|
-
def track(name, properties={})
|
26
|
+
def track(name, properties = {})
|
28
27
|
events << Mixpal::Event.new(name, properties)
|
29
28
|
end
|
30
29
|
|
30
|
+
def track_charge(amount, properties = {})
|
31
|
+
revenue_updates << Mixpal::Revenue.new(amount, properties)
|
32
|
+
end
|
33
|
+
|
31
34
|
def render
|
32
|
-
|
33
|
-
html <<
|
35
|
+
''.tap do |html|
|
36
|
+
html << '<script type="text/javascript">'
|
34
37
|
html << "mixpanel.alias(\"#{identity}\");" if alias_user
|
35
38
|
html << "mixpanel.identify(\"#{identity}\");" if identity
|
36
|
-
html << events.map(&:render).join(
|
37
|
-
html << user_updates.map(&:render).join(
|
38
|
-
html <<
|
39
|
+
html << events.map(&:render).join('')
|
40
|
+
html << user_updates.map(&:render).join('')
|
41
|
+
html << revenue_updates.map(&:render).join('')
|
42
|
+
html << '</script>'
|
39
43
|
end.html_safe
|
40
44
|
end
|
41
45
|
|
42
|
-
def store!
|
43
|
-
|
46
|
+
def store!(session)
|
47
|
+
session[STORAGE_KEY] = to_store
|
44
48
|
end
|
45
49
|
|
46
|
-
|
50
|
+
def restore!(session)
|
51
|
+
data = session[STORAGE_KEY] || {}
|
47
52
|
|
48
|
-
|
49
|
-
|
53
|
+
@alias_user = data['alias_user']
|
54
|
+
@identity ||= data['identity']
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@user_updates = data[:user_updates].map { |u| Mixpal::User.from_store(u) } if data[:user_updates]
|
56
|
+
if data['events']
|
57
|
+
@events = data['events'].map { |e| Mixpal::Event.from_store(e) }
|
58
|
+
end
|
55
59
|
|
56
|
-
|
60
|
+
if data['user_updates']
|
61
|
+
@user_updates =
|
62
|
+
data['user_updates'].map { |u| Mixpal::User.from_store(u) }
|
63
|
+
end
|
64
|
+
|
65
|
+
if data['revenue_updates']
|
66
|
+
@revenue_updates =
|
67
|
+
data['revenue_updates'].map { |u| Mixpal::Revenue.from_store(u) }
|
68
|
+
end
|
69
|
+
|
70
|
+
session.delete(STORAGE_KEY)
|
57
71
|
end
|
58
72
|
|
73
|
+
private
|
74
|
+
|
59
75
|
def to_store
|
60
76
|
{
|
61
|
-
alias_user
|
62
|
-
identity
|
63
|
-
events
|
64
|
-
user_updates
|
77
|
+
'alias_user' => alias_user,
|
78
|
+
'identity' => identity,
|
79
|
+
'events' => events.map(&:to_store),
|
80
|
+
'user_updates' => user_updates.map(&:to_store),
|
81
|
+
'revenue_updates' => revenue_updates.map(&:to_store)
|
65
82
|
}
|
66
83
|
end
|
67
84
|
end
|
data/lib/mixpal/user.rb
CHANGED
@@ -12,12 +12,12 @@ module Mixpal
|
|
12
12
|
|
13
13
|
def to_store
|
14
14
|
{
|
15
|
-
properties
|
15
|
+
'properties' => properties
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.from_store(data)
|
20
|
-
new(data[
|
20
|
+
new(data['properties'])
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -29,15 +29,17 @@ module Mixpal
|
|
29
29
|
# Isolate special properties and rename their keys to align with
|
30
30
|
# Mixpanel's naming.
|
31
31
|
def properties_for_mixpanel
|
32
|
-
Hash[
|
32
|
+
Hash[
|
33
|
+
properties.map { |k, v| [mixpanel_special_properties_map[k] || k, v] }
|
34
|
+
]
|
33
35
|
end
|
34
36
|
|
35
37
|
def mixpanel_special_properties_map
|
36
38
|
{
|
37
|
-
name:
|
38
|
-
email:
|
39
|
-
created_at:
|
39
|
+
name: '$name',
|
40
|
+
email: '$email',
|
41
|
+
created_at: '$created'
|
40
42
|
}.with_indifferent_access
|
41
43
|
end
|
42
44
|
end
|
43
|
-
end
|
45
|
+
end
|