acts_as_referred 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +89 -0
  4. data/Rakefile +30 -0
  5. data/lib/acts_as_referred.rb +21 -0
  6. data/lib/acts_as_referred/class_methods.rb +14 -0
  7. data/lib/acts_as_referred/controller.rb +87 -0
  8. data/lib/acts_as_referred/instance_methods.rb +20 -0
  9. data/lib/acts_as_referred/model.rb +146 -0
  10. data/lib/acts_as_referred/version.rb +3 -0
  11. data/lib/tasks/acts_as_referred_tasks.rake +20 -0
  12. data/test/acts_as_referred_test.rb +97 -0
  13. data/test/dummy/README.rdoc +28 -0
  14. data/test/dummy/Rakefile +6 -0
  15. data/test/dummy/app/assets/javascripts/application.js +13 -0
  16. data/test/dummy/app/assets/javascripts/bookings.js +2 -0
  17. data/test/dummy/app/assets/javascripts/orders.js +2 -0
  18. data/test/dummy/app/assets/javascripts/referees.js +2 -0
  19. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  20. data/test/dummy/app/assets/stylesheets/bookings.css +4 -0
  21. data/test/dummy/app/assets/stylesheets/orders.css +4 -0
  22. data/test/dummy/app/assets/stylesheets/referees.css +4 -0
  23. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  24. data/test/dummy/app/controllers/application_controller.rb +8 -0
  25. data/test/dummy/app/controllers/bookings_controller.rb +58 -0
  26. data/test/dummy/app/controllers/orders_controller.rb +58 -0
  27. data/test/dummy/app/helpers/referees_helper.rb +2 -0
  28. data/test/dummy/app/models/booking.rb +3 -0
  29. data/test/dummy/app/models/order.rb +3 -0
  30. data/test/dummy/app/views/bookings/_form.html.erb +25 -0
  31. data/test/dummy/app/views/bookings/edit.html.erb +6 -0
  32. data/test/dummy/app/views/bookings/index.html.erb +29 -0
  33. data/test/dummy/app/views/bookings/new.html.erb +5 -0
  34. data/test/dummy/app/views/bookings/show.html.erb +18 -0
  35. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/test/dummy/app/views/orders/_form.html.erb +25 -0
  37. data/test/dummy/app/views/orders/edit.html.erb +6 -0
  38. data/test/dummy/app/views/orders/index.html.erb +29 -0
  39. data/test/dummy/app/views/orders/new.html.erb +5 -0
  40. data/test/dummy/app/views/orders/show.html.erb +14 -0
  41. data/test/dummy/bin/bundle +3 -0
  42. data/test/dummy/bin/rails +4 -0
  43. data/test/dummy/bin/rake +4 -0
  44. data/test/dummy/config.ru +4 -0
  45. data/test/dummy/config/application.rb +23 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/database.yml +25 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +29 -0
  50. data/test/dummy/config/environments/production.rb +80 -0
  51. data/test/dummy/config/environments/test.rb +36 -0
  52. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +5 -0
  56. data/test/dummy/config/initializers/secret_token.rb +12 -0
  57. data/test/dummy/config/initializers/session_store.rb +3 -0
  58. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  59. data/test/dummy/config/locales/en.yml +23 -0
  60. data/test/dummy/config/routes.rb +59 -0
  61. data/test/dummy/db/development.sqlite3 +0 -0
  62. data/test/dummy/db/migrate/20130926092411_create_bookings.rb +10 -0
  63. data/test/dummy/db/migrate/20131001152526_create_referee.rb +19 -0
  64. data/test/dummy/db/schema.rb +40 -0
  65. data/test/dummy/db/test.sqlite3 +0 -0
  66. data/test/dummy/lib/tasks/acts_as_referred_tasks.rake +20 -0
  67. data/test/dummy/log/development.log +2650 -0
  68. data/test/dummy/log/test.log +7254 -0
  69. data/test/dummy/public/404.html +58 -0
  70. data/test/dummy/public/422.html +58 -0
  71. data/test/dummy/public/500.html +57 -0
  72. data/test/dummy/public/favicon.ico +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/00e0c908a950734c71f87d732990a88e +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/0bd337b765d2a19a6d65ec976c83d97e +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/12274417909f62d494c4179d6f8bcbe3 +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/2fdee9aa34e89b0182a7523c4484e5f6 +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/31da0321b4952412da9e1d9a1118e056 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/36a1fb9df9acb7490c5ac1284b2de56b +0 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/371bf96e99717688ed7313a0c53f4212 +0 -0
  83. data/test/dummy/tmp/cache/assets/development/sprockets/510da110ae528e2d22533be39ff696c5 +0 -0
  84. data/test/dummy/tmp/cache/assets/development/sprockets/5c9d9893cd8a52cf99a46b89383cc603 +0 -0
  85. data/test/dummy/tmp/cache/assets/development/sprockets/6fc757c2c8329244ca95d6909865bbc2 +0 -0
  86. data/test/dummy/tmp/cache/assets/development/sprockets/7fa2dc3bb1d9661e45d7e10e8b2f9190 +0 -0
  87. data/test/dummy/tmp/cache/assets/development/sprockets/a3756bfb305f629747ba5349a5b02be0 +0 -0
  88. data/test/dummy/tmp/cache/assets/development/sprockets/b1e18c11e5c99b4c7871deabd029f38e +0 -0
  89. data/test/dummy/tmp/cache/assets/development/sprockets/b78b6f63e1fe791ab77083cd45eb5105 +0 -0
  90. data/test/dummy/tmp/cache/assets/development/sprockets/bb1f3aab6b0702db7f17992ade69175a +0 -0
  91. data/test/dummy/tmp/cache/assets/development/sprockets/cb9b952d9398fec90275bc0ef98f4b0c +0 -0
  92. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  93. data/test/dummy/tmp/cache/assets/development/sprockets/d1558f1aaf36f9640ae86479f11375df +0 -0
  94. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  95. data/test/dummy/tmp/cache/assets/development/sprockets/e4468469b981ba614a29626880137332 +0 -0
  96. data/test/dummy/tmp/cache/assets/development/sprockets/e66cbc95d30d07acdf168b5c0de354d7 +0 -0
  97. data/test/dummy/tmp/cache/assets/development/sprockets/f464df872844f50aadec7fd7d56783af +0 -0
  98. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  99. data/test/dummy/tmp/cache/assets/development/sprockets/fd365928616248fd8efc56ed88d265ad +0 -0
  100. data/test/test_helper.rb +15 -0
  101. metadata +261 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 42d5ef44865bb5d0ba9ed490390891ef07c74a6d
