bucket_maker 0.0.1 → 0.0.2
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 +8 -8
- data/.gitignore +1 -0
- data/README.md +1 -0
- data/app/controllers/bucket_maker_controller.rb +13 -0
- data/config/routes.rb +3 -0
- data/lib/bucket_maker/bucket.rb +33 -4
- data/lib/bucket_maker/configuration.rb +26 -1
- data/lib/bucket_maker/engine.rb +4 -0
- data/lib/bucket_maker/models/bucketable.rb +43 -0
- data/lib/bucket_maker/series.rb +34 -4
- data/lib/bucket_maker/series_maker.rb +64 -7
- data/lib/bucket_maker/version.rb +3 -1
- data/lib/generators/active_record/bucket_maker_generator.rb +26 -0
- data/lib/generators/bucket_maker/bucket_maker_generator.rb +2 -0
- data/lib/generators/bucket_maker/install_generator.rb +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzI3ZDVhYjZhNGQwMDc0YTljOTk1MzBmODE1MDYzNmUwOGRkNWQ1Zg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Yjk3N2ExMWNkZjI5MDkzZDIwZGExMDVlZmM0MDE2YTMxOWM0NmJhNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTUwMWQ3ODA5OTYyODNkZDYzOWY0ZmYxMGJlNDQzYjg3N2EzYjcyMmUxOGFk
|
10
|
+
OTZhNTU3ZTg2MmY2MWMwNTY1OTQ1MDcyOWFkMmUxNDU1NWQ4MzNiNGE3MzZi
|
11
|
+
NzllOGQ4ODYyMmQ2OTI0OGNlNjFiMTIxMjQzZTczN2U4ZWJmNTE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTM0Y2QzODkwOTRkNDhlN2FmMGI1ODY0NGMwYjJjZWIzYjY2NjkxOGFmNmRm
|
14
|
+
ZDg3NGE2Nzk0ZGM3Y2FkYjA5MTUwMzU1OGYxYjRmOTQ5Zjg1OTE1MTliNDZj
|
15
|
+
MjVhZWZkMDVhNjYxYmJmNDYwMTMxMmQyMzM1MmRhMWYyMzk0NGE=
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
[](https://codeclimate.com/github/dinks/bucket_maker)
|
5
5
|
[](https://travis-ci.org/dinks/bucket_maker)
|
6
6
|
[](https://coveralls.io/r/dinks/bucket_maker)
|
7
|
+
[](https://gemnasium.com/dinks/bucket_maker)
|
7
8
|
|
8
9
|
A Gem to categorize Objects into buckets. Typical use case is an A/B test for Users
|
9
10
|
|
@@ -4,27 +4,40 @@ class BucketMakerController < ApplicationController
|
|
4
4
|
before_filter :ensure_valid_series_bucket_group, only: [:show, :switch]
|
5
5
|
before_filter :ensure_valid_series_bucket, only: [:randomize]
|
6
6
|
|
7
|
+
# Show if the group for the current_user is the same as stored
|
8
|
+
#
|
7
9
|
def show
|
8
10
|
render text: @current_user.in_bucket?(@series_name, @bucket_name, @group_name)
|
9
11
|
end
|
10
12
|
|
13
|
+
# Switch the current user to group_name
|
14
|
+
#
|
11
15
|
def switch
|
12
16
|
render text: @current_user.force_to_bucket!(@series_name, @bucket_name, @group_name)
|
13
17
|
end
|
14
18
|
|
19
|
+
# Randomize the current_user for series_name and bucket_name
|
20
|
+
#
|
15
21
|
def randomize
|
16
22
|
render text: @current_user.bucketize_for_series_and_bucket!(@series_name, @bucket_name)
|
17
23
|
end
|
18
24
|
|
19
25
|
private
|
26
|
+
|
27
|
+
# Ensure we have valid attributes
|
28
|
+
#
|
20
29
|
def ensure_valid_series_bucket_group
|
21
30
|
not_found unless BucketMaker.buckets_configuration.has_group_in_bucket_in_series?(@series_name, @bucket_name, @group_name)
|
22
31
|
end
|
23
32
|
|
33
|
+
# Ensure we have valid attributes
|
34
|
+
#
|
24
35
|
def ensure_valid_series_bucket
|
25
36
|
not_found unless BucketMaker.buckets_configuration.has_bucket_in_series?(@series_name, @bucket_name)
|
26
37
|
end
|
27
38
|
|
39
|
+
# Head not found for invalid requests
|
40
|
+
#
|
28
41
|
def not_found
|
29
42
|
head :not_found
|
30
43
|
false
|
data/config/routes.rb
CHANGED
@@ -2,12 +2,15 @@ Rails.application.routes.draw do
|
|
2
2
|
if BucketMaker.configured? && BucketMaker.load_routes?
|
3
3
|
|
4
4
|
# Show if the user is in group
|
5
|
+
#
|
5
6
|
get "/#{BucketMaker.configuration.path_prefix}:series_name/:bucket_name/:group_name", to: 'bucket_maker#show', as: :show_bucket, format: :json
|
6
7
|
|
7
8
|
# Randomize group
|
9
|
+
#
|
8
10
|
post "/#{BucketMaker.configuration.path_prefix}:series_name/:bucket_name", to: 'bucket_maker#randomize', as: :randomize_bucket, format: :json
|
9
11
|
|
10
12
|
# Force Switch group
|
13
|
+
#
|
11
14
|
post "/#{BucketMaker.configuration.path_prefix}:series_name/:bucket_name/:group_name", to: 'bucket_maker#switch', as: :switch_bucket, format: :json
|
12
15
|
|
13
16
|
end
|
data/lib/bucket_maker/bucket.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module BucketMaker
|
2
|
+
# Class which holds all the Bucket information
|
3
|
+
#
|
2
4
|
class Bucket
|
3
5
|
attr_accessor :name,
|
4
6
|
:summary,
|
@@ -11,6 +13,11 @@ module BucketMaker
|
|
11
13
|
BUCKET_USER_AFTER = 'created_after'
|
12
14
|
BUCKET_DISTRIBUTION = 'distributions'
|
13
15
|
|
16
|
+
# Initializer
|
17
|
+
#
|
18
|
+
# @param name [string] Name of the Bucket
|
19
|
+
# @param options [Hash] Options for the Bucket like BUCKET_DESCRIPTION, BUCKET_USER_AFTER, BUCKET_DISTRIBUTION
|
20
|
+
#
|
14
21
|
def initialize(name, options={})
|
15
22
|
@name = name.to_sym
|
16
23
|
@summary = options[BUCKET_DESCRIPTION]
|
@@ -27,20 +34,28 @@ module BucketMaker
|
|
27
34
|
result[dist_name.to_sym] = dist_options
|
28
35
|
result
|
29
36
|
end if options[BUCKET_DISTRIBUTION]
|
30
|
-
|
31
37
|
end
|
32
38
|
|
39
|
+
# Randomize and get the group for this bucket
|
40
|
+
#
|
41
|
+
# @return [String] Group Name after randomization
|
42
|
+
#
|
33
43
|
def random_group
|
34
44
|
# Is set after first randomization
|
35
45
|
unless @distributions_percent
|
46
|
+
# Get the total value of the distributions
|
47
|
+
#
|
36
48
|
@denominator = @distributions.inject(0) do |result, (_, dist_value)|
|
37
49
|
result + dist_value
|
38
50
|
end
|
51
|
+
# Populate the variable with Distribution Percentages
|
52
|
+
#
|
39
53
|
@distributions_percent = @distributions.inject({}) do |result, (dist_name, dist_value)|
|
40
54
|
result[dist_name.to_sym] = (dist_value * 100.0)/@denominator
|
41
55
|
result
|
42
56
|
end
|
43
|
-
|
57
|
+
# Change the Distribution Percentages with Ranges for easy checks
|
58
|
+
#
|
44
59
|
@distributions_percent.inject(0) do |starter, (dist_name, percent_value)|
|
45
60
|
ender = starter + percent_value
|
46
61
|
@distributions_percent[dist_name.to_sym] = (starter .. ender)
|
@@ -48,18 +63,32 @@ module BucketMaker
|
|
48
63
|
end
|
49
64
|
end
|
50
65
|
|
66
|
+
# Randomize within the 0..max_range
|
67
|
+
#
|
51
68
|
randomized = rand(@denominator * 100)
|
69
|
+
# Find where the randomized number falls in the calculated range
|
70
|
+
#
|
52
71
|
@distributions_percent.find do |_, percent_range|
|
53
72
|
percent_range.include?(randomized)
|
54
73
|
end.first
|
55
74
|
end
|
56
75
|
|
76
|
+
# Check if the Bucketable conforms to the pre-conditions
|
77
|
+
#
|
78
|
+
# @param bucketable [Object] an object which responds to :created_at
|
79
|
+
# @return [Boolean] is it bucketable?
|
80
|
+
#
|
57
81
|
def is_bucketable?(bucketable)
|
58
|
-
bucketable.created_at >= @created_after
|
82
|
+
bucketable.created_at >= @created_after rescue false
|
59
83
|
end
|
60
84
|
|
85
|
+
# Check if there is a group in this bucket
|
86
|
+
#
|
87
|
+
# @param group_name [String] Name of the group
|
88
|
+
# @return [Boolean] does the group exist?
|
89
|
+
#
|
61
90
|
def has_group?(group_name)
|
62
|
-
@distributions[group_name.to_sym] != nil
|
91
|
+
@distributions[group_name.to_sym] != nil if group_name
|
63
92
|
end
|
64
93
|
end
|
65
94
|
end
|
@@ -3,6 +3,8 @@ require 'active_support/core_ext'
|
|
3
3
|
require 'bucket_maker/series_maker'
|
4
4
|
|
5
5
|
module BucketMaker
|
6
|
+
# Configuration Holder for the BucketMaker
|
7
|
+
#
|
6
8
|
class Configuration
|
7
9
|
attr_accessor :redis_options,
|
8
10
|
:path_prefix,
|
@@ -13,23 +15,35 @@ module BucketMaker
|
|
13
15
|
attr_reader :buckets_configuration,
|
14
16
|
:connection
|
15
17
|
|
18
|
+
# Initializer
|
19
|
+
# Sets up the default variable values
|
20
|
+
#
|
16
21
|
def initialize
|
22
|
+
# For Redis
|
17
23
|
@redis_options = {
|
18
24
|
host: 'localhost',
|
19
25
|
port: 6379,
|
20
26
|
db: 1
|
21
27
|
}
|
22
|
-
|
23
28
|
@redis_expiration_time = 12.months
|
24
29
|
|
30
|
+
# For paths
|
31
|
+
# If nil, the routes wont be loaded
|
25
32
|
@path_prefix = '2bOrNot2B/'
|
26
33
|
|
34
|
+
# Configuration for the buckets
|
35
|
+
#
|
27
36
|
@buckets_config_file = nil
|
28
37
|
@buckets_configuration = nil
|
29
38
|
|
39
|
+
# Lazy Load is used to group only if in_bucket? is called
|
40
|
+
# if false, then the group is done at creation time of the objec as well
|
41
|
+
#
|
30
42
|
@lazy_load = true
|
31
43
|
end
|
32
44
|
|
45
|
+
# Reconfigure the Configuration
|
46
|
+
#
|
33
47
|
def reconfigure!
|
34
48
|
if @buckets_config_file
|
35
49
|
@buckets_configuration = BucketMaker::SeriesMaker.instance
|
@@ -37,10 +51,17 @@ module BucketMaker
|
|
37
51
|
end
|
38
52
|
end
|
39
53
|
|
54
|
+
# Check if the configuration is done
|
55
|
+
#
|
56
|
+
# @return [Boolean]
|
40
57
|
def configured?
|
41
58
|
@buckets_configuration && @buckets_configuration.configured?
|
42
59
|
end
|
43
60
|
|
61
|
+
# Check if its ok to load routes
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
#
|
44
65
|
def load_routes?
|
45
66
|
@path_prefix != nil
|
46
67
|
end
|
@@ -48,6 +69,8 @@ module BucketMaker
|
|
48
69
|
end
|
49
70
|
|
50
71
|
class << self
|
72
|
+
# Forward some of the calls to the configuration object
|
73
|
+
#
|
51
74
|
extend Forwardable
|
52
75
|
|
53
76
|
attr_accessor :configuration
|
@@ -55,6 +78,8 @@ module BucketMaker
|
|
55
78
|
def_delegators :@configuration, :configured?, :reconfigure!, :buckets_configuration, :load_routes?
|
56
79
|
end
|
57
80
|
|
81
|
+
# Configure after yielding to the block
|
82
|
+
#
|
58
83
|
def self.configure
|
59
84
|
if block_given?
|
60
85
|
self.configuration ||= Configuration.new
|
data/lib/bucket_maker/engine.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module BucketMaker
|
2
|
+
# Engine for the BucketMaker
|
3
|
+
#
|
2
4
|
class Engine < Rails::Engine
|
3
5
|
initializer 'Require concerns path' do |app|
|
4
6
|
concerns_path = 'app/controllers/concerns'
|
@@ -7,6 +9,8 @@ module BucketMaker
|
|
7
9
|
app.paths.add(concerns_path)
|
8
10
|
end
|
9
11
|
|
12
|
+
# Always add the concern
|
13
|
+
#
|
10
14
|
require 'concerns/bucket_maker_concern'
|
11
15
|
end
|
12
16
|
end
|
@@ -2,7 +2,18 @@ require 'bucket_maker/series_maker'
|
|
2
2
|
|
3
3
|
module BucketMaker
|
4
4
|
module Models
|
5
|
+
# Module which holds the base methods for Bucketable objects
|
6
|
+
#
|
5
7
|
module Bucketable
|
8
|
+
|
9
|
+
# Used to test if this object has been grouped under a certain group
|
10
|
+
#
|
11
|
+
# @param series_name [String] Name of the Series
|
12
|
+
# @param bucket_name [String] Name of the Bucket
|
13
|
+
# @param group_name [String] Name of the Group
|
14
|
+
# @param force [Boolean] To force the object into the group_name
|
15
|
+
# @return [Boolean] if the given group_name is the same after randomization of groups
|
16
|
+
#
|
6
17
|
def in_bucket?(series_name, bucket_name, group_name, force=false)
|
7
18
|
# Get the singleton Series Maker
|
8
19
|
series_maker = BucketMaker::SeriesMaker.instance
|
@@ -39,15 +50,32 @@ module BucketMaker
|
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
53
|
+
# Used to test if this object has not been grouped under a certain group
|
54
|
+
#
|
55
|
+
# @param series_name [String] Name of the Series
|
56
|
+
# @param bucket_name [String] Name of the Bucket
|
57
|
+
# @param group_name [String] Name of the Group
|
58
|
+
# @return [Boolean] if the given group_name is the not the same after randomization of groups
|
59
|
+
#
|
42
60
|
def not_in_bucket?(series_name, bucket_name, group_name)
|
43
61
|
!in_bucket?(series_name, bucket_name, group_name)
|
44
62
|
end
|
45
63
|
|
64
|
+
# Force object into the group_name under bucket under series
|
65
|
+
#
|
66
|
+
# @param series_name [String] Name of the Series
|
67
|
+
# @param bucket_name [String] Name of the Bucket
|
68
|
+
# @param group_name [String] Name of the Group
|
69
|
+
# @return [Boolean] Always true because forcing is ok by us
|
70
|
+
#
|
46
71
|
def force_to_bucket!(series_name, bucket_name, group_name)
|
47
72
|
# Forcefully place inside the bucket
|
48
73
|
in_bucket?(series_name, bucket_name, group_name, true)
|
49
74
|
end
|
50
75
|
|
76
|
+
# Iteratively group object into buckets
|
77
|
+
#
|
78
|
+
# @return [Boolean] true if all the operations are successfull
|
51
79
|
def bucketize!
|
52
80
|
# Take each series and bucket
|
53
81
|
BucketMaker::SeriesMaker.instance.for_each_series_with_bucketable do |series_maker, series_name, bucket_name|
|
@@ -61,6 +89,11 @@ module BucketMaker
|
|
61
89
|
end
|
62
90
|
end
|
63
91
|
|
92
|
+
# Group object for series_name and bucket_name
|
93
|
+
#
|
94
|
+
# @param series_name [String] Name of the Series
|
95
|
+
# @param bucket_name [String] Name of the Bucket
|
96
|
+
# @return [Boolean] true if the operation is successfull
|
64
97
|
def bucketize_for_series_and_bucket!(series_name, bucket_name)
|
65
98
|
# Get the singleton Series Maker
|
66
99
|
series_maker = BucketMaker::SeriesMaker.instance
|
@@ -77,10 +110,20 @@ module BucketMaker
|
|
77
110
|
end
|
78
111
|
end
|
79
112
|
|
113
|
+
# Get the value from persistent store for a key
|
114
|
+
#
|
115
|
+
# @param series_key [String] Series Key
|
116
|
+
# @return [String] should return the group_name
|
117
|
+
#
|
80
118
|
def group_for_key(series_key)
|
81
119
|
raise NotImplementedError, "Implement group_for_key"
|
82
120
|
end
|
83
121
|
|
122
|
+
# Set the value to a persistent store for a series and group
|
123
|
+
#
|
124
|
+
# @param series_key [String] Series Key
|
125
|
+
# @param group_name [String] Name of the Group
|
126
|
+
#
|
84
127
|
def set_group_for_key(series_key, group_name)
|
85
128
|
raise NotImplementedError, "Implement set_group_for_key"
|
86
129
|
end
|
data/lib/bucket_maker/series.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module BucketMaker
|
2
|
+
# Class which holds all the Series based information
|
3
|
+
#
|
2
4
|
class Series
|
3
5
|
attr_reader :name,
|
4
6
|
:description,
|
@@ -9,6 +11,11 @@ module BucketMaker
|
|
9
11
|
SERIES_USER_AFTER = 'created_after'
|
10
12
|
SERIES_BUCKETS = 'buckets'
|
11
13
|
|
14
|
+
# Initializer
|
15
|
+
#
|
16
|
+
# @param name [String] Name of the Series
|
17
|
+
# @param options [Hash] Options for the Series like SERIES_DESCRIPTION, SERIES_USER_AFTER, SERIES_BUCKETS
|
18
|
+
#
|
12
19
|
def initialize(name, options={})
|
13
20
|
@name = name.to_sym
|
14
21
|
@description = options[SERIES_DESCRIPTION] || ''
|
@@ -23,26 +30,49 @@ module BucketMaker
|
|
23
30
|
end if options[SERIES_BUCKETS]
|
24
31
|
end
|
25
32
|
|
33
|
+
# Iterator going through each bucket
|
34
|
+
#
|
26
35
|
def each_bucket
|
27
36
|
@buckets.each do |bucket_name, bucket|
|
28
37
|
yield bucket_name, bucket
|
29
38
|
end if block_given?
|
30
39
|
end
|
31
40
|
|
41
|
+
# Get the Bucket object when given the bucket_name
|
42
|
+
#
|
43
|
+
# @param bucket_name [String] Name of the Bucket
|
44
|
+
# @return [BucketMaker::Bucket] Bucket object
|
45
|
+
#
|
32
46
|
def bucket_with_name(bucket_name)
|
33
|
-
@buckets[bucket_name.to_sym]
|
47
|
+
@buckets[bucket_name.to_sym] if bucket_name
|
34
48
|
end
|
35
49
|
|
50
|
+
# Check if the Bucketable object conforms to the conditions
|
51
|
+
#
|
52
|
+
# @param bucketable [Object] an object which responds to :created_at
|
53
|
+
# @return [Boolean] is it bucketable?
|
54
|
+
#
|
36
55
|
def is_bucketable?(bucketable)
|
37
|
-
bucketable.created_at >= @created_after
|
56
|
+
bucketable.created_at >= @created_after rescue false
|
38
57
|
end
|
39
58
|
|
59
|
+
# Check if the Series has the bucket
|
60
|
+
#
|
61
|
+
# @param bucket_name [String] Name of the Bucket
|
62
|
+
# @return [Boolean] does the bucket exist?
|
63
|
+
#
|
40
64
|
def has_bucket?(bucket_name)
|
41
|
-
@buckets[bucket_name.to_sym] != nil
|
65
|
+
@buckets[bucket_name.to_sym] != nil rescue false
|
42
66
|
end
|
43
67
|
|
68
|
+
# Check if the bucket has the group
|
69
|
+
#
|
70
|
+
# @param bucket_name [String] Name of the Bucket
|
71
|
+
# @param group_name [String] Name of the Group
|
72
|
+
# @return [Boolean] does the group exist inside the bucket?
|
73
|
+
#
|
44
74
|
def has_group_in_bucket?(bucket_name, group_name)
|
45
|
-
has_bucket?(bucket_name) && @buckets[bucket_name.to_sym].has_group?(group_name)
|
75
|
+
has_bucket?(bucket_name) && @buckets[bucket_name.to_sym].has_group?(group_name) rescue false
|
46
76
|
end
|
47
77
|
end
|
48
78
|
end
|
@@ -4,6 +4,8 @@ require 'bucket_maker/series'
|
|
4
4
|
require 'bucket_maker/bucket'
|
5
5
|
|
6
6
|
module BucketMaker
|
7
|
+
# Singleton which holds all information regarding the series, buckets and groups
|
8
|
+
#
|
7
9
|
class SeriesMaker
|
8
10
|
include Singleton
|
9
11
|
|
@@ -13,6 +15,10 @@ module BucketMaker
|
|
13
15
|
|
14
16
|
BUCKET_ROOT = 'series'
|
15
17
|
|
18
|
+
# Set the class variables with configuration details
|
19
|
+
#
|
20
|
+
# @param config [String] Path of the config file WRT the rails app
|
21
|
+
#
|
16
22
|
def make!(config)
|
17
23
|
@series = []
|
18
24
|
|
@@ -28,10 +34,17 @@ module BucketMaker
|
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
37
|
+
# Check if the SeriesMaker is configured
|
38
|
+
#
|
39
|
+
# @return [Boolean] Signifies if the Object is configured
|
40
|
+
#
|
31
41
|
def configured?
|
32
42
|
@configuration != nil
|
33
43
|
end
|
34
44
|
|
45
|
+
# Iterator for each series to be run on a Bucketable instance
|
46
|
+
# Expects a block parameter to be passed
|
47
|
+
#
|
35
48
|
def for_each_series_with_bucketable
|
36
49
|
@series.collect do |series_name, series|
|
37
50
|
series.each_bucket do |bucket_name, bucket|
|
@@ -40,37 +53,81 @@ module BucketMaker
|
|
40
53
|
end.inject(true) {|result, value| result && value } if block_given?
|
41
54
|
end
|
42
55
|
|
56
|
+
# Get the Series object when given the series_name
|
57
|
+
#
|
58
|
+
# @param series_name [String] Series Name to search
|
59
|
+
# @return [BucketMaker::Series]
|
60
|
+
#
|
43
61
|
def series_with_name(series_name)
|
44
|
-
@series[series_name.to_sym]
|
62
|
+
@series[series_name.to_sym] if series_name
|
45
63
|
end
|
46
64
|
|
65
|
+
# Check if the series exist
|
66
|
+
#
|
67
|
+
# @param series_name [String] Series Name to search
|
68
|
+
# @return [Boolean] is it there?
|
69
|
+
#
|
47
70
|
def has_series?(series_name)
|
48
|
-
@series[series_name.to_sym] != nil
|
71
|
+
@series[series_name.to_sym] != nil rescue false
|
49
72
|
end
|
50
73
|
|
74
|
+
# Check if the bucket exist in the series
|
75
|
+
#
|
76
|
+
# @param series_name [String] Series Name to search
|
77
|
+
# @param bucket_name [String] Bucket Name to search
|
78
|
+
# @return [Boolean] is it there?
|
79
|
+
#
|
51
80
|
def has_bucket_in_series?(series_name, bucket_name)
|
52
|
-
has_series?(series_name) && series_with_name(series_name).has_bucket?(bucket_name)
|
81
|
+
has_series?(series_name) && series_with_name(series_name).has_bucket?(bucket_name) rescue false
|
53
82
|
end
|
54
83
|
|
84
|
+
# Check if the group exists in the bucket in the series
|
85
|
+
#
|
86
|
+
# @param series_name [String] Series Name to search
|
87
|
+
# @param bucket_name [String] Bucket Name to search
|
88
|
+
# @param group_name [String] Group Name to search
|
89
|
+
# @return [Boolean] is it there?
|
90
|
+
#
|
55
91
|
def has_group_in_bucket_in_series?(series_name, bucket_name, group_name)
|
56
92
|
has_bucket_in_series?(series_name, bucket_name) &&
|
57
|
-
series_with_name(series_name).bucket_with_name(bucket_name).has_group?(group_name)
|
93
|
+
series_with_name(series_name).bucket_with_name(bucket_name).has_group?(group_name) rescue false
|
58
94
|
end
|
59
95
|
|
96
|
+
# Get the key for the combination
|
97
|
+
#
|
98
|
+
# @param bucketable [Object] any object which responds to :id
|
99
|
+
# @param series_name [String] Series Name
|
100
|
+
# @param bucket_name [String] Bucket Name
|
101
|
+
# @return [String] The key for future use
|
102
|
+
#
|
60
103
|
def key_for_series(bucketable, series_name, bucket_name)
|
61
|
-
"bucket_maker:#{bucketable.class.to_s.underscore}_#{bucketable.id}:#{series_name.to_s}:#{bucket_name.to_s}" if bucketable && bucketable.id
|
104
|
+
"bucket_maker:#{bucketable.class.to_s.underscore}_#{bucketable.id}:#{series_name.to_s}:#{bucket_name.to_s}" if bucketable && bucketable.id rescue nil
|
62
105
|
end
|
63
106
|
|
107
|
+
# Get a random group for the bucket in a series
|
108
|
+
#
|
109
|
+
# @param series_name [String] Series Name
|
110
|
+
# @param bucket_name [String] Bucket Name
|
111
|
+
# @return [String] Random Group Name according to the distribution
|
112
|
+
#
|
64
113
|
def bucketize(series_name, bucket_name)
|
65
|
-
series_with_name(series_name).bucket_with_name(bucket_name).random_group
|
114
|
+
series_with_name(series_name).bucket_with_name(bucket_name).random_group rescue nil
|
66
115
|
end
|
67
116
|
|
117
|
+
# Check if a Bucketable Object conform to the pre-conditions
|
118
|
+
#
|
119
|
+
# @param bucketable [Object] any object which has methods :has_attribute?, :created_at and :id
|
120
|
+
# @param series_name [String] Series Name
|
121
|
+
# @param bucket_name [String] Bucket Name
|
122
|
+
# @param group_name [String] Group Name
|
123
|
+
# @return [Boolean] is it bucketable?
|
124
|
+
#
|
68
125
|
def bucketable?(bucketable, series_name, bucket_name, group_name)
|
69
126
|
if bucketable.has_attribute?(:created_at) &&
|
70
127
|
has_group_in_bucket_in_series?(series_name, bucket_name, group_name)
|
71
128
|
|
72
129
|
series_with_name(series_name).bucket_with_name(bucket_name).is_bucketable?(bucketable) ||
|
73
|
-
series_with_name(series_name).is_bucketable?(bucketable)
|
130
|
+
series_with_name(series_name).is_bucketable?(bucketable) rescue false
|
74
131
|
|
75
132
|
else
|
76
133
|
false
|
data/lib/bucket_maker/version.rb
CHANGED
@@ -2,6 +2,8 @@ require 'rails/generators/active_record'
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Generators
|
5
|
+
# Generator to create Active Record specific classes
|
6
|
+
#
|
5
7
|
class BucketMakerGenerator < ActiveRecord::Generators::Base
|
6
8
|
|
7
9
|
ACTIVE_RECORDABLE = 'bucket'
|
@@ -9,6 +11,10 @@ module ActiveRecord
|
|
9
11
|
source_root File.expand_path("../../templates", __FILE__)
|
10
12
|
argument :store_in, type: :string, default: 'redis'
|
11
13
|
|
14
|
+
# Generate the model
|
15
|
+
#
|
16
|
+
# If store_in is active_record then the ACTIVE_RECORDABLE is also created which holds association
|
17
|
+
#
|
12
18
|
def generate_model
|
13
19
|
invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
|
14
20
|
if store_in == 'active_record'
|
@@ -16,6 +22,10 @@ module ActiveRecord
|
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
25
|
+
# Inject the content to the models
|
26
|
+
#
|
27
|
+
# If store_in is actice_record then the ACTIVE_RECORDABLE is also inserted with association
|
28
|
+
#
|
19
29
|
def inject_bucket_maker_content
|
20
30
|
contents = <<-CONTENT.strip_heredoc
|
21
31
|
# Module Inclusion for BucketMaker
|
@@ -40,6 +50,8 @@ module ActiveRecord
|
|
40
50
|
inject_for_active_recordable if store_in == 'active_record' && active_recordable_exists?
|
41
51
|
end
|
42
52
|
|
53
|
+
# Content for association if store_in is active_record
|
54
|
+
#
|
43
55
|
def inject_for_active_recordable
|
44
56
|
ar_contents = <<-CONTENT.strip_heredoc
|
45
57
|
# Association with the bucket
|
@@ -51,6 +63,10 @@ module ActiveRecord
|
|
51
63
|
inject_into_class(active_recordable_path, ACTIVE_RECORDABLE.camelize, ar_contents)
|
52
64
|
end
|
53
65
|
|
66
|
+
# Migration copier
|
67
|
+
#
|
68
|
+
# If store_in is actice_record then the migration runs
|
69
|
+
#
|
54
70
|
def copy_bucket_maker_migration
|
55
71
|
if behavior == :invoke && store_in == 'active_record' && active_recordable_exists?
|
56
72
|
migration_template "active_recordable_migration.rb", "db/migrate/create_#{ACTIVE_RECORDABLE.pluralize}"
|
@@ -59,22 +75,32 @@ module ActiveRecord
|
|
59
75
|
|
60
76
|
private
|
61
77
|
|
78
|
+
# Check if the MODEL exists
|
79
|
+
#
|
62
80
|
def model_exists?
|
63
81
|
File.exists?(File.join(destination_root, model_path))
|
64
82
|
end
|
65
83
|
|
84
|
+
# Check if the association exists
|
85
|
+
#
|
66
86
|
def active_recordable_exists?
|
67
87
|
File.exists?(File.join(destination_root, active_recordable_path))
|
68
88
|
end
|
69
89
|
|
90
|
+
# Get the association path
|
91
|
+
#
|
70
92
|
def active_recordable_path
|
71
93
|
@ar_path ||= File.join("app", "models", "#{ACTIVE_RECORDABLE}.rb")
|
72
94
|
end
|
73
95
|
|
96
|
+
# Get the MODEL path
|
97
|
+
#
|
74
98
|
def model_path
|
75
99
|
@model_path ||= File.join("app", "models", "#{file_path}.rb")
|
76
100
|
end
|
77
101
|
|
102
|
+
# Get the module name to the included for store_in values
|
103
|
+
#
|
78
104
|
def module_name_for_include
|
79
105
|
case store_in
|
80
106
|
when 'redis'
|
@@ -2,11 +2,15 @@ require 'rails/generators/base'
|
|
2
2
|
|
3
3
|
module BucketMaker
|
4
4
|
module Generators
|
5
|
+
# Installs the initializer
|
6
|
+
#
|
5
7
|
class InstallGenerator < Rails::Generators::Base
|
6
8
|
source_root File.expand_path("../../templates", __FILE__)
|
7
9
|
|
8
10
|
desc "Creates a BucketMaker initializer"
|
9
11
|
|
12
|
+
# Copies the initializer bucket_maker from the templates and pastes
|
13
|
+
#
|
10
14
|
def copy_initializer
|
11
15
|
template "bucket_maker.rb", "config/initializers/bucket_maker.rb"
|
12
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bucket_maker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dinesh Vasudevan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -337,3 +337,4 @@ test_files:
|
|
337
337
|
- spec/unit/bucketable_spec.rb
|
338
338
|
- spec/unit/series_maker_spec.rb
|
339
339
|
- spec/unit/series_spec.rb
|
340
|
+
has_rdoc:
|