caboose-cms 0.3.20 → 0.3.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODg5MTMwYTQ4MmM5MDgwYzE4YTNhOTVkOTJmNjU1YjU4MmU0YmFkNg==
4
+ MzhlOGU4ZDRlZmYxZGIzODk0Zjk0NDhhM2IwYjM2ZTViMjlmZTA2ZA==
5
5
  data.tar.gz: !binary |-
6
- ZmQ3YzA1ZjU2ZGEwOTYyNWRiMjkxZWFhYzU5MDlkYjZhNmIxNGQzMw==
6
+ OWUwZmM5MjdkNmEwNWJkMWIzYjlkN2Q4NTBiMjliZmNkNDM5MzZiNQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NTIxZjMzNTBmMmJmOTE0NzdiNmQ4ZmIyZGVlMGRlZDQyNGQxOWEzNzAxZDA3
10
- ZTE1YTI4OGEzMmYwNGJjMzNkMGJkMjgyMzQwZDU3Njg2MTliNTdmOTZlMWFj
11
- OTU5MWYwY2M1MTY4ZDQ1NTgzMjU3MGZjNzc4OGViM2ZhNzdjNzI=
9
+ YjNhYjE1MzU1ZmU4NTQzOWFjMDY5ZmVjM2UwY2NhMzFmNDViMjE0ZTBhY2Ux
10
+ NDA1ODFiMGQyZGMzNWQ1MTcxZjMxMGI1MjAzZDliYjNlYzdlNWE4YTY4Yzc0
11
+ OTRmMzkwMmFkODcxYmVkM2I5MzAzMmI1YWU2NDI3NDQwZjJmYWU=
12
12
  data.tar.gz: !binary |-
