impressionist2 1.5.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 (146) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +26 -0
  5. data/CHANGELOG.rdoc +82 -0
  6. data/Gemfile +26 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +250 -0
  9. data/Rakefile +34 -0
  10. data/app/controllers/impressionist_controller.rb +146 -0
  11. data/app/models/impression.rb +2 -0
  12. data/app/models/impressionist/bots.rb +1468 -0
  13. data/app/models/impressionist/impressionable.rb +62 -0
  14. data/gemfiles/rails32.gemfile +30 -0
  15. data/gemfiles/rails40.gemfile +30 -0
  16. data/gemfiles/rails50.gemfile +30 -0
  17. data/impressionist2.gemspec +23 -0
  18. data/lib/generators/active_record/impressionist_generator.rb +22 -0
  19. data/lib/generators/active_record/templates/create_impressions_table.rb +32 -0
  20. data/lib/generators/impressionist_generator.rb +13 -0
  21. data/lib/generators/mongo_mapper/impressionist_generator.rb +8 -0
  22. data/lib/generators/mongoid/impressionist_generator.rb +8 -0
  23. data/lib/generators/templates/impression.rb +8 -0
  24. data/lib/impressionist/bots.rb +21 -0
  25. data/lib/impressionist/controllers/mongoid/impressionist_controller.rb +10 -0
  26. data/lib/impressionist/counter_cache.rb +73 -0
  27. data/lib/impressionist/engine.rb +30 -0
  28. data/lib/impressionist/is_impressionable.rb +23 -0
  29. data/lib/impressionist/load.rb +11 -0
  30. data/lib/impressionist/models/active_record/impression.rb +14 -0
  31. data/lib/impressionist/models/active_record/impressionist/impressionable.rb +12 -0
  32. data/lib/impressionist/models/mongo_mapper/impression.rb +18 -0
  33. data/lib/impressionist/models/mongo_mapper/impressionist/impressionable.rb +21 -0
  34. data/lib/impressionist/models/mongoid/impression.rb +25 -0
  35. data/lib/impressionist/models/mongoid/impressionist/impressionable.rb +28 -0
  36. data/lib/impressionist/rails_toggle.rb +26 -0
  37. data/lib/impressionist/setup_association.rb +49 -0
  38. data/lib/impressionist/update_counters.rb +69 -0
  39. data/lib/impressionist/version.rb +3 -0
  40. data/lib/impressionist.rb +12 -0
  41. data/logo.png +0 -0
  42. data/tests/README +11 -0
  43. data/tests/spec/minitest_helper.rb +4 -0
  44. data/tests/spec/rails_toggle_spec.rb +38 -0
  45. data/tests/spec/setup_association_spec.rb +56 -0
  46. data/tests/test_app/.gitignore +18 -0
  47. data/tests/test_app/.rspec +1 -0
  48. data/tests/test_app/Gemfile +51 -0
  49. data/tests/test_app/README +256 -0
  50. data/tests/test_app/README.rdoc +261 -0
  51. data/tests/test_app/Rakefile +7 -0
  52. data/tests/test_app/app/assets/images/rails.png +0 -0
  53. data/tests/test_app/app/assets/javascripts/application.js +13 -0
  54. data/tests/test_app/app/assets/stylesheets/application.css +13 -0
  55. data/tests/test_app/app/controllers/application_controller.rb +8 -0
  56. data/tests/test_app/app/controllers/articles_controller.rb +18 -0
  57. data/tests/test_app/app/controllers/dummy_controller.rb +8 -0
  58. data/tests/test_app/app/controllers/posts_controller.rb +23 -0
  59. data/tests/test_app/app/controllers/profiles_controller.rb +14 -0
  60. data/tests/test_app/app/controllers/widgets_controller.rb +12 -0
  61. data/tests/test_app/app/helpers/application_helper.rb +2 -0
  62. data/tests/test_app/app/mailers/.gitkeep +0 -0
  63. data/tests/test_app/app/models/.gitkeep +0 -0
  64. data/tests/test_app/app/models/article.rb +3 -0
  65. data/tests/test_app/app/models/dummy.rb +7 -0
  66. data/tests/test_app/app/models/post.rb +3 -0
  67. data/tests/test_app/app/models/profile.rb +6 -0
  68. data/tests/test_app/app/models/user.rb +3 -0
  69. data/tests/test_app/app/models/widget.rb +3 -0
  70. data/tests/test_app/app/views/articles/index.html.erb +1 -0
  71. data/tests/test_app/app/views/articles/show.html.erb +1 -0
  72. data/tests/test_app/app/views/dummy/index.html.erb +0 -0
  73. data/tests/test_app/app/views/layouts/application.html.erb +14 -0
  74. data/tests/test_app/app/views/posts/edit.html.erb +0 -0
  75. data/tests/test_app/app/views/posts/index.html.erb +0 -0
  76. data/tests/test_app/app/views/posts/show.html.erb +0 -0
  77. data/tests/test_app/app/views/profiles/show.html.erb +3 -0
  78. data/tests/test_app/app/views/widgets/index.html.erb +0 -0
  79. data/tests/test_app/app/views/widgets/new.html.erb +0 -0
  80. data/tests/test_app/app/views/widgets/show.html.erb +0 -0
  81. data/tests/test_app/config/application.rb +59 -0
  82. data/tests/test_app/config/boot.rb +6 -0
  83. data/tests/test_app/config/cucumber.yml +8 -0
  84. data/tests/test_app/config/database.yml +30 -0
  85. data/tests/test_app/config/environment.rb +5 -0
  86. data/tests/test_app/config/environments/development.rb +37 -0
  87. data/tests/test_app/config/environments/pg_test.rb +35 -0
  88. data/tests/test_app/config/environments/production.rb +67 -0
  89. data/tests/test_app/config/environments/test.rb +37 -0
  90. data/tests/test_app/config/initializers/backtrace_silencers.rb +7 -0
  91. data/tests/test_app/config/initializers/impression.rb +8 -0
  92. data/tests/test_app/config/initializers/inflections.rb +15 -0
  93. data/tests/test_app/config/initializers/mime_types.rb +5 -0
  94. data/tests/test_app/config/initializers/secret_token.rb +7 -0
  95. data/tests/test_app/config/initializers/session_store.rb +8 -0
  96. data/tests/test_app/config/initializers/wrap_parameters.rb +14 -0
  97. data/tests/test_app/config/locales/en.yml +5 -0
  98. data/tests/test_app/config/routes.rb +4 -0
  99. data/tests/test_app/config.ru +4 -0
  100. data/tests/test_app/db/migrate/20110201153144_create_articles.rb +13 -0
  101. data/tests/test_app/db/migrate/20110210205028_create_posts.rb +13 -0
  102. data/tests/test_app/db/migrate/20111127184039_create_widgets.rb +15 -0
  103. data/tests/test_app/db/migrate/20130719024021_create_impressions_table.rb +32 -0
  104. data/tests/test_app/db/migrate/20150207135825_create_profiles.rb +10 -0
  105. data/tests/test_app/db/migrate/20150207140310_create_friendly_id_slugs.rb +18 -0
  106. data/tests/test_app/db/schema.rb +80 -0
  107. data/tests/test_app/db/seeds.rb +7 -0
  108. data/tests/test_app/lib/assets/.gitkeep +0 -0
  109. data/tests/test_app/lib/tasks/.gitkeep +0 -0
  110. data/tests/test_app/lib/tasks/cucumber.rake +53 -0
  111. data/tests/test_app/log/.gitkeep +0 -0
  112. data/tests/test_app/public/404.html +26 -0
  113. data/tests/test_app/public/422.html +26 -0
  114. data/tests/test_app/public/500.html +25 -0
  115. data/tests/test_app/public/favicon.ico +0 -0
  116. data/tests/test_app/public/images/rails.png +0 -0
  117. data/tests/test_app/public/index.html +241 -0
  118. data/tests/test_app/public/javascripts/application.js +2 -0
  119. data/tests/test_app/public/javascripts/controls.js +965 -0
  120. data/tests/test_app/public/javascripts/dragdrop.js +974 -0
  121. data/tests/test_app/public/javascripts/effects.js +1123 -0
  122. data/tests/test_app/public/javascripts/prototype.js +6001 -0
  123. data/tests/test_app/public/javascripts/rails.js +175 -0
  124. data/tests/test_app/public/robots.txt +5 -0
  125. data/tests/test_app/public/stylesheets/.gitkeep +0 -0
  126. data/tests/test_app/script/cucumber +10 -0
  127. data/tests/test_app/script/rails +6 -0
  128. data/tests/test_app/spec/controllers/articles_controller_spec.rb +76 -0
  129. data/tests/test_app/spec/controllers/dummy_controller_spec.rb +11 -0
  130. data/tests/test_app/spec/controllers/impressionist_uniqueness_spec.rb +396 -0
  131. data/tests/test_app/spec/controllers/posts_controller_spec.rb +28 -0
  132. data/tests/test_app/spec/controllers/widgets_controller_spec.rb +91 -0
  133. data/tests/test_app/spec/fixtures/articles.yml +3 -0
  134. data/tests/test_app/spec/fixtures/impressions.yml +43 -0
  135. data/tests/test_app/spec/fixtures/posts.yml +3 -0
  136. data/tests/test_app/spec/fixtures/profiles.yml +4 -0
  137. data/tests/test_app/spec/fixtures/widgets.yml +4 -0
  138. data/tests/test_app/spec/initializers/initializers_spec.rb +20 -0
  139. data/tests/test_app/spec/models/bots_spec.rb +27 -0
  140. data/tests/test_app/spec/models/counter_caching_spec.rb +51 -0
  141. data/tests/test_app/spec/models/model_spec.rb +70 -0
  142. data/tests/test_app/spec/rails_generators/rails_generators_spec.rb +23 -0
  143. data/tests/test_app/spec/spec_helper.rb +43 -0
  144. data/upgrade_migrations/version_0_3_0.rb +27 -0
  145. data/upgrade_migrations/version_0_4_0.rb +9 -0
  146. metadata +314 -0