4
+ data.tar.gz: 9745dd1b1913d9cb7a735c012149a532883063f3
5
+ SHA512:
6
+ metadata.gz: c93d99f61e5aed0cedc4e2706e2f9b7c70607fde113cdc7e08d63e593509cbf5634d11b2c9615667a9a7aabd3af9237b6642b141812117be884586627089fe32
7
+ data.tar.gz: f3d48468214d25ae3ff4fecf8641205daf72079c891746c30f25e2e95346779083e75c676d3a26a0f60df65021dc006861e3d39d31b6a3f280e36242ca9b84ed
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 rene paulokat
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,89 @@
1
+ = ActsAsReferred
2
+
3
+ A gentle gem which offers the possibility to track referrer and campaigning.
4
+ After an ActiveRecord-instance that includes +acts_as_referred+ gets persistet to your database a relational +Referee+ is persistet as well.
5
+
6
+ A +before_action+ included to ApplicationController scans the incoming request for specific data supplied in the request-url.
7
+
8
+ This information is stored in a cookie and in the session as well. The created cookie is valid for 1 month.
9
+
10
+ The action then makes this data available to your model. When supplied with +acts_as_referred+ your pimped model will make use of a +after_create+ action to create a relational +Referee+ after it was created.
11
+
12
+
13
+ == Status
14
+
15
+ not yet production ready
16
+
17
+ == Install
18
+
19
+ at this stage only by github:
20
+ so add to your Gemfile
21
+
22
+ gem 'acts_as_referred', :git => 'git://github.com/erpe/acts_as_referred.git'
23
+
24
+ afterwards run the rake task to create necessary database table
25
+
26
+ rake acts_as_referred:create_table
27
+
28
+ add +acts_as_referred+ to your model e.g.
29
+
30
+ class Order < ActiveRecord::Base
31
+ acts_as_referred
32
+ end
33
+
34
+ and the final step - adopt your ApplicationController with:
35
+
36
+ class ApplicationController < ActionController::Base
37
+ include ActsAsReferred::Controller
38
+ end
39
+
40
+
41
+ +acts_as_referred+ supports manual tagged campaigns
42
+
43
+ If you use google-adwords you have to define manual-url-tagging instead of googles default auto-tagging
44
+
45
+ Auto-tagging (gclid) is recognized as campaign but can not be resolved.
46
+ (could be by parsing e.g. google-analytics cookies - but i think its kind of bad practice to relay on googles cookie-syntax)
47
+
48
+
49
+ == Usage
50
+
51
+ A example Order-model which includes +acts_as_referred+ has a relation to *Referee* on creation
52
+ you can query referees after relational order was created:
53
+
54
+
55
+ [order.referee.is_campaign]
56
+ returns true if request supplied campaign-arguments
57
+
58
+ [order.referee.campaign]
59
+ returns campaign name if supplied by tagging
60
+
61
+ [order.referee.keywords]
62
+ returns supplied keywords in the request.
63
+
64
+ [order.referee.host]
65
+ returns referrer-hostname
66
+
67
+ [order.referee.path]
68
+ returns path part of request
69
+
70
+ [Referee.campaigns]
71
+ returns collection of referee-instances connected to a campaign
72
+
73
+
74
+ == Background
75
+
76
+ whenever a request travels through ApplicationController and initializes a session the delivered referrer is persisted in the session.
77
+ As soon as an ActiveRecord which includes +acts_as_referred+ gets created the stored referrer will be used to build a new *Referee*
78
+
79
+ [A typical request coming in from adwords (non-auto-tagging!) looks like]
80
+
81
+ http://example.com/terror-in-baskets?utm_campaign=NSA&utm_term=PRISM&utm_medium=intercepted&utm_source=fbook
82
+
83
+ [If you prefer - what i'd apreciate - Piwik as privacy-aware tracking]
84
+
85
+ http://example.com/terror-in-baskets?pk_campaign=NSA&pk_kwd=PRISM
86
+
87
+ +ActsAsReferred+ stores the supplied campaign and keywords with in a +Referee+
88
+
89
+
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ Dir.glob('lib/tasks/*.rake').each {|r| import r}
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'ActsAsReferred'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.rdoc')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ Bundler::GemHelper.install_tasks
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |t|
24
+ t.libs << 'lib'
25
+ t.libs << 'test'
26
+ t.pattern = 'test/**/*_test.rb'
27
+ t.verbose = false
28
+ end
29
+
30
+ task default: :test
@@ -0,0 +1,21 @@
1
+ require 'acts_as_referred/instance_methods'
2
+ require 'acts_as_referred/model'
3
+ require 'acts_as_referred/class_methods'
4
+ require 'acts_as_referred/controller'
5
+
6
+ module ActsAsReferred
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ end
11
+
12
+ # provide rake-tasks to create db-migration
13
+ class RakeTasks < Rails::Railtie
14
+ rake_tasks do
15
+ Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ ActiveRecord::Base.send :include, ActsAsReferred
@@ -0,0 +1,14 @@
1
+ module ActsAsReferred
2
+ module ClassMethods
3
+
4
+ # Hook to serve behavior to ActiveRecord-Descendants
5
+ def acts_as_referred(options = {})
6
+
7
+ has_one :referee, as: :referable, dependent: :destroy, class_name: 'Referee'
8
+ after_create :create_referrer
9
+
10
+ include ActsAsReferred::InstanceMethods
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,87 @@
1
+ module ActsAsReferred
2
+
3
+ # Namespce for controller-related functionality
4
+ module Controller
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_filter :_check_cookie_and_session
9
+ before_filter :_supply_model_hook
10
+ end
11
+
12
+ protected
13
+
14
+ # The before_filter which processes necessary data for
15
+ # +acts_as_referred+ - models
16
+ def _supply_model_hook
17
+
18
+ # 3.2.1 -> adwords auto-tagging - (show hint to manual tag adwords):
19
+ # ?gclid=xxxx
20
+ # 3.2.2 -> manual tagging
21
+ # ?utm_source=google&utm_medium=cpc&utm_term=my_keyword&utm_campaign=my_summerdeal
22
+ # 3.2.3 -> manual url-tagging specific for piwik
23
+ # ?pk_campaign=my_summerdeal&pk_kwd=my_keyword
24
+ # cookie / session persisted:
25
+ # e.g.: "req=http://foo/baz?utm_campaign=plonk|ref=http://google.com/search|count=0"
26
+
27
+ tmp = session[:__reqref]
28
+ _struct = nil
29
+ if tmp
30
+ arr = tmp.split('|')
31
+ _struct = OpenStruct.new(
32
+ request_url: arr[0].split('=',2)[1],
33
+ referrer_url: minlength_or_nil(arr[1].split('=',2)[1]),
34
+ visit_count: arr[2].split('=',2)[1].to_i
35
+ )
36
+ end
37
+
38
+ ActiveRecord::Base.send(
39
+ :define_method,
40
+ '_get_reqref',
41
+ proc{ _struct }
42
+ )
43
+ end
44
+
45
+
46
+ private
47
+
48
+ # checks for existing +__reqref+ key in session
49
+ # if not found checks for signed cookie with key +__reqref+
50
+ # if this is the initial request to our site, we write a cookie with
51
+ # referrer and request.url
52
+ def _check_cookie_and_session
53
+ args = {}
54
+ if session[:__reqref]
55
+ tmp = session[:__reqref]
56
+ else
57
+ if cookies.signed[:__reqref]
58
+ tmp = cookies.signed[:__reqref]
59
+ tmp = _increment_returning_count(tmp)
60
+ _set_cookie(__reqref: tmp)
61
+ else
62
+ tmp = "req=#{request.url}|ref=#{request.referrer}|ret=0"
63
+ _set_cookie(__reqref: tmp)
64
+ end
65
+ session[:__reqref] = tmp
66
+ end
67
+ end
68
+
69
+ def _set_cookie(args={})
70
+ args.each_pair do |k,v|
71
+ cookies.signed[k] = { value: v, expires: 1.month.from_now }
72
+ end
73
+ end
74
+
75
+ def _increment_returning_count(tmp)
76
+ arr = tmp.split("|")
77
+ arr[-1] = "ret=#{arr[-1].split('=')[-1].to_i + 1}"
78
+ arr.join('|')
79
+ end
80
+
81
+ def minlength_or_nil(string)
82
+ URI.parse(string).host ? string : nil
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,20 @@
1
+ module ActsAsReferred
2
+ module InstanceMethods
3
+
4
+ private
5
+
6
+ # after create hook to create a corresponding +Referee+
7
+ def create_referrer
8
+ if struct = _get_reqref
9
+
10
+ self.create_referee(
11
+ origin: struct.referrer_url,
12
+ request: struct.request_url,
13
+ visits: struct.visit_count
14
+ )
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+
@@ -0,0 +1,146 @@
1
+ module ActsAsReferred
2
+
3
+ # Namespace for ActsAsReferred-model
4
+ module Model
5
+
6
+ # Represents a Referee -
7
+ # *attributes*
8
+ # [origin] the full URI of referrer
9
+ # [origin_host] host-part of referrer
10
+ # [request] the full request as string
11
+ # [request_query] the query-string - may be nil
12
+ # [campaign] the supplied campaign-name in query
13
+ # [keywords] the supplied keywords in query
14
+ # [is_campaign] if this referee is from campaign
15
+ # [visits] number of visits before conversion
16
+ #
17
+ class ::Referee < ActiveRecord::Base
18
+
19
+ self.table_name = 'referees'
20
+
21
+ belongs_to :referable, polymorphic: true
22
+
23
+ before_create :process_request_and_referrer
24
+
25
+ # all referees which where created throughout a
26
+ # campaign-based request
27
+ scope :campaigns, -> { where('is_campaign=?', true) }
28
+
29
+ # returns referrer as instance of URI
30
+ def origin_uri
31
+ has_referrer? ? URI.parse(origin) : nil
32
+ end
33
+
34
+ # returns host-part of referrer
35
+ # may be nil
36
+ def host
37
+ origin_host
38
+ end
39
+
40
+ # returns path part of request
41
+ # may be nil
42
+ def request_path
43
+ URI.parse(request).path
44
+ end
45
+
46
+ def has_referrer?
47
+ true if origin
48
+ end
49
+
50
+ def has_request?
51
+ true if request
52
+ end
53
+
54
+ def has_query?
55
+ true if request_query
56
+ end
57
+
58
+ private
59
+
60
+ def process_request_and_referrer
61
+ process_origin if origin
62
+
63
+ if request && URI.parse(request).query
64
+ self.request_query = URI.parse(request).query
65
+ if process_request
66
+ self.is_campaign = true
67
+ end
68
+ end
69
+ end
70
+
71
+ def process_request
72
+ if self.request_query
73
+ if self.request_query.match(/utm_campaign/) || self.request_query.match(/utm_term/)
74
+ return process_google_tagged(self.request_query)
75
+ end
76
+ if self.request_query.match(/pk_campaign/) || self.request_query.match(/pk_term/)
77
+ return process_piwik_tagged(self.request_query)
78
+ end
79
+ if self.request_query.match(/gclid/)
80
+ return process_google_auto_tagged(self.request_query)
81
+ end
82
+ end
83
+ end
84
+
85
+
86
+ # a.t.m. only care about campaign name and keywords
87
+ def process_google_tagged(string)
88
+ hash = Hash[* string.split('&').collect { |i| i.split('=') }.flatten]
89
+ retval = nil
90
+ hash.keys.each do |key|
91
+ case key
92
+ #when 'utm_source'
93
+ # source = hash[key]
94
+ when 'utm_campaign'
95
+ self.campaign = hash[key]
96
+ retval = true
97
+ #when 'utm_medium'
98
+ # medium = hash[key]
99
+ when 'utm_term'
100
+ self.keywords = hash[key]
101
+ retval = true
102
+ end
103
+ end
104
+ retval
105
+ end
106
+
107
+ # standard piwik campaign-tracking
108
+ def process_piwik_tagged(string)
109
+ hash = Hash[*(string.split('&').collect { |i| i.split('=') }.flatten)]
110
+ retval = nil
111
+ hash.keys.each do |key|
112
+ case key
113
+ when 'pk_campaign'
114
+ self.campaign = hash[key]
115
+ retval = true
116
+ when 'pk_kwd'
117
+ self.keywords = hash[key]
118
+ retval = true
119
+ end
120
+ end
121
+ retval
122
+ end
123
+
124
+ # adwords set to autotagging
125
+ # no chance to get campaign info by url
126
+ # would have to do cookie parsing - what would suck
127
+ def process_google_auto_tagged(string)
128
+ hash = Hash[* string.split('|').collect { |i| i.split('=') }.flatten]
129
+ retval = nil
130
+ if hash['gclid']
131
+ self.campaign = "Adwords - autotagged: #{hash['gclid']}"
132
+ self.is_campaign = true
133
+ retval = true
134
+ end
135
+ retval
136
+ end
137
+
138
+ def process_origin
139
+ self.origin_host = URI.parse(origin).host
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end