13
- MmRmZWE4OGM0MTMzZmIyYzQwZmViMDVjYjJjMTExMWNiNWRlZGExZTI4YTgx
14
- NjNjYzgyODllMmYwZDMxY2ZhMTE4ZWY2NmU5YTcxNjMyNmI2NzQyNmQ2Nzc1
15
- ODIzN2VjYjQ0MThlYzdiNTg5M2ZkZjJhYmVjOTE4N2NjNTM5NGE=
13
+ ZmQ2YTQ4N2Q4M2U3MGJkYWYwNmNhYjczZDU4YTEzZmYwODM2NDBhN2RlMDRm
14
+ NDBkMGU4ZGJiZDAxMjI2NWRkMGQzMjc2ZTVkOTQ5YTNiYWFkNTMwNjMyNmE5
15
+ MTg5N2M5Yjc4ZjY3Yjg3Y2FhOTkwMTY5M2Q3NGIxM2UyM2Q1N2Y=
@@ -0,0 +1,57 @@
1
+ module Caboose
2
+ class AbOptionsController < ApplicationController
3
+ layout 'caboose/admin'
4
+
5
+ # GET /admin/ab_variants/:variant_id/options
6
+ def admin_index
7
+ return unless user_is_allowed_to 'view', 'ab_variants'
8
+ v = AbVariant.find(params[:variant_id])
9
+ render :json => v.ab_options
10
+ end
11
+
12
+ # POST admin/ab-variants/:variant_id/options'
13
+ def admin_create
14
+ return unless user_is_allowed_to 'edit','ab_variants'
15
+
16
+ resp = StdClass.new({
17
+ 'error' => nil,
18
+ 'redirect' => nil
19
+ })
20
+
21
+ opt = AbOption.create(
22
+ :ab_variant_id => params[:variant_id],
23
+ :text => params[:text]
24
+ )
25
+ resp.redirect = "/admin/ab-variants/#{params[:variant_id]}"
26
+ render :json => resp
27
+ end
28
+
29
+ # PUT /admin/ab_options/:id
30
+ def admin_update
31
+ return unless user_is_allowed_to 'edit', 'ab_variants'
32
+
33
+ resp = StdClass.new
34
+ opt = AbOption.find(params[:id])
35
+
36
+ save = true
37
+ params.each do |k,v|
38
+ case k
39
+ when 'text'
40
+ opt.text = v
41
+ break
42
+ end
43
+ end
44
+
45
+ resp.success = save && opt.save
46
+ render :json => resp
47
+ end
48
+
49
+ # DELETE /admin/ab_options/:id
50
+ def admin_delete
51
+ return unless user_is_allowed_to 'delete', 'ab_variants'
52
+ AbOption.find(params[:id]).destroy
53
+ render :json => true
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ module Caboose
2
+ class AbVariantsController < ApplicationController
3
+ layout 'caboose/admin'
4
+
5
+ def before_action
6
+ @page = Page.page_with_uri('/admin')
7
+ end
8
+
9
+ # GET /admin/ab_variants
10
+ def admin_index
11
+ return unless user_is_allowed_to 'view', 'ab_variants'
12
+
13
+ @gen = PageBarGenerator.new(params, {'name' => '', 'analytics_name' => ''}, {
14
+ 'model' => 'Caboose::AbVariant',
15
+ 'sort' => 'name',
16
+ 'desc' => 'false',
17
+ 'base_url' => '/admin/ab-variants'
18
+ })
19
+ @variants = @gen.items
20
+ end
21
+
22
+ # GET /admin/ab_variants/new
23
+ def admin_new
24
+ return unless user_is_allowed_to 'add', 'ab_variants'
25
+ @variant = AbVariant.new
26
+ end
27
+
28
+ # GET /admin/ab_variants/:id
29
+ def admin_edit
30
+ return unless user_is_allowed_to 'edit', 'ab_variants'
31
+ @variant = AbVariant.find(params[:id])
32
+ end
33
+
34
+ # POST /admin/ab-variants
35
+ def admin_create
36
+ return unless user_is_allowed_to 'edit', 'ab_variants'
37
+
38
+ resp = StdClass.new({
39
+ 'error' => nil,
40
+ 'redirect' => nil
41
+ })
42
+
43
+ variant = AbVariant.new
44
+ variant.name = params[:name]
45
+ variant.analytics_name = params[:name].gsub(' ', '_').downcase
46
+
47
+ if (variant.name.length == 0)
48
+ resp.error = "A name is required."
49
+ elsif variant.save
50
+ resp.redirect = "/admin/ab-variants/#{variant.id}"
51
+ end
52
+
53
+ render json: resp
54
+ end
55
+
56
+ # PUT /admin/ab_variants/:id
57
+ def admin_update
58
+ return unless user_is_allowed_to 'edit', 'ab_variants'
59
+
60
+ resp = StdClass.new
61
+ variant = AbVariant.find(params[:id])
62
+
63
+ save = true
64
+ params.each do |k,v|
65
+ case k
66
+ when 'name'
67
+ variant.name = v
68
+ break
69
+ when 'analytics_name'
70
+ variant.analytics_name = v
71
+ break
72
+ end
73
+ end
74
+
75
+ resp.success = save && variant.save
76
+ render :json => resp
77
+ end
78
+
79
+ # DELETE /admin/ab_variants/:id
80
+ def admin_destroy
81
+ return unless user_is_allowed_to 'delete', 'ab_variants'
82
+ AbVariants.find(params[:id]).destroy
83
+ resp = StdClass.new('redirect' => '/admin/ab-variants')
84
+ render :json => resp
85
+ end
86
+
87
+ end
88
+ end
@@ -13,6 +13,7 @@ module Caboose
13
13
  @page = Page.page_with_uri(request.fullpath)
14
14
 
15
15
  session['use_redirect_urls'] = true if session['use_redirect_urls'].nil?
16
+ assign_ab_variants
16
17
 
17
18
  @crumb_trail = Caboose::Page.crumb_trail(@page)
18
19
  @subnav = {}
@@ -23,10 +24,10 @@ module Caboose
23
24
 
24
25
  # Sets an instance variable of the logged in user
25
26
  @logged_in_user = logged_in_user
26
-
27
+
27
28
  before_action
28
29
  end
29
-
30
+
30
31
  # Parses any parameters in the URL and adds them to the params
31
32
  def parse_url_params
32
33
  return if !Caboose.use_url_params
@@ -96,6 +97,8 @@ module Caboose
96
97
  return session["app_user"]
97
98
  end
98
99
 
100
+ # DEPRECATED: Use user_is_allowed_to(action, resource)
101
+ #
99
102
  # Checks to see if a user has permission to perform the given action
100
103
  # on the given resource.
101
104
  # Redirects to login if not logged in.
@@ -115,7 +118,31 @@ module Caboose
115
118
 
116
119
  return true
117
120
  end