@@ -0,0 +1,175 @@
1
+ (function() {
2
+ // Technique from Juriy Zaytsev
3
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
+ function isEventSupported(eventName) {
5
+ var el = document.createElement('div');
6
+ eventName = 'on' + eventName;
7
+ var isSupported = (eventName in el);
8
+ if (!isSupported) {
9
+ el.setAttribute(eventName, 'return;');
10
+ isSupported = typeof el[eventName] == 'function';
11
+ }
12
+ el = null;
13
+ return isSupported;
14
+ }
15
+
16
+ function isForm(element) {
17
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
+ }
19
+
20
+ function isInput(element) {
21
+ if (Object.isElement(element)) {
22
+ var name = element.nodeName.toUpperCase()
23
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
+ }
25
+ else return false
26
+ }
27
+
28
+ var submitBubbles = isEventSupported('submit'),
29
+ changeBubbles = isEventSupported('change')
30
+
31
+ if (!submitBubbles || !changeBubbles) {
32
+ // augment the Event.Handler class to observe custom events when needed
33
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
+ function(init, element, eventName, selector, callback) {
35
+ init(element, eventName, selector, callback)
36
+ // is the handler being attached to an element that doesn't support this event?
37
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
+ // "submit" => "emulated:submit"
40
+ this.eventName = 'emulated:' + this.eventName
41
+ }
42
+ }
43
+ )
44
+ }
45
+
46
+ if (!submitBubbles) {
47
+ // discover forms on the page by observing focus events which always bubble
48
+ document.on('focusin', 'form', function(focusEvent, form) {
49
+ // special handler for the real "submit" event (one-time operation)
50
+ if (!form.retrieve('emulated:submit')) {
51
+ form.on('submit', function(submitEvent) {
52
+ var emulated = form.fire('emulated:submit', submitEvent, true)
53
+ // if custom event received preventDefault, cancel the real one too
54
+ if (emulated.returnValue === false) submitEvent.preventDefault()
55
+ })
56
+ form.store('emulated:submit', true)
57
+ }
58
+ })
59
+ }
60
+
61
+ if (!changeBubbles) {
62
+ // discover form inputs on the page
63
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
+ // special handler for real "change" events
65
+ if (!input.retrieve('emulated:change')) {
66
+ input.on('change', function(changeEvent) {
67
+ input.fire('emulated:change', changeEvent, true)
68
+ })
69
+ input.store('emulated:change', true)
70
+ }
71
+ })
72
+ }
73
+
74
+ function handleRemote(element) {
75
+ var method, url, params;
76
+
77
+ var event = element.fire("ajax:before");
78
+ if (event.stopped) return false;
79
+
80
+ if (element.tagName.toLowerCase() === 'form') {
81
+ method = element.readAttribute('method') || 'post';
82
+ url = element.readAttribute('action');
83
+ params = element.serialize();
84
+ } else {
85
+ method = element.readAttribute('data-method') || 'get';
86
+ url = element.readAttribute('href');
87
+ params = {};
88
+ }
89
+
90
+ new Ajax.Request(url, {
91
+ method: method,
92
+ parameters: params,
93
+ evalScripts: true,
94
+
95
+ onComplete: function(request) { element.fire("ajax:complete", request); },
96
+ onSuccess: function(request) { element.fire("ajax:success", request); },
97
+ onFailure: function(request) { element.fire("ajax:failure", request); }
98
+ });
99
+
100
+ element.fire("ajax:after");
101
+ }
102
+
103
+ function handleMethod(element) {
104
+ var method = element.readAttribute('data-method'),
105
+ url = element.readAttribute('href'),
106
+ csrf_param = $$('meta[name=csrf-param]')[0],
107
+ csrf_token = $$('meta[name=csrf-token]')[0];
108
+
109
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
+ element.parentNode.insert(form);
111
+
112
+ if (method !== 'post') {
113
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
+ form.insert(field);
115
+ }
116
+
117
+ if (csrf_param) {
118
+ var param = csrf_param.readAttribute('content'),
119
+ token = csrf_token.readAttribute('content'),
120
+ field = new Element('input', { type: 'hidden', name: param, value: token });
121
+ form.insert(field);
122
+ }
123
+
124
+ form.submit();
125
+ }
126
+
127
+
128
+ document.on("click", "*[data-confirm]", function(event, element) {
129
+ var message = element.readAttribute('data-confirm');
130
+ if (!confirm(message)) event.stop();
131
+ });
132
+
133
+ document.on("click", "a[data-remote]", function(event, element) {
134
+ if (event.stopped) return;
135
+ handleRemote(element);
136
+ event.stop();
137
+ });
138
+
139
+ document.on("click", "a[data-method]", function(event, element) {
140
+ if (event.stopped) return;
141
+ handleMethod(element);
142
+ event.stop();
143
+ });
144
+
145
+ document.on("submit", function(event) {
146
+ var element = event.findElement(),
147
+ message = element.readAttribute('data-confirm');
148
+ if (message && !confirm(message)) {
149
+ event.stop();
150
+ return false;
151
+ }
152
+
153
+ var inputs = element.select("input[type=submit][data-disable-with]");
154
+ inputs.each(function(input) {
155
+ input.disabled = true;
156
+ input.writeAttribute('data-original-value', input.value);
157
+ input.value = input.readAttribute('data-disable-with');
158
+ });
159
+
160
+ var element = event.findElement("form[data-remote]");
161
+ if (element) {
162
+ handleRemote(element);
163
+ event.stop();
164
+ }
165
+ });
166
+
167
+ document.on("ajax:after", "form", function(event, element) {
168
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
+ inputs.each(function(input) {
170
+ input.value = input.readAttribute('data-original-value');
171
+ input.removeAttribute('data-original-value');
172
+ input.disabled = false;
173
+ });
174
+ });
175
+ })();
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-Agent: *
5
+ # Disallow: /
File without changes
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
4
+ if vendored_cucumber_bin
5
+ load File.expand_path(vendored_cucumber_bin)
6
+ else
7
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
8
+ require 'cucumber'
9
+ load Cucumber::BINARY
10
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArticlesController do
4
+ fixtures :articles,:impressions,:posts,:widgets
5
+
6
+ render_views
7
+
8
+ it "should make the impressionable_hash available" do
9
+ get "index"
10
+ response.body.include?("false").should eq true
11
+ end
12
+
13
+ it "should log an impression with a message" do
14
+ get "index"
15
+ Impression.all.size.should eq 12
16
+ Article.first.impressions.last.message.should eq "this is a test article impression"
17
+ Article.first.impressions.last.controller_name.should eq "articles"
18
+ Article.first.impressions.last.action_name.should eq "index"
19
+ end
20
+
21
+ it "should log an impression without a message" do
22
+ get "show", :id=> 1
23
+ Impression.all.size.should eq 12
24
+ Article.first.impressions.last.message.should eq nil
25
+ Article.first.impressions.last.controller_name.should eq "articles"
26
+ Article.first.impressions.last.action_name.should eq "show"
27
+ end
28
+
29
+ it "should log the user_id if user is authenticated (@current_user before_filter method)" do
30
+ session[:user_id] = 123
31
+ get "show", :id=> 1
32
+ Article.first.impressions.last.user_id.should eq 123
33
+ end
34
+
35
+ it "should not log the user_id if user is authenticated" do
36
+ get "show", :id=> 1
37
+ Article.first.impressions.last.user_id.should eq nil
38
+ end
39
+
40
+ it "should log the request_hash, ip_address, referrer and session_hash" do
41
+ get "show", :id=> 1
42
+ Impression.last.request_hash.size.should eq 64
43
+ Impression.last.ip_address.should eq "0.0.0.0"
44
+ Impression.last.session_hash.size.should eq 32
45
+ Impression.last.referrer.should eq nil
46
+ end
47
+
48
+ # Capybara has change the way it works
49
+ # We need to pass :type options in order to make include helper methods
50
+ # see https://github.com/jnicklas/capybara#using-capybara-with-rspec
51
+ it "should log the referrer when you click a link", :type => :feature do
52
+ visit article_url(Article.first)
53
+ click_link "Same Page"
54
+ Impression.last.referrer.should eq "http://test.host/articles/1"
55
+ end
56
+
57
+ it "should log request with params (checked = true)" do
58
+ get "show", id: 1, checked: true
59
+ Impression.last.params.should eq({"checked"=>true})
60
+ Impression.last.request_hash.size.should eq 64
61
+ Impression.last.ip_address.should eq "0.0.0.0"
62
+ Impression.last.session_hash.size.should eq 32
63
+ Impression.last.referrer.should eq nil
64
+ end
65
+
66
+ it "should log request with params {}" do
67
+ get "index"
68
+ Impression.last.params.should eq({})
69
+ Impression.last.request_hash.size.should eq 64
70
+ Impression.last.ip_address.should eq "0.0.0.0"
71
+ Impression.last.session_hash.size.should eq 32
72
+ Impression.last.referrer.should eq nil
73
+ end
74
+ end
75
+
76
+
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe DummyController do
4
+ fixtures :impressions
5
+ render_views
6
+
7
+ it "should log impression at the per action level on non-restful controller" do
8
+ get "index"
9
+ Impression.all.size.should eq 12
10
+ end
11
+ end
@@ -0,0 +1,396 @@
1
+ require 'spec_helper'
2
+
3
+ # we use the posts controller as it uses the impressionsist module. any such controller would do.
4
+ describe DummyController do
5
+
6
+ before do
7
+ @impression_count = Impression.all.size
8
+ end
9
+
10
+ describe "impressionist filter uniqueness" do
11
+
12
+ it "should ignore uniqueness if not requested" do
13
+ controller.impressionist_subapp_filter
14
+ controller.impressionist_subapp_filter
15
+ Impression.should have(@impression_count + 2).records
16
+ end
17
+
18
+ it "should recognize unique session" do
19
+ controller.stub(:session_hash).and_return(request.session_options[:id])
20
+ controller.impressionist_subapp_filter(unique: [:session_hash])
21
+ controller.impressionist_subapp_filter(unique: [:session_hash])
22
+ Impression.should have(@impression_count + 1).records
23
+ end
24
+
25
+ it "should recognize unique ip" do
26
+ controller.request.stub(:remote_ip).and_return("1.2.3.4")
27
+ controller.impressionist_subapp_filter(unique: [:ip_address])
28
+ controller.impressionist_subapp_filter(unique: [:ip_address])
29
+ Impression.should have(@impression_count + 1).records
30
+ end
31
+
32
+ it "should recognize unique request" do
33
+ controller.impressionist_subapp_filter(unique: [:request_hash])
34
+ controller.impressionist_subapp_filter(unique: [:request_hash])
35
+ Impression.should have(@impression_count + 1).records
36
+ end
37
+
38
+ it "should recognize unique action" do
39
+ controller.stub(:action_name).and_return("test_action")
40
+ controller.impressionist_subapp_filter(unique: [:action_name])
41
+ controller.impressionist_subapp_filter(unique: [:action_name])
42
+ Impression.should have(@impression_count + 1).records
43
+ end
44
+
45
+ it "should recognize unique controller" do
46
+ controller.stub(:controller_name).and_return("post")
47
+ controller.impressionist_subapp_filter(unique: [:controller_name])
48
+ controller.impressionist_subapp_filter(unique: [:controller_name])
49
+ Impression.should have(@impression_count + 1).records
50
+ end
51
+
52
+ it "should recognize unique user" do
53
+ controller.stub(:user_id).and_return(42)
54
+ controller.impressionist_subapp_filter(unique: [:user_id])
55
+ controller.impressionist_subapp_filter(unique: [:user_id])
56
+ Impression.should have(@impression_count + 1).records
57
+ end
58
+
59
+ it "should recognize unique referer" do
60
+ controller.request.stub(:referer).and_return("http://foo/bar")
61
+ controller.impressionist_subapp_filter(unique: [:referrer])
62
+ controller.impressionist_subapp_filter(unique: [:referrer])
63
+ Impression.should have(@impression_count + 1).records
64
+ end
65
+
66
+ it "should recognize unique id" do
67
+ controller.stub(:params).and_return({:id => "666"}) # for correct impressionable id in filter
68
+ controller.impressionist_subapp_filter(unique: [:impressionable_id])
69
+ controller.impressionist_subapp_filter(unique: [:impressionable_id])
70
+ Impression.should have(@impression_count + 1).records
71
+ end
72
+
73
+ # extra redundant test for important controller and action combination.
74
+ it "should recognize different controller and action" do
75
+ controller.stub(:controller_name).and_return("post")
76
+ controller.stub(:action_name).and_return("test_action")
77
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
78
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
79
+ Impression.should have(@impression_count + 1).records
80
+ controller.stub(:action_name).and_return("another_action")
81
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
82
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
83
+ Impression.should have(@impression_count + 2).records
84
+ controller.stub(:controller_name).and_return("article")
85
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
86
+ controller.impressionist_subapp_filter(unique: [:controller_name, :action_name])
87
+ Impression.should have(@impression_count + 3).records
88
+ end
89
+
90
+ it "should recognize different action" do
91
+ controller.stub(:action_name).and_return("test_action")
92
+ controller.impressionist_subapp_filter(unique: [:action_name])
93
+ controller.impressionist_subapp_filter(unique: [:action_name])
94
+ Impression.should have(@impression_count + 1).records
95
+ controller.stub(:action_name).and_return("another_action")
96
+ controller.impressionist_subapp_filter(unique: [:action_name])
97
+ controller.impressionist_subapp_filter(unique: [:action_name])
98
+ Impression.should have(@impression_count + 2).records
99
+ end
100
+
101
+ it "should recognize different controller" do
102
+ controller.stub(:controller_name).and_return("post")
103
+ controller.impressionist_subapp_filter(unique: [:controller_name])
104
+ controller.impressionist_subapp_filter(unique: [:controller_name])
105
+ Impression.should have(@impression_count + 1).records
106
+ controller.stub(:controller_name).and_return("article")
107
+ controller.impressionist_subapp_filter(unique: [:controller_name])
108
+ controller.impressionist_subapp_filter(unique: [:controller_name])
109
+ Impression.should have(@impression_count + 2).records
110
+ end
111
+
112
+ it "should recognize different session" do
113
+ controller.stub(:session_hash).and_return("foo")
114
+ controller.impressionist_subapp_filter(unique: [:session_hash])
115
+ controller.impressionist_subapp_filter(unique: [:session_hash])
116
+ Impression.should have(@impression_count + 1).records
117
+ controller.stub(:session_hash).and_return("bar")
118
+ controller.impressionist_subapp_filter(unique: [:session_hash])
119
+ controller.impressionist_subapp_filter(unique: [:session_hash])
120
+ Impression.should have(@impression_count + 2).records
121
+ end
122
+
123
+ it "should recognize different ip" do
124
+ controller.request.stub(:remote_ip).and_return("1.2.3.4")
125
+ controller.impressionist_subapp_filter(unique: [:ip_address])
126
+ controller.impressionist_subapp_filter(unique: [:ip_address])
127
+ Impression.should have(@impression_count + 1).records
128
+ controller.request.stub(:remote_ip).and_return("5.6.7.8")
129
+ controller.impressionist_subapp_filter(unique: [:ip_address])
130
+ controller.impressionist_subapp_filter(unique: [:ip_address])
131
+ Impression.should have(@impression_count + 2).records
132
+ end
133
+
134
+ it "should recognize different referer" do
135
+ controller.request.stub(:referer).and_return("http://foo/bar")
136
+ controller.impressionist_subapp_filter(unique: [:referrer])
137
+ controller.impressionist_subapp_filter(unique: [:referrer])
138
+ Impression.should have(@impression_count + 1).records
139
+ controller.request.stub(:referer).and_return("http://bar/fo")
140
+ controller.impressionist_subapp_filter(unique: [:referrer])
141
+ controller.impressionist_subapp_filter(unique: [:referrer])
142
+ Impression.should have(@impression_count + 2).records
143
+ end
144
+
145
+ it "should recognize different id" do
146
+ controller.stub(:params).and_return({:id => "666"}) # for correct impressionable id in filter
147
+ controller.impressionist_subapp_filter(unique: [:impressionable_type, :impressionable_id])
148
+ controller.impressionist_subapp_filter(unique: [:impressionable_type, :impressionable_id])
149
+ controller.stub(:params).and_return({:id => "42"}) # for correct impressionable id in filter
150
+ controller.impressionist_subapp_filter(unique: [:impressionable_type, :impressionable_id])
151
+ controller.impressionist_subapp_filter(unique: [:impressionable_type, :impressionable_id])
152
+ Impression.should have(@impression_count + 2).records
153
+ end
154
+
155
+ it "should recognize combined uniqueness" do
156
+ controller.stub(:action_name).and_return("test_action")
157
+ controller.impressionist_subapp_filter(unique: [:ip_address, :request_hash, :action_name])
158
+ controller.impressionist_subapp_filter(unique: [:request_hash, :ip_address, :action_name])
159
+ controller.impressionist_subapp_filter(unique: [:request_hash, :action_name])
160
+ controller.impressionist_subapp_filter(unique: [:ip_address, :action_name])
161
+ controller.impressionist_subapp_filter(unique: [:ip_address, :request_hash])
162
+ controller.impressionist_subapp_filter(unique: [:action_name])
163
+ controller.impressionist_subapp_filter(unique: [:ip_address])
164
+ controller.impressionist_subapp_filter(unique: [:request_hash])
165
+ Impression.should have(@impression_count + 1).records
166
+ end
167
+
168
+ it "should recognize combined non-uniqueness" do
169
+ controller.stub(:action_name).and_return(nil)
170
+ controller.impressionist_subapp_filter(unique: [:ip_address, :action_name])
171
+ controller.stub(:action_name).and_return("test_action")
172
+ controller.impressionist_subapp_filter(unique: [:ip_address, :action_name])
173
+ controller.stub(:action_name).and_return("another_action")
174
+ controller.impressionist_subapp_filter(unique: [:ip_address, :action_name])
175
+ Impression.should have(@impression_count + 3).records
176
+ end
177
+
178
+ end
179
+
180
+ describe "impressionist method uniqueness for impressionables" do
181
+
182
+ # in this test we reuse the post model. might break if model changes.
183
+
184
+ it "should ignore uniqueness if not requested" do
185
+ impressionable = Post.create
186
+ controller.impressionist impressionable
187
+ controller.impressionist impressionable
188
+ Impression.should have(@impression_count + 2).records
189
+ end
190
+
191
+ it "should recognize unique session" do
192
+ controller.stub(:session_hash).and_return(request.session_options[:id])
193
+ impressionable = Post.create
194
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
195
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
196
+ Impression.should have(@impression_count + 1).records
197
+ end
198
+
199
+ it "should recognize unique ip" do
200
+ controller.request.stub(:remote_ip).and_return("1.2.3.4")
201
+ impressionable = Post.create
202
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
203
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
204
+ Impression.should have(@impression_count + 1).records
205
+ end
206
+
207
+ it "should recognize unique request" do
208
+ impressionable = Post.create
209
+ controller.impressionist(impressionable, nil, :unique => [:request_hash])
210
+ controller.impressionist(impressionable, nil, :unique => [:request_hash])
211
+ Impression.should have(@impression_count + 1).records
212
+ end
213
+
214
+ it "should recognize unique user" do
215
+ controller.stub(:user_id).and_return(666)
216
+ impressionable = Post.create
217
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
218
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
219
+ Impression.should have(@impression_count + 1).records
220
+ end
221
+
222
+ it "should recognize unique referer" do
223
+ controller.request.stub(:referer).and_return("http://foo/bar")
224
+ impressionable = Post.create
225
+ controller.impressionist(impressionable, nil, :unique => [:referrer])
226
+ controller.impressionist(impressionable, nil, :unique => [:referrer])
227
+ Impression.should have(@impression_count + 1).records
228
+ end
229
+
230
+ it "should recognize different session" do
231
+ impressionable = Post.create
232
+ controller.stub(:session_hash).and_return("foo")
233
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
234
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
235
+ Impression.should have(@impression_count + 1).records
236
+ controller.stub(:session_hash).and_return("bar")
237
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
238
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
239
+ Impression.should have(@impression_count + 2).records
240
+ end
241
+
242
+ it "should recognize different ip" do
243
+ controller.request.stub(:remote_ip).and_return("1.2.3.4")
244
+ impressionable = Post.create
245
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
246
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
247
+ Impression.should have(@impression_count + 1).records
248
+ controller.request.stub(:remote_ip).and_return("5.6.7.8")
249
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
250
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
251
+ Impression.should have(@impression_count + 2).records
252
+ end
253
+
254
+ it "should recognize different user" do
255
+ impressionable = Post.create
256
+ controller.stub(:user_id).and_return(666)
257
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
258
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
259
+ Impression.should have(@impression_count + 1).records
260
+ controller.stub(:user_id).and_return(42)
261
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
262
+ controller.impressionist(impressionable, nil, :unique => [:user_id])
263
+ Impression.should have(@impression_count + 2).records
264
+ end
265
+
266
+ it "should recognize combined uniqueness" do
267
+ impressionable = Post.create
268
+ controller.stub(:session_hash).and_return("foo")
269
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash])
270
+ controller.impressionist(impressionable, nil, :unique => [:request_hash, :ip_address, :session_hash])
271
+ controller.impressionist(impressionable, nil, :unique => [:request_hash, :session_hash])
272
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
273
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash])
274
+ controller.impressionist(impressionable, nil, :unique => [:session_hash])
275
+ controller.impressionist(impressionable, nil, :unique => [:ip_address])
276
+ controller.impressionist(impressionable, nil, :unique => [:request_hash])
277
+ Impression.should have(@impression_count + 1).records
278
+ end
279
+
280
+ it "should recognize combined non-uniqueness" do
281
+ impressionable = Post.create
282
+ controller.stub(:session_hash).and_return(nil)
283
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
284
+ controller.stub(:session_hash).and_return("foo")
285
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
286
+ controller.stub(:session_hash).and_return("bar")
287
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
288
+ Impression.should have(@impression_count + 3).records
289
+ end
290
+
291
+ end
292
+
293
+ describe "impressionist filter and method uniqueness" do
294
+
295
+ it "should recognize uniqueness" do
296
+ impressionable = Post.create
297
+ controller.stub(:controller_name).and_return("posts") # for correct impressionable type in filter
298
+ controller.stub(:params).and_return({:id => impressionable.id.to_s}) # for correct impressionable id in filter
299
+ controller.stub(:session_hash).and_return("foo")
300
+ controller.request.stub(:remote_ip).and_return("1.2.3.4")
301
+ # order of the following methods is important for the test!
302
+ controller.impressionist_subapp_filter(unique: [:ip_address, :request_hash, :session_hash])
303
+ controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash])
304
+ Impression.should have(@impression_count + 1).records
305
+ end
306
+
307
+ end
308
+
309
+ describe 'impressionist with friendly id' do
310
+ it 'should unique' do
311
+ impressionable = Profile.create({username: 'test_profile', slug: 'test_profile'})
312
+
313
+ controller.stub(:controller_name).and_return('profile')
314
+ controller.stub(:action_name).and_return('show')
315
+ controller.stub(:params).and_return({id: impressionable.slug})
316
+ controller.request.stub(:remote_ip).and_return('1.2.3.4')
317
+
318
+ controller.impressionist(impressionable, nil, :unique => [:impressionable_type, :impressionable_id])
319
+ controller.impressionist(impressionable, nil, :unique => [:impressionable_type, :impressionable_id])
320
+ Impression.should have(@impression_count + 1).records
321
+ end
322
+ end
323
+
324
+ shared_examples_for 'an impressionable action' do
325
+ it 'should record an impression' do
326
+ controller.impressionist_subapp_filter(condition)
327
+ Impression.should have(@impression_count + 1).records
328
+ end
329
+ end
330
+
331
+ shared_examples_for 'an unimpressionable action' do
332
+ it 'should record an impression' do
333
+ controller.impressionist_subapp_filter(condition)
334
+ Impression.should have(@impression_count).records
335
+ end
336
+ end
337
+
338
+ describe "conditional impressions" do
339
+ describe ":if condition" do
340
+ context "true condition" do
341
+ before do
342
+ controller.stub(:true_condition).and_return(true)
343
+ end
344
+ it_behaves_like 'an impressionable action' do
345
+ let(:condition) {{ if: :true_condition }}
346
+ end
347
+
348
+ it_behaves_like 'an impressionable action' do
349
+ let(:condition) {{ if: lambda { true } }}
350
+ end
351
+ end
352
+
353
+ context "false condition" do
354
+ before do
355
+ controller.stub(:false_condition).and_return(false)
356
+ end
357
+ it_behaves_like 'an unimpressionable action' do
358
+ let(:condition) {{ if: :false_condition }}
359
+ end
360
+
361
+ it_behaves_like 'an unimpressionable action' do
362
+ let(:condition) {{ if: lambda { false } }}
363
+ end
364
+ end
365
+ end
366
+
367
+ describe ":unless condition" do
368
+ context "true condition" do
369
+ before do
370
+ controller.stub(:true_condition).and_return(true)
371
+ end
372
+ it_behaves_like 'an unimpressionable action' do
373
+ let(:condition) {{ unless: :true_condition }}
374
+ end
375
+
376
+ it_behaves_like 'an unimpressionable action' do
377
+ let(:condition) {{ unless: lambda { true } }}
378
+ end
379
+ end
380
+
381
+ context "false condition" do
382
+ before do
383
+ controller.stub(:false_condition).and_return(false)
384
+ end
385
+ it_behaves_like 'an impressionable action' do
386
+ let(:condition) {{ unless: :false_condition }}
387
+ end
388
+
389
+ it_behaves_like 'an impressionable action' do
390
+ let(:condition) {{ unless: lambda { false } }}
391
+ end
392
+ end
393
+ end
394
+ end
395
+ end
396
+