blackbeard 0.0.2.0 → 0.0.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/Guardfile +8 -0
- data/README.md +162 -20
- data/Rakefile +6 -0
- data/TODO.md +13 -34
- data/blackbeard.gemspec +5 -1
- data/console.rb +3 -0
- data/dashboard/public/bootstrap3-editable/css/bootstrap-editable.css +663 -0
- data/dashboard/public/bootstrap3-editable/img/clear.png +0 -0
- data/dashboard/public/bootstrap3-editable/img/loading.gif +0 -0
- data/dashboard/public/bootstrap3-editable/js/bootstrap-editable.min.js +7 -0
- data/dashboard/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/dashboard/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/dashboard/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/dashboard/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/dashboard/public/javascripts/bootstrap.min.js +7 -0
- data/dashboard/public/javascripts/jquery-1.10.2.min.js +6 -0
- data/dashboard/public/stylesheets/application.css +28 -0
- data/dashboard/public/stylesheets/bootstrap-theme.css +7 -0
- data/dashboard/public/stylesheets/bootstrap.css +7 -0
- data/dashboard/routes/base.rb +19 -0
- data/dashboard/routes/groups.rb +22 -0
- data/dashboard/routes/home.rb +11 -0
- data/dashboard/routes/metrics.rb +30 -0
- data/dashboard/routes/tests.rb +23 -0
- data/dashboard/views/groups/index.erb +22 -0
- data/dashboard/views/groups/show.erb +58 -0
- data/dashboard/views/index.erb +4 -0
- data/dashboard/views/layout.erb +48 -0
- data/dashboard/views/metrics/_metric_data.erb +59 -0
- data/dashboard/views/metrics/index.erb +23 -0
- data/dashboard/views/metrics/show.erb +73 -0
- data/dashboard/views/tests/index.erb +21 -0
- data/dashboard/views/tests/show.erb +58 -0
- data/lib/blackbeard/configuration.rb +8 -1
- data/lib/blackbeard/configuration_methods.rb +24 -0
- data/lib/blackbeard/context.rb +33 -21
- data/lib/blackbeard/dashboard.rb +17 -21
- data/lib/blackbeard/dashboard_helpers.rb +29 -0
- data/lib/blackbeard/errors.rb +2 -2
- data/lib/blackbeard/group.rb +35 -0
- data/lib/blackbeard/metric.rb +34 -32
- data/lib/blackbeard/metric_data/base.rb +101 -0
- data/lib/blackbeard/metric_data/total.rb +39 -0
- data/lib/blackbeard/metric_data/unique.rb +58 -0
- data/lib/blackbeard/metric_date.rb +11 -0
- data/lib/blackbeard/metric_hour.rb +17 -0
- data/lib/blackbeard/pirate.rb +33 -22
- data/lib/blackbeard/redis_store.rb +46 -2
- data/lib/blackbeard/selected_variation.rb +13 -0
- data/lib/blackbeard/storable.rb +39 -27
- data/lib/blackbeard/storable_attributes.rb +54 -0
- data/lib/blackbeard/storable_has_many.rb +60 -0
- data/lib/blackbeard/storable_has_set.rb +59 -0
- data/lib/blackbeard/test.rb +21 -0
- data/lib/blackbeard/version.rb +1 -1
- data/lib/blackbeard.rb +0 -8
- data/spec/configuration_spec.rb +15 -0
- data/spec/context_spec.rb +94 -19
- data/spec/dashboard/groups_spec.rb +50 -0
- data/spec/dashboard/home_spec.rb +20 -0
- data/spec/dashboard/metrics_spec.rb +57 -0
- data/spec/dashboard/tests_spec.rb +43 -0
- data/spec/group_spec.rb +36 -0
- data/spec/metric_data/base_spec.rb +57 -0
- data/spec/metric_data/total_spec.rb +116 -0
- data/spec/metric_data/unique_spec.rb +91 -0
- data/spec/metric_spec.rb +52 -44
- data/spec/pirate_spec.rb +32 -15
- data/spec/redis_store_spec.rb +121 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/storable_attributes_spec.rb +47 -0
- data/spec/storable_has_many_spec.rb +49 -0
- data/spec/storable_has_set_spec.rb +39 -0
- data/spec/storable_spec.rb +33 -0
- data/spec/test_spec.rb +25 -0
- metadata +133 -17
- data/lib/blackbeard/dashboard/helpers.rb +0 -8
- data/lib/blackbeard/dashboard/views/layout.erb +0 -15
- data/lib/blackbeard/dashboard/views/metrics/index.erb +0 -10
- data/lib/blackbeard/dashboard/views/metrics/show.erb +0 -16
- data/lib/blackbeard/feature.rb +0 -13
- data/lib/blackbeard/metric/total.rb +0 -17
- data/lib/blackbeard/metric/unique.rb +0 -18
- data/spec/dashboard_spec.rb +0 -38
- data/spec/total_metric_spec.rb +0 -65
- data/spec/unique_metric_spec.rb +0 -60
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'blackbeard/metric_data/base'
|
2
|
+
|
3
|
+
module Blackbeard
|
4
|
+
module MetricData
|
5
|
+
class Unique < Base
|
6
|
+
|
7
|
+
DEFAULT_SEGMENT = 'uniques'
|
8
|
+
def add(uid, amount = nil, segment = DEFAULT_SEGMENT)
|
9
|
+
add_at(tz.now, uid, amount, segment)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_at(time, uid, amount = nil, segment = DEFAULT_SEGMENT)
|
13
|
+
key = key_for_hour(time)
|
14
|
+
segment_key = segment_key(key, segment)
|
15
|
+
|
16
|
+
db.set_add_member(hours_set_key, key)
|
17
|
+
db.set_add_member(key, segment_key)
|
18
|
+
db.set_add_member(segment_key, uid)
|
19
|
+
#TODO: if not today, blow away rollup keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def result_for_hour(time)
|
23
|
+
key = key_for_hour(time)
|
24
|
+
segment_keys = db.set_members(key)
|
25
|
+
result = {}
|
26
|
+
segment_keys.each do |segment_key|
|
27
|
+
segment = segment_key.split(/::/).last
|
28
|
+
result[segment] = db.set_count(segment_key)
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def segment_key(key, segment)
|
34
|
+
"#{key}::#{segment}"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def merge_results(keys)
|
40
|
+
segments = {}
|
41
|
+
keys.each do |key|
|
42
|
+
segment_keys = db.set_members(key)
|
43
|
+
segment_keys.each do |segment_key|
|
44
|
+
segment = segment_key.split(/::/).last
|
45
|
+
segments[segment] ||= []
|
46
|
+
segments[segment].push(segment_key)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
merged_results = {}
|
50
|
+
segments.each do |segment, segment_keys|
|
51
|
+
merged_results[segment] = db.set_union_count(segment_keys)
|
52
|
+
end
|
53
|
+
merged_results
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Blackbeard
|
2
|
+
class MetricHour
|
3
|
+
attr_reader :hour, :result
|
4
|
+
|
5
|
+
def initialize(time, result)
|
6
|
+
@hour = round_to_beginning_of_hour(time)
|
7
|
+
@result = result
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def round_to_beginning_of_hour(t)
|
13
|
+
t - ((t.min * 60) + t.sec)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/blackbeard/pirate.rb
CHANGED
@@ -1,47 +1,58 @@
|
|
1
1
|
require "blackbeard/context"
|
2
2
|
require "blackbeard/metric"
|
3
|
-
require "blackbeard/
|
4
|
-
require "blackbeard/
|
5
|
-
require "blackbeard/
|
3
|
+
require "blackbeard/metric_data/unique"
|
4
|
+
require "blackbeard/metric_data/total"
|
5
|
+
require "blackbeard/test"
|
6
6
|
require "blackbeard/errors"
|
7
|
+
require "blackbeard/group"
|
7
8
|
|
8
9
|
module Blackbeard
|
9
10
|
class Pirate
|
10
11
|
def initialize
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@features = {}
|
12
|
+
@metrics = {}
|
13
|
+
@tests = {}
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
@
|
16
|
+
def metric(type, type_id)
|
17
|
+
@metrics["#{type}::#{type_id}"] ||= Metric.new(type, type_id)
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
@
|
20
|
+
def test(id)
|
21
|
+
@tests[id] ||= Test.new(id)
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
24
|
+
def context(*args)
|
25
|
+
Context.new(self, *args)
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
28
|
+
def set_context(*args)
|
29
|
+
@set_context = context(*args)
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
@set_context =
|
32
|
+
def clear_context
|
33
|
+
@set_context = nil
|
34
34
|
end
|
35
35
|
|
36
|
-
def add_unique(
|
37
|
-
|
38
|
-
@set_context.add_unique(
|
36
|
+
def add_unique(id)
|
37
|
+
return self unless @set_context
|
38
|
+
@set_context.add_unique(id)
|
39
39
|
end
|
40
40
|
|
41
|
-
def add_total(
|
42
|
-
|
43
|
-
@set_context.add_total(
|
41
|
+
def add_total(id, amount)
|
42
|
+
return self unless @set_context
|
43
|
+
@set_context.add_total(id, amount)
|
44
44
|
end
|
45
45
|
|
46
|
+
def ab_test(id, options)
|
47
|
+
return self unless @set_context
|
48
|
+
@set_context.ab_test(id, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def active?(id)
|
52
|
+
return self unless @set_context
|
53
|
+
@set_context.active?(id)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
46
57
|
end
|
47
58
|
end
|
@@ -14,10 +14,20 @@ module Blackbeard
|
|
14
14
|
redis.keys
|
15
15
|
end
|
16
16
|
|
17
|
+
|
18
|
+
# Hash commands
|
17
19
|
def hash_key_set_if_not_exists(hash_key, field, value)
|
18
20
|
redis.hsetnx(hash_key, field, value)
|
19
21
|
end
|
20
22
|
|
23
|
+
def hash_set(hash_key, field, value)
|
24
|
+
redis.hset(hash_key, field, value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash_multi_set(hash_key, hash)
|
28
|
+
redis.mapped_hmset(hash_key, hash) unless hash.empty?
|
29
|
+
end
|
30
|
+
|
21
31
|
def hash_length(hash_key)
|
22
32
|
redis.hlen(hash_key)
|
23
33
|
end
|
@@ -26,24 +36,50 @@ module Blackbeard
|
|
26
36
|
redis.hkeys(hash_key)
|
27
37
|
end
|
28
38
|
|
39
|
+
def hash_get(hash_key, field)
|
40
|
+
redis.hget(hash_key, field)
|
41
|
+
end
|
42
|
+
|
43
|
+
def hash_get_all(hash_key)
|
44
|
+
redis.hgetall(hash_key)
|
45
|
+
end
|
46
|
+
|
47
|
+
def hash_increment_by_float(hash_key, field, float)
|
48
|
+
redis.hincrbyfloat(hash_key, field, float)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set commands
|
29
52
|
def set_members(set_key)
|
30
53
|
redis.smembers(set_key)
|
31
54
|
end
|
32
55
|
|
56
|
+
def set_remove_member(set_key, member)
|
57
|
+
redis.srem(set_key, member)
|
58
|
+
end
|
59
|
+
|
33
60
|
def set_add_member(set_key, member)
|
34
61
|
redis.sadd(set_key, member)
|
35
62
|
end
|
36
63
|
|
64
|
+
def set_add_members(set_key, *members)
|
65
|
+
redis.sadd(set_key, members.flatten)
|
66
|
+
end
|
67
|
+
|
37
68
|
def set_count(set_key)
|
38
69
|
redis.scard(set_key)
|
39
70
|
end
|
40
71
|
|
72
|
+
def set_union_count(*keys)
|
73
|
+
redis.sunionstore('set_union_count', keys.flatten)
|
74
|
+
end
|
75
|
+
|
76
|
+
# String commands
|
41
77
|
def del(*keys)
|
42
78
|
redis.del(*keys)
|
43
79
|
end
|
44
80
|
|
45
|
-
def increment_by_float(key,
|
46
|
-
redis.incrbyfloat(key,
|
81
|
+
def increment_by_float(key, float)
|
82
|
+
redis.incrbyfloat(key, float)
|
47
83
|
end
|
48
84
|
|
49
85
|
def increment(key)
|
@@ -54,5 +90,13 @@ module Blackbeard
|
|
54
90
|
redis.get(key)
|
55
91
|
end
|
56
92
|
|
93
|
+
def multi_get(*keys)
|
94
|
+
redis.mget(*keys.flatten)
|
95
|
+
end
|
96
|
+
|
97
|
+
def set(key, value)
|
98
|
+
redis.set(key, value)
|
99
|
+
end
|
100
|
+
|
57
101
|
end
|
58
102
|
end
|
data/lib/blackbeard/storable.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
|
+
require 'blackbeard/configuration_methods'
|
2
|
+
require 'blackbeard/storable_attributes'
|
3
|
+
require 'blackbeard/storable_has_many'
|
4
|
+
require 'blackbeard/storable_has_set'
|
5
|
+
|
1
6
|
module Blackbeard
|
2
7
|
class Storable
|
3
|
-
|
8
|
+
include ConfigurationMethods
|
9
|
+
include StorableHasMany
|
10
|
+
include StorableHasSet
|
11
|
+
include StorableAttributes
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
13
|
+
class << self
|
14
|
+
def set_master_key(master_key)
|
15
|
+
@master_key = master_key.to_s
|
16
|
+
end
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
def master_key
|
19
|
+
return @master_key if defined? @master_key
|
20
|
+
return self.superclass.master_key if self.superclass.respond_to?(:master_key)
|
21
|
+
raise StorableMasterKeyUndefined, "define master key in the class that inherits from storable"
|
22
|
+
end
|
12
23
|
end
|
13
24
|
|
14
|
-
|
15
|
-
|
16
|
-
|
25
|
+
attr_reader :id
|
26
|
+
|
27
|
+
def initialize(id)
|
28
|
+
@id = id.to_s.downcase
|
29
|
+
db.hash_key_set_if_not_exists(master_key, key, tz.now.to_date)
|
17
30
|
end
|
18
31
|
|
19
32
|
def self.all_keys
|
@@ -24,34 +37,33 @@ module Blackbeard
|
|
24
37
|
db.hash_length(master_key)
|
25
38
|
end
|
26
39
|
|
27
|
-
def self.
|
28
|
-
|
40
|
+
def self.new_from_keys(*keys)
|
41
|
+
keys.flatten.map{ |key| new_from_key(key) }
|
29
42
|
end
|
30
43
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
44
|
+
def self.new_from_key(key)
|
45
|
+
if key =~ /^#{master_key}::(.+)$/
|
46
|
+
new($1)
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
35
50
|
end
|
36
51
|
|
37
|
-
def
|
38
|
-
|
52
|
+
def self.all
|
53
|
+
all_keys.map{ |key| new_from_key(key) }
|
39
54
|
end
|
40
55
|
|
41
|
-
def
|
42
|
-
self.class.
|
56
|
+
def ==(o)
|
57
|
+
o.class == self.class && o.id == self.id
|
43
58
|
end
|
44
59
|
|
45
|
-
def
|
46
|
-
|
60
|
+
def key
|
61
|
+
"#{master_key}::#{ id }"
|
47
62
|
end
|
48
63
|
|
49
|
-
def
|
50
|
-
self.class.
|
64
|
+
def master_key
|
65
|
+
self.class.master_key
|
51
66
|
end
|
52
67
|
|
53
|
-
def self.tz
|
54
|
-
Blackbeard.tz
|
55
|
-
end
|
56
68
|
end
|
57
69
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Blackbeard
|
2
|
+
module StorableAttributes
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def storable_attributes=(x)
|
10
|
+
@storable_attributes = x
|
11
|
+
end
|
12
|
+
|
13
|
+
def storable_attributes
|
14
|
+
return @storable_attributes if defined? @storable_attributes
|
15
|
+
return self.superclass.storable_attributes if self.superclass.respond_to?(:storable_attributes)
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
def string_attributes(*attributes)
|
20
|
+
self.storable_attributes += attributes
|
21
|
+
attributes.each do |method_name|
|
22
|
+
method_name = method_name.to_sym
|
23
|
+
send :define_method, method_name do
|
24
|
+
storable_attributes_hash[method_name.to_s]
|
25
|
+
end
|
26
|
+
send :define_method, "#{method_name}=".to_sym do |value|
|
27
|
+
db.hash_set(attributes_hash_key, method_name, value)
|
28
|
+
storable_attributes_hash[method_name.to_s] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
def update_attributes(tainted_params)
|
36
|
+
attributes = self.class.storable_attributes
|
37
|
+
safe_attributes = tainted_params.keys.select{ |k| attributes.include?(k.to_sym) }
|
38
|
+
safe_attributes.each do |attribute|
|
39
|
+
self.send("#{attribute}=".to_sym, tainted_params[attribute])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def storable_attributes_hash
|
44
|
+
@storable_attributes ||= db.hash_get_all(attributes_hash_key)
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes_hash_key
|
48
|
+
"#{key}::attributes"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Blackbeard
|
2
|
+
module StorableHasMany
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def has_many(options = {})
|
14
|
+
options.each_pair do |plural, klass|
|
15
|
+
_has_many(plural, klass)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def _has_many(plural, klass)
|
20
|
+
plural = plural.to_s.downcase
|
21
|
+
singular = klass.name.split('::').last.downcase
|
22
|
+
|
23
|
+
methods = <<-END_OF_RUBY
|
24
|
+
|
25
|
+
def has_#{singular}?(o)
|
26
|
+
#{singular}_ids.include?(o.id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_#{singular}(o)
|
30
|
+
db.set_add_member(#{plural}_set_key, o.key) unless has_#{singular}?(o)
|
31
|
+
\@#{plural} = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove_#{singular}(o)
|
35
|
+
db.set_remove_member(#{plural}_set_key, o.key)
|
36
|
+
\@#{plural} = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def #{plural}
|
40
|
+
\@#{plural} ||= #{klass.name}.new_from_keys(#{singular}_keys)
|
41
|
+
end
|
42
|
+
|
43
|
+
def #{singular}_ids
|
44
|
+
#{plural}.map{ |g| g.id }
|
45
|
+
end
|
46
|
+
|
47
|
+
def #{singular}_keys
|
48
|
+
db.set_members(#{plural}_set_key)
|
49
|
+
end
|
50
|
+
|
51
|
+
def #{plural}_set_key
|
52
|
+
key+"::#{plural}"
|
53
|
+
end
|
54
|
+
END_OF_RUBY
|
55
|
+
|
56
|
+
class_eval(methods)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blackbeard
|
2
|
+
module StorableHasSet
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def has_set(options = {})
|
14
|
+
options.each_pair do |plural, singular|
|
15
|
+
_has_set(plural, singular)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def _has_set(plural, singular)
|
20
|
+
plural = plural.to_s.downcase
|
21
|
+
singular = singular.to_s.downcase
|
22
|
+
|
23
|
+
methods = <<-END_OF_RUBY
|
24
|
+
|
25
|
+
def has_#{singular}?(o)
|
26
|
+
#{plural}.include?(o)
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_#{singular}(o)
|
30
|
+
db.set_add_member(#{plural}_key, o) unless has_#{singular}?(o)
|
31
|
+
\@#{plural} = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_#{plural}(*args)
|
35
|
+
args.flatten.reject!{|o| has_#{singular}?(o) }
|
36
|
+
db.set_add_members(#{plural}_key, args) unless args.empty?
|
37
|
+
\@#{plural} = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_#{singular}(o)
|
41
|
+
db.set_remove_member(#{plural}_key, o)
|
42
|
+
\@#{plural} = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def #{plural}
|
46
|
+
\@#{plural} ||= db.set_members(#{plural}_key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def #{plural}_key
|
50
|
+
key+"::#{plural}"
|
51
|
+
end
|
52
|
+
|
53
|
+
END_OF_RUBY
|
54
|
+
|
55
|
+
class_eval(methods)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'blackbeard/storable'
|
2
|
+
|
3
|
+
module Blackbeard
|
4
|
+
class Test < Storable
|
5
|
+
set_master_key :tests
|
6
|
+
string_attributes :name, :description
|
7
|
+
has_set :variations => :variation
|
8
|
+
|
9
|
+
def select_variation
|
10
|
+
# add :off unless :off, :control, :default, or :inactive
|
11
|
+
:off
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
storable_attributes_hash['name'] || id
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/blackbeard/version.rb
CHANGED
data/lib/blackbeard.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module Blackbeard
|
4
|
+
describe Configuration do
|
5
|
+
let(:config) { Configuration.new }
|
6
|
+
describe "define_group" do
|
7
|
+
it "should store the block in group_definiations" do
|
8
|
+
config.define_group(:hello) do |user,controller|
|
9
|
+
"world"
|
10
|
+
end
|
11
|
+
config.group_definitions[:hello].call.should == 'world'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|