118
-
121
+
122
+ # Checks to see if a user has permission
123
+ # to perform action on resource
124
+ #
125
+ # Redirects to login if not logged in
126
+ # Redirects to error page with message if not allowed
127
+ #
128
+ # useful for creating super-readable code, for example:
129
+ # > return unless user_is_allowed_to 'edit', 'pages'
130
+ # Even your mom could read that code.
131
+ def user_is_allowed_to(action, resource)
132
+ unless logged_in?
133
+ redirect_to "/login?return_url=" + URI.encode(request.fullpath)
134
+ return false
135
+ end
136
+
137
+ @user = logged_in_user
138
+ unless @user.is_allowed(resource, action)
139
+ @error = "You don't have permission to #{action} #{resource}"
140
+ render :template => "caboose/extras/error"
141
+ return false
142
+ end
143
+ return true
144
+ end
145
+
119
146
  # Redirects to login if not logged in.
120
147
  def verify_logged_in
121
148
  if (!logged_in?)
@@ -147,5 +174,44 @@ module Caboose
147
174
  return "" if v.nil?
148
175
  return v.val
149
176
  end
177
+
178
+ #===========================================================================
179
+ # AB Testing
180
+ #===========================================================================
181
+
182
+ # Sets the ab_variants for the user's session
183
+ def assign_ab_variants
184
+ return if session['ab_variants']
185
+
186
+ h = {}
187
+ arr = []
188
+ AbVariant.find_each do |var|
189
+ next if var.ab_options.nil? || var.ab_options.count == 0
190
+ i = rand(var.ab_options.count)
191
+ h[var.analytics_name] = var.ab_options[i].text
192
+ arr << "#{var.analytics_name}=#{i+1}"
193
+ end
194
+ session['ab_variants'] = h
195
+ session['ab_variants_analytics_string'] = "|#{arr.join('|')}|"
196
+ end
197
+
198
+ # Get the variant option text for the given variant name.
199
+ def ab_option_for(analytics_name)
200
+ assign_ab_variants if session['ab_variants'].nil?
201
+ return session['ab_variants'][analytics_name] if !session['ab_variants'][analytics_name].nil?
202
+
203
+ # Otherwise, add the new variant to the session
204
+ var = AbVariant.find(:analytics_name => analytics_name).first
205
+ i = rand(var.ab_options.count)
206
+ session['ab_variants'][var.analytics_name] = var.ab_options[i].text
207
+ session['ab_variants_analytics_string'] << "#{var.analytics_name}=#{i+1}|"
208
+ end
209
+
210
+ # Gets the string to be sent to google analytics
211
+ def analytics_string
212
+ assign_ab_variants if session['ab_variants_analytics_string'].nil?
213
+ return "#{session['ab_variants_analytics_string']}"
214
+ end
215
+
150
216
  end
151
217
  end
@@ -20,5 +20,10 @@ module Caboose
20
20
  return str
21
21
  end
22
22
 
23
+ def ab_variants_analytics_code
24
+ str = session['ab_variants_analytics_string']
25
+ return "var _gaq = _gaq || [];\n_gaq.push(['_setCustomVar', 1, 'caboose_ab_variants', #{Caboose.json(str)}]);"
26
+ end
27
+
23
28
  end
24
29
  end
@@ -0,0 +1,7 @@
1
+ class Caboose::AbOption < ActiveRecord::Base
2
+
3
+ self.table_name = "ab_options"
4
+ belongs_to :ab_variant
5
+ attr_accessible :text, :ab_variant_id
6
+
7
+ end
@@ -0,0 +1,22 @@
1
+ # Class for A-B testing variants. A variant is a set of changes
2
+ # to the same element on a page. For example, a variant can be used
3
+ # to change the button text, or images displayed, or even more
4
+ # complicated behavior
5
+ class Caboose::AbVariant < ActiveRecord::Base
6
+ self.table_name = "ab_variants"
7
+
8
+ has_many :ab_options, :order => 'id', :dependent => :destroy
9
+ attr_accessible :name, :analytics_name
10
+
11
+ def get_session_option
12
+ return "" unless self.ab_options
13
+ opt = self.ab_options.sample
14
+ return {text: opt.text, id: opt.id}
15
+ end
16
+
17
+ #def random_option
18
+ # return nil self.ab_options.nil? || self.ab_options.count == 0
19
+ # return self.ab_options.sample
20
+ #end
21
+
22
+ end
@@ -20,12 +20,13 @@ class Caboose::CorePlugin < Caboose::CaboosePlugin
20
20
  'text' => 'Settings',
21
21
  'children' => []
22
22
  }
