acts_as_referred 0.0.1

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.
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