opawesome 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 (64) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +39 -0
  4. data/app/assets/javascripts/opawesome/cookies.js +141 -0
  5. data/app/assets/javascripts/opawesome.js +36 -0
  6. data/app/controllers/opawesome/sessions_controller.rb +16 -0
  7. data/app/controllers/opawesome/tests_controller.rb +31 -0
  8. data/app/helpers/opawesome/sessions_helper.rb +2 -0
  9. data/app/helpers/opawesome/tracker_helper.rb +56 -0
  10. data/app/models/opawesome/test.rb +47 -0
  11. data/app/models/opawesome/test_option.rb +19 -0
  12. data/app/models/opawesome.rb +5 -0
  13. data/app/views/opawesome/tests/_option_fields.html.erb +5 -0
  14. data/app/views/opawesome/tests/edit.html.erb +30 -0
  15. data/app/views/opawesome/tests/index.html.erb +11 -0
  16. data/app/views/opawesome/tests/show.html.erb +33 -0
  17. data/config/routes.rb +6 -0
  18. data/db/migrate/20130519180140_create_opawesome_tests.rb +13 -0
  19. data/db/migrate/20130519180402_create_opawesome_test_options.rb +12 -0
  20. data/lib/opawesome/engine.rb +14 -0
  21. data/lib/opawesome/version.rb +3 -0
  22. data/lib/opawesome.rb +7 -0
  23. data/lib/tasks/opawesome_tasks.rake +4 -0
  24. data/spec/controllers/opawesome/sessions_controller_spec.rb +50 -0
  25. data/spec/dummy/README.rdoc +261 -0
  26. data/spec/dummy/Rakefile +7 -0
  27. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  28. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  29. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  30. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/config/application.rb +65 -0
  33. data/spec/dummy/config/boot.rb +10 -0
  34. data/spec/dummy/config/database.yml +25 -0
  35. data/spec/dummy/config/environment.rb +5 -0
  36. data/spec/dummy/config/environments/development.rb +37 -0
  37. data/spec/dummy/config/environments/production.rb +67 -0
  38. data/spec/dummy/config/environments/test.rb +37 -0
  39. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/dummy/config/initializers/inflections.rb +15 -0
  41. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  42. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  43. data/spec/dummy/config/initializers/session_store.rb +8 -0
  44. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/spec/dummy/config/locales/en.yml +5 -0
  46. data/spec/dummy/config/routes.rb +58 -0
  47. data/spec/dummy/config.ru +4 -0
  48. data/spec/dummy/db/development.sqlite3 +0 -0
  49. data/spec/dummy/db/schema.rb +34 -0
  50. data/spec/dummy/db/test.sqlite3 +0 -0
  51. data/spec/dummy/log/development.log +155 -0
  52. data/spec/dummy/log/test.log +1258 -0
  53. data/spec/dummy/public/404.html +26 -0
  54. data/spec/dummy/public/422.html +26 -0
  55. data/spec/dummy/public/500.html +25 -0
  56. data/spec/dummy/public/favicon.ico +0 -0
  57. data/spec/dummy/script/rails +6 -0
  58. data/spec/factories/opawesome_test_options.rb +8 -0
  59. data/spec/factories/opawesome_tests.rb +8 -0
  60. data/spec/helpers/opawesome/tracker_helper_spec.rb +163 -0
  61. data/spec/models/opawesome/test_option_spec.rb +31 -0
  62. data/spec/models/opawesome/test_spec.rb +41 -0
  63. data/spec/spec_helper.rb +19 -0
  64. metadata +250 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Alex Rehberg
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,3 @@
1
+ = Opawesome
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ begin
9
+ require 'rdoc/task'
10
+ rescue LoadError
11
+ require 'rdoc/rdoc'
12
+ require 'rake/rdoctask'
13
+ RDoc::Task = Rake::RDocTask
14
+ end
15
+
16
+ RDoc::Task.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Opawesome'
19
+ rdoc.options << '--line-numbers'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ Bundler::GemHelper.install_tasks
25
+
26
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
27
+ load 'rails/tasks/engine.rake'
28
+
29
+ Bundler::GemHelper.install_tasks
30
+
31
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
32
+
33
+ require 'rspec/core'
34
+ require 'rspec/core/rake_task'
35
+
36
+ desc "Run all specs in spec directory (excluding plugin specs)"
37
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
38
+
39
+ task :default => :spec
@@ -0,0 +1,141 @@
1
+ /*!
2
+ * Cookies.js - 0.3.1
3
+ * Wednesday, April 24 2013 @ 2:28 AM EST
4
+ *
5
+ * Copyright (c) 2013, Scott Hamper
6
+ * Licensed under the MIT license,
7
+ * http://www.opensource.org/licenses/MIT
8
+ */
9
+ (function (undefined) {
10
+ 'use strict';
11
+
12
+ var Cookies = function (key, value, options) {
13
+ return arguments.length === 1 ?
14
+ Cookies.get(key) : Cookies.set(key, value, options);
15
+ };
16
+
17
+ // Allows for setter injection in unit tests
18
+ Cookies._document = document;
19
+ Cookies._navigator = navigator;
20
+
21
+ Cookies.defaults = {
22
+ path: '/'
23
+ };
24
+
25
+ Cookies.get = function (key) {
26
+ if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
27
+ Cookies._renewCache();
28
+ }
29
+
30
+ return Cookies._cache[key];
31
+ };
32
+
33
+ Cookies.set = function (key, value, options) {
34
+ options = Cookies._getExtendedOptions(options);
35
+ options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);
36
+
37
+ Cookies._document.cookie = Cookies._generateCookieString(key, value, options);
38
+
39
+ return Cookies;
40
+ };
41
+
42
+ Cookies.expire = function (key, options) {
43
+ return Cookies.set(key, undefined, options);
44
+ };
45
+
46
+ Cookies._getExtendedOptions = function (options) {
47
+ return {
48
+ path: options && options.path || Cookies.defaults.path,
49
+ domain: options && options.domain || Cookies.defaults.domain,
50
+ expires: options && options.expires || Cookies.defaults.expires,
51
+ secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure
52
+ };
53
+ };
54
+
55
+ Cookies._isValidDate = function (date) {
56
+ return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
57
+ };
58
+
59
+ Cookies._getExpiresDate = function (expires, now) {
60
+ now = now || new Date();
61
+ switch (typeof expires) {
62
+ case 'number': expires = new Date(now.getTime() + expires * 1000); break;
63
+ case 'string': expires = new Date(expires); break;
64
+ }
65
+
66
+ if (expires && !Cookies._isValidDate(expires)) {
67
+ throw new Error('`expires` parameter cannot be converted to a valid Date instance');
68
+ }
69
+
70
+ return expires;
71
+ };
72
+
73
+ Cookies._generateCookieString = function (key, value, options) {
74
+ key = encodeURIComponent(key);
75
+ value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
76
+ options = options || {};
77
+
78
+ var cookieString = key + '=' + value;
79
+ cookieString += options.path ? ';path=' + options.path : '';
80
+ cookieString += options.domain ? ';domain=' + options.domain : '';
81
+ cookieString += options.expires ? ';expires=' + options.expires.toGMTString() : '';
82
+ cookieString += options.secure ? ';secure' : '';
83
+
84
+ return cookieString;
85
+ };
86
+
87
+ Cookies._getCookieObjectFromString = function (documentCookie) {
88
+ var cookieObject = {};
89
+ var cookiesArray = documentCookie ? documentCookie.split('; ') : [];
90
+
91
+ for (var i = 0; i < cookiesArray.length; i++) {
92
+ var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);
93
+
94
+ if (cookieObject[cookieKvp.key] === undefined) {
95
+ cookieObject[cookieKvp.key] = cookieKvp.value;
96
+ }
97
+ }
98
+
99
+ return cookieObject;
100
+ };
101
+
102
+ Cookies._getKeyValuePairFromCookieString = function (cookieString) {
103
+ // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
104
+ var separatorIndex = cookieString.indexOf('=');
105
+
106
+ // IE omits the "=" when the cookie value is an empty string
107
+ separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;
108
+
109
+ return {
110
+ key: decodeURIComponent(cookieString.substr(0, separatorIndex)),
111
+ value: decodeURIComponent(cookieString.substr(separatorIndex + 1))
112
+ };
113
+ };
114
+
115
+ Cookies._renewCache = function () {
116
+ Cookies._cache = Cookies._getCookieObjectFromString(Cookies._document.cookie);
117
+ Cookies._cachedDocumentCookie = Cookies._document.cookie;
118
+ };
119
+
120
+ Cookies._areEnabled = function () {
121
+ return Cookies._navigator.cookieEnabled ||
122
+ Cookies.set('cookies.js', 1).get('cookies.js') === '1';
123
+ };
124
+
125
+ Cookies.enabled = Cookies._areEnabled();
126
+
127
+ // AMD support
128
+ if (typeof define === 'function' && define.amd) {
129
+ define(function () { return Cookies; });
130
+ // CommonJS and Node.js module support.
131
+ } else if (typeof exports !== 'undefined') {
132
+ // Support Node.js specific `module.exports` (which can be a function)
133
+ if (typeof module !== 'undefined' && module.exports) {
134
+ exports = module.exports = Cookies;
135
+ }
136
+ // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
137
+ exports.Cookies = Cookies;
138
+ } else {
139
+ window.Cookies = Cookies;
140
+ }
141
+ })();
@@ -0,0 +1,36 @@
1
+ //= require 'opawesome/cookies'
2
+
3
+ OptimizatonTestForm = {
4
+ setup: function(){
5
+ $('.edit_test').on('click', '.remove_option', this.remove_attribute);
6
+ $('.edit_test').on('click', '.add_fields', this.add_attribute);
7
+ },
8
+
9
+ remove_attribute: function(event){
10
+ $(this).prev('input[type=hidden]').val('1');
11
+ $(this).closest('.input').hide();
12
+ event.preventDefault();
13
+ },
14
+
15
+ add_attribute: function(event){
16
+ time = new Date().getTime()
17
+ regexp = new RegExp($(this).data('id'), 'g')
18
+ $(this).before($(this).data('fields').replace(regexp, time))
19
+ event.preventDefault()
20
+ }
21
+ }
22
+
23
+ OptimizationTracker = {
24
+ create_session: function(){
25
+ if(Cookies('opt_valid_session') === undefined) {
26
+ $.post('/opawesome/sessions', function(data) {
27
+ // optimization tracking started successfully (we're not doing anything special here yet)
28
+ });
29
+ }
30
+ }
31
+ }
32
+
33
+ $(document).ready(function(){
34
+ OptimizatonTestForm.setup();
35
+ OptimizationTracker.create_session();
36
+ });
@@ -0,0 +1,16 @@
1
+ class Opawesome::SessionsController < ApplicationController
2
+ def create
3
+ # check for existing option cookies
4
+ unless cookies[:opaw_valid_session]
5
+ cookies.each do |k, v|
6
+ if k[0..4] == 'opaw_' && k != 'opaw_valid_session' && k != 'opaw_ignore'
7
+ option = Opawesome::TestOption.where(id: v).first
8
+ option.select! unless option.nil?
9
+ end
10
+ end
11
+ end
12
+
13
+ cookies[:opaw_valid_session] = true
14
+ render nothing: true, status: 200
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ class Opawesome::TestsController < ApplicationController
2
+ def index
3
+ @tests = Opawesome::Test.order('created_at DESC').all
4
+ end
5
+
6
+ def show
7
+ @test = Opawesome::Test.find(params[:id])
8
+ @test_options = @test.options.sort_by {|option| option.conversion_rate }
9
+ @test_options.reverse!
10
+ end
11
+
12
+ def edit
13
+ @test = Opawesome::Test.find(params[:id])
14
+ end
15
+
16
+ def update
17
+ @test = Opawesome::Test.find(params[:id])
18
+
19
+ if @test.update_attributes(params[:opawesome_test])
20
+ redirect_to opawesome_test_path(@test)
21
+ else
22
+ render :edit
23
+ end
24
+ end
25
+
26
+ def destroy
27
+ @test = Opawesome::Test.find(params[:id])
28
+ @test.destroy
29
+ redirect_to opawesome_tests_path
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ module Opawesome::SessionsHelper
2
+ end
@@ -0,0 +1,56 @@
1
+ # TODO: make this less of an ugly mess
2
+
3
+ module Opawesome::TrackerHelper
4
+ def opaw_select(key, default)
5
+ if cookies["opaw_#{key}"]
6
+ option = Opawesome::TestOption.where(id: cookies["opaw_#{key}"]).first
7
+
8
+ if option.nil?
9
+ cookies["opaw_#{key}"] = nil
10
+ else
11
+ return option.value
12
+ end
13
+ end
14
+
15
+ test = Opawesome::Test.where(key: key).first_or_initialize
16
+
17
+ # set up defaults if the test is new
18
+ if test.new_record?
19
+ test.name = test.key.to_s.humanize
20
+ test.save!
21
+ Opawesome::TestOption.create!(test_id: test.id, value: default, conversion_count: 0, selection_count: 0)
22
+ end
23
+
24
+ if cookies[:opaw_ignore]
25
+ test.best_option.value
26
+ else
27
+ # select an option and save it in the user's cookies
28
+ option = test.select_option
29
+
30
+ option.select! unless cookies[:opaw_valid_session].nil?
31
+
32
+ cookies.permanent["opaw_#{key}"] = option.id
33
+ option.value
34
+ end
35
+ end
36
+
37
+ def opaw_convert!(key)
38
+ return if cookies[:opaw_ignore]
39
+
40
+ if cookies["opaw_#{key}"]
41
+ option = Opawesome::TestOption.where(id: cookies["opaw_#{key}"]).first
42
+
43
+ if option.nil?
44
+ cookies["opaw_#{key}"] = nil
45
+ else
46
+ option.convert! if cookies[:opaw_valid_session]
47
+ end
48
+ end
49
+ end
50
+
51
+ def check_opaw_ignore
52
+ if params[:opaw_ignore]
53
+ cookies.permanent[:opaw_ignore] = true
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,47 @@
1
+ class Opawesome::Test < ActiveRecord::Base
2
+ attr_accessible :key, :name, :options_attributes
3
+
4
+ has_many :options, class_name: Opawesome::TestOption
5
+
6
+ accepts_nested_attributes_for :options, allow_destroy: true
7
+
8
+ validates :key, presence: true, uniqueness: true
9
+
10
+ def selection_count
11
+ options.map(&:selection_count).reduce(:+)
12
+ end
13
+
14
+ def conversion_count
15
+ options.map(&:conversion_count).reduce(:+)
16
+ end
17
+
18
+ def best_option
19
+ best_option = options.first
20
+
21
+ options.each do |option|
22
+ if option.conversion_rate > best_option.conversion_rate
23
+ best_option = option
24
+ end
25
+ end
26
+
27
+ best_option
28
+ end
29
+
30
+ # TODO: Break this out in to an OptionSelector class so we can support different selection strategies
31
+ def select_option
32
+ # round robbin until each option has at least 20 conversions, which gives us a good base to start from
33
+ options.shuffle.each do |option|
34
+ return option if option.conversion_count < 20
35
+ end
36
+
37
+ # use the best performing option most of the time, but try others as well
38
+ epsilon = 0.1
39
+ random = Random.rand
40
+
41
+ if random < epsilon
42
+ options.sample
43
+ else
44
+ best_option
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ class Opawesome::TestOption < ActiveRecord::Base
2
+ attr_accessible :test_id, :value, :conversion_count, :selection_count
3
+
4
+ belongs_to :test, class_name: Opawesome::Test
5
+
6
+ def conversion_rate
7
+ (selection_count == 0 or conversion_count == 0) ? 0 : (conversion_count.to_f / selection_count.to_f * 100.0)
8
+ end
9
+
10
+ def select!
11
+ self.selection_count += 1
12
+ save!
13
+ end
14
+
15
+ def convert!
16
+ self.conversion_count += 1
17
+ save!
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Opawesome
2
+ def self.table_name_prefix
3
+ 'opawesome_'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ <div class="input">
2
+ <%= f.text_field :value %>
3
+ <%= f.hidden_field :_destroy %>
4
+ <%= link_to "Remove", '#', class: "remove_option" %>
5
+ </div>
@@ -0,0 +1,30 @@
1
+ <h1>Edit Test</h1>
2
+
3
+ <%= form_for @test, html: {class: 'edit_test'} do |f| %>
4
+ <div class="input">
5
+ <%= f.label :name, 'Name' %>
6
+ <%= f.text_field :name %>
7
+ </div>
8
+
9
+ <h2>Test Options</h2>
10
+ <p><strong>NOTE:</strong> only edit options to fix typos, if you want to remove an option and add a new one, fully remove the option you want to get rid of and then create a new option. If you don't do this, the conversion stats from the old option will be used.</p>
11
+ <%= f.fields_for :options do |builder| %>
12
+ <%= render 'option_fields', f: builder %>
13
+ <% end %>
14
+ <%=
15
+ new_object = Opawesome::TestOption.new
16
+ id = new_object.object_id
17
+ fields = f.fields_for(:options, new_object, child_index: id) do |builder|
18
+ render "option_fields", f: builder
19
+ end
20
+ link_to("New Test Option", '#', class: "add_fields btn", data: {id: id, fields: fields.gsub("\n", "")})
21
+ %>
22
+
23
+ <div class="input">
24
+ <% if @test.new_record? %>
25
+ <%= f.submit "Create Test", class: 'btn' %>
26
+ <% else %>
27
+ <%= f.submit "Update Test", class: 'btn' %>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <h1>Opawesome Tests</h1>
2
+ <p>Viewing all tests</p>
3
+
4
+ <table cellspacing="3" class="admin_dashboard_table">
5
+ <% @tests.each do |test| %>
6
+ <tr>
7
+ <td><%= link_to test.name, opawesome_test_path(test) %></td>
8
+ <td><%= link_to "Edit", edit_opawesome_test_path(test) %></td>
9
+ </tr>
10
+ <% end %>
11
+ </table>
@@ -0,0 +1,33 @@
1
+ <h1><%= @test.name %></h1>
2
+ <p><%= link_to "Edit test and options", edit_opawesome_test_path(@test), class: 'btn' %></p>
3
+
4
+ <h2>Test options</h2>
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>Value</th>
9
+ <th>Selection Count</th>
10
+ <th>Conversion Count</th>
11
+ <th>Conversion Rate</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% @test_options.each do |option| %>
16
+ <% if @test.best_option == option %>
17
+ <tr>
18
+ <td><strong><%= option.value %></strong></td>
19
+ <td><strong><%= option.selection_count %></strong></td>
20
+ <td><strong><%= option.conversion_count %></strong></td>
21
+ <td><strong><%= option.conversion_rate.round(2) %>%</strong></td>
22
+ </tr>
23
+ <% else %>
24
+ <tr>
25
+ <td><%= option.value %></td>
26
+ <td><%= option.selection_count %></td>
27
+ <td><%= option.conversion_count %></td>
28
+ <td><%= option.conversion_rate.round(2) %>%</td>
29
+ </tr>
30
+ <% end %>
31
+ <% end %>
32
+ </tbody>
33
+ </table>
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ Rails.application.routes.draw do
2
+ namespace :opawesome do
3
+ resources :sessions
4
+ resources :tests
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ class CreateOpawesomeTests < ActiveRecord::Migration
2
+ def change
3
+ create_table :opawesome_tests do |t|
4
+ t.string :key
5
+ t.string :name
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :opawesome_tests, :key
11
+ end
12
+ end
13
+
@@ -0,0 +1,12 @@
1
+ class CreateOpawesomeTestOptions < ActiveRecord::Migration
2
+ def change
3
+ create_table :opawesome_test_options do |t|
4
+ t.integer :test_id
5
+ t.integer :conversion_count, default: 0
6
+ t.integer :selection_count, default: 0
7
+ t.string :value
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Opawesome
2
+ class Engine < ::Rails::Engine
3
+ initializer "opawesome.tracking_methods" do |app|
4
+ ActionController::Base.send :include, Opawesome::TrackerHelper
5
+ end
6
+
7
+ config.generators do |g|
8
+ g.test_framework :rspec, fixture: false
9
+ g.fixture_replacement :factory_girl, dir: 'spec/factories'
10
+ g.assets false
11
+ g.helper false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Opawesome
2
+ VERSION = "0.0.1"
3
+ end
data/lib/opawesome.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "opawesome/engine"
2
+
3
+ module Opawesome
4
+ def self.table_name_prefix
5
+ 'opawesome_'
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :opawesome do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opawesome::SessionsController do
4
+ describe "POST 'create'" do
5
+ it "should respond with an empty success response" do
6
+ post :create
7
+ response.should be_successful
8
+ end
9
+
10
+ it "should create a valid optimization session cookie" do
11
+ post :create
12
+ cookies[:opaw_valid_session].should eq(true)
13
+ end
14
+
15
+ context "when there are existing test option ids" do
16
+ before do
17
+ @test_option = mock_model Opawesome::TestOption, select!: true
18
+ Opawesome::TestOption.stub(:where){ Opawesome::TestOption }
19
+ Opawesome::TestOption.stub(:first){ @test_option }
20
+ cookies[:opaw_test] = @test_option.id
21
+ end
22
+
23
+ it "should find the test option" do
24
+ Opawesome::TestOption.should_receive(:where).with(hash_including(id: @test_option.id))
25
+ post :create
26
+ end
27
+
28
+ it "should select the option" do
29
+ @test_option.should_receive(:select!)
30
+ post :create
31
+ end
32
+
33
+ context "when the session has already been validated" do
34
+ before do
35
+ cookies[:opaw_valid_session] = true
36
+ end
37
+
38
+ it "should not find the test option" do
39
+ Opawesome::TestOption.should_not_receive(:where)
40
+ post :create
41
+ end
42
+
43
+ it "should not select the option" do
44
+ @test_option.should_not_receive(:select!)
45
+ post :create
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end