23
-
23
+
24
24
  item['children'] << { 'id' => 'users' , 'text' => 'Users' , 'href' => '/admin/users' , 'modal' => false } if user.is_allowed('users' , 'view')
25
25
  item['children'] << { 'id' => 'roles' , 'text' => 'Roles' , 'href' => '/admin/roles' , 'modal' => false } if user.is_allowed('roles' , 'view')
26
26
  item['children'] << { 'id' => 'permissions' , 'text' => 'Permissions' , 'href' => '/admin/permissions' , 'modal' => false } if user.is_allowed('permissions' , 'view')
27
+ item['children'] << { 'id' => 'blocktypes' , 'text' => 'AB Test Variants' , 'href' => '/admin/ab-variants' , 'modal' => false } if user.is_allowed('abvariants' , 'view')
27
28
  item['children'] << { 'id' => 'variables' , 'text' => 'Variables' , 'href' => '/admin/settings' , 'modal' => false } if user.is_allowed('settings' , 'view')
28
- item['children'] << { 'id' => 'blocktypes' , 'text' => 'Page Block Types' , 'href' => '/admin/page-block-types' , 'modal' => false } if user.is_allowed('pageblocktypes' , 'view')
29
+ item['children'] << { 'id' => 'blocktypes' , 'text' => 'Page Block Types' , 'href' => '/admin/page-block-types' , 'modal' => false } if user.is_allowed('pageblocktypes' , 'view')
29
30
 
30
31
  nav << item if item['children'].count > 0
31
32
 
@@ -39,6 +40,9 @@ class Caboose::CorePlugin < Caboose::CaboosePlugin
39
40
  item['children'] << { 'id' => 'posts' , 'text' => 'Posts' , 'href' => '/admin/posts' , 'modal' => false } if user.is_allowed('posts' , 'view')
40
41
 
41
42
  nav << item if item['children'].count > 0
43
+ if user.is_allowed('ab-variants','view')
44
+ nav << { 'id' => 'ab_variants', 'text' => 'AB Testing', 'href' => '/admin/ab-variants', 'modal' => false }
45
+ end
42
46
  return nav
43
47
  end
44
48
 
@@ -143,6 +143,14 @@ class Caboose::Schema < Caboose::Utilities::Schema
143
143
  [ :page_id , :integer ],
144
144
  [ :action , :string ]
145
145
  ],
