opawesome 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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