146
+ Caboose::AbVariant => [
147
+ [ :name , :string ],
148
+ [ :analytics_name , :string ],
149
+ ],
150
+ Caboose::AbOption => [
151
+ [ :text , :string ],
152
+ [ :ab_variant_id , :integer ]
153
+ ],
146
154
  Caboose::DatabaseSession => [
147
155
  [ :session_id , :string , :null => false ],
148
156
  [ :data , :text ],
@@ -45,11 +45,17 @@ class Caboose::Utilities::Schema
45
45
  c = ActiveRecord::Base.connection
46
46
  self.schema.each do |model, columns|
47
47
  tbl = model.table_name
48
- #puts "Creating table #{tbl}..."
48
+ puts "Creating table #{tbl}..."
49
49
  c.create_table tbl if !c.table_exists?(tbl)
50
50
  columns.each do |col|
51
- #puts "Creating column #{tbl}.#{col[0]}..."
51
+ puts "Creating column #{tbl}.#{col[0]}..."
52
52
 
53
+ # Special case for attachments
54
+ if col[1] == :attachment
55
+ c.add_attachment tbl, col[1] if !c.column_exists?(tbl, "#{col[1]}_file_size")
56
+ next
57
+ end
58
+
53
59
  # Skip if the column exists with the proper data type
54
60
  next if c.column_exists?(tbl, col[0], col[1])
55
61
 
@@ -0,0 +1,99 @@
1
+ <h1>Editing AB Test Variant</h1>
2
+
3
+ <div id="abvariant_<%= @variant.id %>_name"></div>
4
+ <div id="abvariant_<%= @variant.id %>_analytics_name"></div>
5
+
6
+ <p>Google analytics search term: <code>caboose_ab_variants="*|<%= @variant.analytics_name %>=1|*"</code></p>
7
+ <h2>Options</h2>
8
+ <% if @variant.ab_options && @variant.ab_options.count > 0 %>
9
+ <table>
10
+ <% @variant.ab_options.each do |opt| %>
11
+ <tr>
12
+ <td><div id="aboption_<%= opt.id %>_text"></div></td>
13
+ <td><input type='button' value='Delete' onclick="delete_option(<%= opt.id %>);" /></td>
14
+ </tr>
15
+ <% end %>
16
+ </table>
17
+ <% else %>
18
+ <p>This variant doesn't have any options.</p>
19
+ <% end %>
20
+
21
+ <div id="message"></div>
22
+ <p>
23
+ <input type='button' value='Back' onclick="window.location='/admin/ab-variants';" >
24
+ <input type='button' value='Add New Option' onclick="add_option();" >
25
+ </p>
26
+
27
+ <% content_for :caboose_js do %>
28
+ <%= javascript_include_tag "caboose/model/all" %>
29
+ <script type="text/javascript">
30
+
31
+ function delete_option(option_id, confirm)
32
+ {
33
+ if (!confirm)
34
+ {
35
+ var p = $('<p/>').addClass('note warning')
36
+ .append("Are you sure you want to delete the option? ")
37
+ .append($('<input/>').attr('type','button').val('Yes').click(function() { delete_option(option_id, true); })).append(' ')
38
+ .append($('<input/>').attr('type','button').val('No').click(function() { $('#message').empty(); }));
39
+ $('#message').empty().append(p);
40
+ return;
41
+ }
42
+ $('#message').empty().html($('<p/>').addClass('loading').html("Deleting option..."));
43
+ $.ajax({
44
+ url: '/admin/ab-options/' + option_id,
45
+ type: 'delete',
46
+ success: function(resp) {
47
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
48
+ else window.location.reload(true);
49
+ }
50
+ });
51
+ }
52
+
53
+ function add_option() {
54
+ $('#message').html("<p class='loading'>Adding new A/B Option...</p>");
55
+ $.ajax({
56
+ url: '/admin/ab-variants/<%= @variant.id %>/options',
57
+ type: 'post',
58
+ data: {
59
+ text: ""
60
+ },
61
+ success: function(resp) {
62
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
63
+ if (resp.redirect) window.location.reload(true);
64
+ }
65
+ });
66
+ }
67
+
68
+ $(document).ready(function() {
69
+
70
+ m = new ModelBinder({
71
+ name: 'abvariant',
72
+ id: <%= @variant.id %>,
73
+ update_url: '/admin/ab-variants/<%= @variant.id %>',
74
+ authenticity_token: '<%= form_authenticity_token %>',
75
+ attributes: [
76
+ { name: 'name' , nice_name: 'Variant Name' , type: 'text', value: <%= raw Caboose.json(@variant.name) %> , width: 800 },
77
+ { name: 'analytics_name' , nice_name: 'Google Analytics Name' , type: 'text', value: <%= raw Caboose.json(@variant.analytics_name) %> , width: 800 }
78
+ ]
79
+ });
80
+
81
+ <% if @variant.ab_options && @variant.ab_options.count > 0 %>
82
+ <% i = 1 %>
83
+ <% @variant.ab_options.each do |opt| %>
84
+ m = new ModelBinder({
85
+ name: 'aboption',
86
+ id: <%= opt.id %>,
87
+ update_url: '/admin/ab-options/<%= opt.id %>',
88
+ authenticity_token: '<%= form_authenticity_token %>',
89
+ attributes: [
90
+ { name: 'text', nice_name: 'Option <%= i %>', type: 'text', value: <%= raw Caboose.json(opt.text) %> , width: 800 }
91
+ ]
92
+ });
93
+ <% i = i + 1 %>
94
+ <% end %>
95
+ <% end %>
96
+ });
97
+
98
+ </script>
99
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <h1>A/B Test Variants</h1>
2
+
3
+ <p><a href='/admin/ab-variants/new'>New Variant</a></p>
4
+ <% if (@variants.count > 0) %>
5
+ <table class='data'>
6
+ <tr><%= raw @gen.sortable_table_headings({'name' => 'Variant Name', 'analytics_name' => 'Analytics Name'}) %></tr>
7
+ <% @variants.each do |v| %>
8
+ <tr onclick="window.location='/admin/ab-variants/<%= v.id %>';">
9
+ <td><%= v.name %></td>
10
+ <td><%= v.analytics_name %></td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
14
+ <p><%= raw @gen.generate %></p>
15
+ <% else %>
16
+ <p>No A/B variants found.</p>
17
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <h1>New AB Test</h1>
2
+
3
+ <form action='/admin/ab-variants' method='post' id='ab_variant_form'>
4
+ <input type='hidden' name='authenticity_token' value='<%=form_authenticity_token %>'>
5
+ <p><input type='text' name='name' id='name' placeholder='AB Variant Name' style='width: 500px;'></p>
6
+ <div id='message'></div>
7
+ <p>
8
+ <input type='button' value='< Back' onclick="window.location='/admin/ab-variants';">
9
+ <input type='submit' value='Add New A/B Variant' onclick="add_ab_variant(); return false;">
10
+ </p>
11
+ </form>
12
+
13
+ <% content_for :caboose_js do %>
14
+ <script type='text/javascript'>
15
+
16
+ function add_ab_variant()
17
+ {
18
+ $('#message').html("<p class='loading'>Adding A/B Variant...</p>");
19
+ $.ajax({
20
+ url: '/admin/ab-variants',
21
+ type: 'post',
22
+ data: $('#ab_variant_form').serialize(),
23
+ success: function(resp) {
24
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
25
+ if (resp.redirect) window.location = resp.redirect;
26
+ }
27
+ });
28
+ }
29
+
30
+ var modal = false;
31
+ $(window).load(function() {
32
+ modal = new CabooseModal(800);
33
+ });
34
+
35
+ </script>
36
+ <% end %>
@@ -115,6 +115,18 @@ Caboose::Engine.routes.draw do
115
115
  get "admin/posts" => "posts#admin_index"
116
116
  post "admin/posts" => "posts#admin_add"
117
117
  delete "admin/posts/:id" => "posts#admin_delete"
118
+
119
+ get "admin/ab-variants" => "ab_variants#admin_index"
120
+ get "admin/ab-variants/new" => "ab_variants#admin_new"
121
+ get "admin/ab-variants/:id" => "ab_variants#admin_edit"
122
+ put "admin/ab-variants/:id" => "ab_variants#admin_update"
123
+ post "admin/ab-variants" => "ab_variants#admin_create"
124
+ delete "admin/ab-variants/:id" => "ab_variants#admin_delete"
125
+
126
+ get "admin/ab-variants/:variant_id/options" => "ab_options#admin_index"
127
+ put "admin/ab-options/:id" => "ab_options#admin_update"
128
+ post "admin/ab-variants/:variant_id/options" => "ab_options#admin_create"
129
+ delete "admin/ab-options/:id" => "ab_options#admin_delete"
118
130
 
119
131
  match '*path' => 'pages#show'
120
132
  root :to => 'pages#show'
@@ -1,3 +1,3 @@
1
1
  module Caboose
2
- VERSION = '0.3.20'
2
+ VERSION = '0.3.22'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caboose-cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.20
4
+ version: 0.3.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-17 00:00:00.000000000 Z
11
+ date: 2014-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -166,6 +166,8 @@ files:
166
166
  - app/assets/stylesheets/caboose/station_modal.css
167
167
  - app/assets/stylesheets/caboose/station_sidebar.css
168
168
  - app/assets/stylesheets/caboose/tinymce.css
169
+ - app/controllers/caboose/ab_options_controller.rb
170
+ - app/controllers/caboose/ab_variants_controller.rb
169
171
  - app/controllers/caboose/admin_controller.rb
170
172
  - app/controllers/caboose/application_controller.rb
171
173
  - app/controllers/caboose/login_controller.rb
@@ -186,6 +188,8 @@ files:
186
188
  - app/helpers/caboose/pages_helper.rb
187
189
  - app/helpers/caboose/permissions_helper.rb
188
190
  - app/helpers/caboose/products_helper.rb
191
+ - app/models/caboose/ab_option.rb
192
+ - app/models/caboose/ab_variant.rb
189
193
  - app/models/caboose/approval_request.rb
190
194
  - app/models/caboose/asset.rb
191
195
  - app/models/caboose/authenticator.rb
@@ -221,6 +225,9 @@ files:
221
225
  - app/models/caboose/std_class.rb
222
226
  - app/models/caboose/user.rb
223
227
  - app/models/caboose/utilities/schema.rb
228
+ - app/views/caboose/ab_variants/admin_edit.html.erb
229
+ - app/views/caboose/ab_variants/admin_index.html.erb
230
+ - app/views/caboose/ab_variants/admin_new.html.erb
224
231
  - app/views/caboose/admin/index.html.erb
225
232
  - app/views/caboose/application/show.html.erb
226
233
  - app/views/caboose/extras/error.html.erb