bucket_maker 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 (71) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +8 -0
  4. data/Appraisals +11 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +115 -0
  8. data/Rakefile +17 -0
  9. data/app/controllers/bucket_maker_controller.rb +32 -0
  10. data/app/controllers/concerns/bucket_maker_concern.rb +23 -0
  11. data/bucket_maker.gemspec +37 -0
  12. data/config/routes.rb +14 -0
  13. data/lib/bucket_maker.rb +8 -0
  14. data/lib/bucket_maker/bucket.rb +65 -0
  15. data/lib/bucket_maker/configuration.rb +66 -0
  16. data/lib/bucket_maker/engine.rb +13 -0
  17. data/lib/bucket_maker/models/active_recordable.rb +35 -0
  18. data/lib/bucket_maker/models/bucketable.rb +92 -0
  19. data/lib/bucket_maker/models/redisable.rb +59 -0
  20. data/lib/bucket_maker/series.rb +48 -0
  21. data/lib/bucket_maker/series_maker.rb +80 -0
  22. data/lib/bucket_maker/version.rb +3 -0
  23. data/lib/generators/active_record/bucket_maker_generator.rb +90 -0
  24. data/lib/generators/bucket_maker/bucket_maker_generator.rb +15 -0
  25. data/lib/generators/bucket_maker/install_generator.rb +16 -0
  26. data/lib/generators/templates/active_recordable_migration.rb +12 -0
  27. data/lib/generators/templates/bucket_maker.rb +41 -0
  28. data/spec/controllers/bucket_maker_controller_spec.rb +112 -0
  29. data/spec/dummy/Rakefile +7 -0
  30. data/spec/dummy/app/assets/images/.keep +0 -0
  31. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  32. data/spec/dummy/app/assets/javascripts/welcome.js.coffee +3 -0
  33. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  34. data/spec/dummy/app/assets/stylesheets/welcome.css.scss +3 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +12 -0
  36. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  37. data/spec/dummy/app/controllers/welcome_controller.rb +4 -0
  38. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  39. data/spec/dummy/app/helpers/welcome_helper.rb +2 -0
  40. data/spec/dummy/app/models/.keep +0 -0
  41. data/spec/dummy/app/models/concerns/.keep +0 -0
  42. data/spec/dummy/app/models/user.rb +12 -0
  43. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  44. data/spec/dummy/app/views/welcome/index.html.erb +2 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/config/application.rb +16 -0
  47. data/spec/dummy/config/boot.rb +9 -0
  48. data/spec/dummy/config/buckets.yml +35 -0
  49. data/spec/dummy/config/database.yml +6 -0
  50. data/spec/dummy/config/environment.rb +5 -0
  51. data/spec/dummy/config/environments/test.rb +31 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/bucket_maker.rb +41 -0
  54. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/spec/dummy/config/initializers/inflections.rb +16 -0
  56. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  57. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  58. data/spec/dummy/config/initializers/session_store.rb +3 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +23 -0
  61. data/spec/dummy/config/routes.rb +2 -0
  62. data/spec/dummy/db/migrate/20131223144333_create_users.rb +9 -0
  63. data/spec/dummy/db/test.sqlite3 +0 -0
  64. data/spec/factories/factories.rb +8 -0
  65. data/spec/routing/routes_spec.rb +81 -0
  66. data/spec/spec_helper.rb +24 -0
  67. data/spec/unit/bucket_spec.rb +57 -0
  68. data/spec/unit/bucketable_spec.rb +100 -0
  69. data/spec/unit/series_maker_spec.rb +91 -0
  70. data/spec/unit/series_spec.rb +64 -0
  71. metadata +339 -0
@@ -0,0 +1,41 @@
1
+ # Configure the BucketMaker
2
+ BucketMaker.configure do |config|
3
+ # Path Prefix
4
+ # All the Series, Buckets and Groups can be viewed/edited via a special URI
5
+ # The path prefix is used to prefix these routes
6
+ #
7
+ # Default value - '2bOrNot2B/'
8
+ # config.path_prefix = '2bOrNot2B/'
9
+ #
10
+ # Redis Options
11
+ # Set the Redis options which can be host, port and db
12
+ # This takes effect when in module included
13
+ # in the ActiveModel is BucketMaker::Models::Redisable
14
+ #
15
+ # Default value - { host: 'localhost', port: 6379, db: 1 }
16
+ # config.redis_options = {
17
+ # host: 'localhost',
18
+ # port: 6379,
19
+ # db: 1
20
+ # }
21
+ #
22
+ # Buckets Config File
23
+ # The configuration file for adding/removing Series, Buckets and Groups
24
+ #
25
+ config.buckets_config_file = 'config/buckets.yml'
26
+ #
27
+ # Lazy Load
28
+ # If false then Bucketize the object on creation
29
+ # If true then the Bucketization happens on the first call of in_bucket?
30
+ #
31
+ # Default value - true
32
+ # config.lazy_load = true
33
+ #
34
+ # Redis Expiration Time
35
+ # The redis expiration time if the configuration is for Redis
36
+ #
37
+ # Default value - 12.months
38
+ # config.redis_expiration_time = 12.months
39
+ #
40
+ end
41
+
@@ -0,0 +1,4 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Configure sensitive parameters which will be filtered from the log file.
4
+ Rails.application.config.filter_parameters += [:password]
@@ -0,0 +1,16 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format. Inflections
4
+ # are locale specific, and you may define rules for as many different
5
+ # locales as you wish. All of these examples are active by default:
6
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ # inflect.plural /^(ox)$/i, '\1en'
8
+ # inflect.singular /^(ox)en/i, '\1'
9
+ # inflect.irregular 'person', 'people'
10
+ # inflect.uncountable %w( fish sheep )
11
+ # end
12
+
13
+ # These inflection rules are supported but not enabled by default:
14
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
15
+ # inflect.acronym 'RESTful'
16
+ # end
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf
5
+ # Mime::Type.register_alias "text/html", :iphone
@@ -0,0 +1,12 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key is used for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+
6
+ # Make sure the secret is at least 30 characters and all random,
7
+ # no regular words or you'll be exposed to dictionary attacks.
8
+ # You can use `rake secret` to generate a secure secret key.
9
+
10
+ # Make sure your secret_key_base is kept private
11
+ # if you're sharing your code publicly.
12
+ Dummy::Application.config.secret_key_base = '26ea46774c38ecbb8fa7ca2ab6b102c3bde865884ab51460a21d5d91164681d5a22013ccaa65cc3130f59896db68cc62bbefd164a8ea143866b4e4de047cc41e'
@@ -0,0 +1,3 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Dummy::Application.config.session_store :cookie_store, key: '_usettings_test_session'
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9
+ end
10
+
11
+ # To enable root element in JSON for ActiveRecord objects.
12
+ # ActiveSupport.on_load(:active_record) do
13
+ # self.include_root_in_json = true
14
+ # end
@@ -0,0 +1,23 @@
1
+ # Files in the config/locales directory are used for internationalization
2
+ # and are automatically loaded by Rails. If you want to use locales other
3
+ # than English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t 'hello'
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t('hello') %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # To learn more, please read the Rails Internationalization guide
20
+ # available at http://guides.rubyonrails.org/i18n.html.
21
+
22
+ en:
23
+ hello: "Hello world"
@@ -0,0 +1,2 @@
1
+ Dummy::Application.routes.draw do
2
+ end
@@ -0,0 +1,9 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :email
5
+ t.string :uuid
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
Binary file
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ id "12345"
4
+ email "test@test.com"
5
+ uuid "YGSHJKBEKJWKJHJKXNJKLDNWN"
6
+ created_at DateTime.parse('1st Jan 2011')
7
+ end
8
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Routes' do
4
+ context 'with default routes' do
5
+ it 'should have get bucket route' do
6
+ expect(get: '/2bOrNot2B/test_series_one/actual_test_one/group_one.json').to route_to(
7
+ controller: 'bucket_maker',
8
+ action: 'show',
9
+ series_name: 'test_series_one',
10
+ bucket_name: 'actual_test_one',
11
+ group_name: 'group_one',
12
+ format: 'json'
13
+ )
14
+ end
15
+
16
+ it 'should have force set bucket route' do
17
+ expect(post: '/2bOrNot2B/test_series_one/actual_test_one/group_one.json').to route_to(
18
+ controller: 'bucket_maker',
19
+ action: 'switch',
20
+ series_name: 'test_series_one',
21
+ bucket_name: 'actual_test_one',
22
+ group_name: 'group_one',
23
+ format: 'json'
24
+ )
25
+ end
26
+
27
+ it 'should have randomize bucket route' do
28
+ expect(post: '/2bOrNot2B/test_series_one/actual_test_one.json').to route_to(
29
+ controller: 'bucket_maker',
30
+ action: 'randomize',
31
+ series_name: 'test_series_one',
32
+ bucket_name: 'actual_test_one',
33
+ format: 'json'
34
+ )
35
+ end
36
+ end
37
+
38
+ context 'with special prefix' do
39
+ around do |example|
40
+ default_base_path = BucketMaker.configuration.path_prefix
41
+ BucketMaker.configuration.path_prefix = 'non_default_path'
42
+
43
+ Rails.application.reload_routes!
44
+
45
+ BucketMaker.configuration.path_prefix = default_base_path
46
+ Rails.application.reload_routes!
47
+ end
48
+
49
+ it 'should have get bucket route' do
50
+ expect(get: '/non_default_path/test_series_one/actual_test_one/group_one.json').to route_to(
51
+ controller: 'bucket_maker',
52
+ action: 'show',
53
+ series_name: 'test_series_one',
54
+ bucket_name: 'actual_test_one',
55
+ group_name: 'group_one',
56
+ format: 'json'
57
+ )
58
+ end
59
+
60
+ it 'should have force set bucket route' do
61
+ expect(post: '/non_default_path/test_series_one/actual_test_one/group_one.json').to route_to(
62
+ controller: 'bucket_maker',
63
+ action: 'switch',
64
+ series_name: 'test_series_one',
65
+ bucket_name: 'actual_test_one',
66
+ group_name: 'group_one',
67
+ format: 'json'
68
+ )
69
+ end
70
+
71
+ it 'should have randomize bucket route' do
72
+ expect(post: '/non_default_path/test_series_one/actual_test_one.json').to route_to(
73
+ controller: 'bucket_maker',
74
+ action: 'randomize',
75
+ series_name: 'test_series_one',
76
+ bucket_name: 'actual_test_one',
77
+ format: 'json'
78
+ )
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,24 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
4
+ require 'pry'
5
+ require 'rails/test_help'
6
+ require 'rspec/rails'
7
+ require 'capybara/rails'
8
+ require 'factory_girl_rails'
9
+
10
+ require 'coveralls'
11
+ Coveralls.wear!
12
+
13
+ Rails.backtrace_cleaner.remove_silencers!
14
+ Capybara.default_driver = :rack_test
15
+ Capybara.default_selector = :css
16
+
17
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|f| require f}
18
+
19
+ RSpec.configure do |config|
20
+ require 'rspec/expectations'
21
+ config.include RSpec::Matchers
22
+ config.mock_with :rspec
23
+ config.include FactoryGirl::Syntax::Methods
24
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe BucketMaker::Bucket do
4
+ let(:bucket_options) do
5
+ {
6
+ 'description' => 'Test One',
7
+ 'created_after' => "2nd Jan 2010",
8
+ 'distributions' => {
9
+ 'group_one' => 0.3,
10
+ 'group_two' => 0.5,
11
+ 'group_three' => 0.2,
12
+ }
13
+ }
14
+ end
15
+
16
+ let(:bucket) { BucketMaker::Bucket.new('test_bucket', bucket_options) }
17
+
18
+ context 'for configuration' do
19
+ it 'should have the correct distributions' do
20
+ bucket.distributions[:group_one].should == 0.3
21
+ bucket.distributions[:group_two].should == 0.5
22
+ bucket.distributions[:group_three].should == 0.2
23
+ end
24
+
25
+ context 'and distributions percent' do
26
+ before { bucket.random_group }
27
+
28
+ it 'should have the correct distribution percentages' do
29
+ bucket.distributions_percent[:group_one].should == (0..30)
30
+ bucket.distributions_percent[:group_two].should == (30..80)
31
+ bucket.distributions_percent[:group_three].should == (80..100)
32
+ end
33
+ end
34
+ end
35
+
36
+ context '#is_bucketable?' do
37
+ it 'should return false if created after doesnt satisfy' do
38
+ user = build(:user, id: 12345, created_at: DateTime.parse('1st Jan 2010'))
39
+ bucket.is_bucketable?(user).should be_false
40
+ end
41
+
42
+ it 'should return true if created after does satisfy' do
43
+ user = build(:user, id: 12345, created_at: DateTime.parse('3rd Jan 2010'))
44
+ bucket.is_bucketable?(user).should be_true
45
+ end
46
+ end
47
+
48
+ context '#has_group?' do
49
+ it 'should return false for non existant group' do
50
+ bucket.has_group?('non_existant_test').should be_false
51
+ end
52
+
53
+ it 'should return true for existing group' do
54
+ bucket.has_group?('group_two').should be_true
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe BucketMaker::Models::Bucketable do
4
+ class DummyClass
5
+ def id; 12345; end
6
+ end
7
+
8
+ let(:dummy) { DummyClass.new }
9
+ let(:existing_series) { 'test_series_one' }
10
+ let(:existing_bucket) { 'actual_test_one' }
11
+ let(:existing_group) { 'group_two' }
12
+
13
+ before do
14
+ dummy.extend BucketMaker::Models::Bucketable
15
+ end
16
+
17
+ context '#group_for_key' do
18
+ it 'should raise exception' do
19
+ expect {
20
+ dummy.send(:group_for_key, 'test_series')
21
+ }.to raise_error
22
+ end
23
+ end
24
+
25
+ context '#set_group_for_key' do
26
+ it 'should raise exception' do
27
+ expect {
28
+ dummy.send(:set_group_for_key, 'test_series', 'test_group')
29
+ }.to raise_error NotImplementedError
30
+ end
31
+ end
32
+
33
+ context 'with stubbed group_for_key and set_group_for_key' do
34
+ before do
35
+ dummy.stub(:group_for_key) { true }
36
+ dummy.stub(:set_group_for_key) { true }
37
+ dummy.stub(:has_attribute?) { true }
38
+ dummy.stub(:created_at) { DateTime.parse('1st Jan 2010') }
39
+ end
40
+
41
+ context '#in_bucket?' do
42
+ context 'with invalid params' do
43
+ it 'should return false' do
44
+ dummy.in_bucket?('non_existant_series', existing_bucket, existing_group).should be_false
45
+ end
46
+ end
47
+
48
+ context 'with valid params' do
49
+ it 'should return false' do
50
+ # group_for_key return true and true == existing_group is false
51
+ dummy.in_bucket?(existing_series, existing_bucket, existing_group).should be_false
52
+ end
53
+
54
+ context 'when group_for_key returns the current group' do
55
+ before do
56
+ dummy.stub(:group_for_key).with(anything()) { existing_group }
57
+ end
58
+
59
+ it 'should return true' do
60
+ dummy.in_bucket?(existing_series, existing_bucket, existing_group).should be_true
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ context '#force_to_bucket!' do
67
+ context 'with invalid params' do
68
+ it 'should return false' do
69
+ dummy.force_to_bucket!('non_existant_series', existing_bucket, existing_group).should be_false
70
+ end
71
+ end
72
+
73
+ context 'with valid params' do
74
+ it 'should return true' do
75
+ dummy.force_to_bucket!(existing_series, existing_bucket, existing_group).should be_true
76
+ end
77
+ end
78
+ end
79
+
80
+ context '#bucketize_for_series_and_bucket!' do
81
+ context 'with invalid params' do
82
+ it 'should return false' do
83
+ dummy.bucketize_for_series_and_bucket!('non_existant_series', existing_bucket).should be_false
84
+ end
85
+ end
86
+
87
+ context 'with valid params' do
88
+ it 'should return true' do
89
+ dummy.bucketize_for_series_and_bucket!(existing_series, existing_bucket).should be_true
90
+ end
91
+ end
92
+ end
93
+
94
+ context '#bucketize!' do
95
+ it 'should return true' do
96
+ dummy.bucketize!.should be_true
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe BucketMaker::SeriesMaker do
4
+ let(:bucket_maker) { BucketMaker::SeriesMaker.instance }
5
+ let(:user) { build(:user, id: 12345) }
6
+ let(:existing_series) { 'test_series_one' }
7
+ let(:existing_bucket) { 'actual_test_one' }
8
+ let(:existing_group) { 'group_two' }
9
+
10
+ before do
11
+ BucketMaker::Bucket.any_instance.stub(:random_group) { existing_group }
12
+ end
13
+
14
+ context 'for configuration' do
15
+ it 'should have correct number of series' do
16
+ bucket_maker.series.keys.length.should == 2 # Look at config/buckets.yml
17
+ end
18
+
19
+ it 'should have correct number of buckets' do
20
+ bucket_maker.series_with_name(existing_series).buckets.keys.length.should == 2
21
+ end
22
+
23
+ it 'should have correct number of distribut' do
24
+ bucket_maker.series_with_name(existing_series).bucket_with_name(existing_bucket).distributions.keys.length.should == 3
25
+ end
26
+ end
27
+
28
+ context '#configured?' do
29
+ it 'should be configured' do
30
+ bucket_maker.configured?.should be_true
31
+ end
32
+ end
33
+
34
+ context '#key_for_series' do
35
+ it 'should return the correct key' do
36
+ bucket_maker.key_for_series(user, 'series_one', 'bucket_one').should == 'bucket_maker:user_12345:series_one:bucket_one'
37
+ end
38
+ end
39
+
40
+ context '#has_series?' do
41
+ it 'should return true for an existing series' do
42
+ bucket_maker.has_series?(existing_series).should be_true
43
+ end
44
+
45
+ it 'should return false for a non existing series' do
46
+ bucket_maker.has_series?('non_existing_series').should be_false
47
+ end
48
+ end
49
+
50
+ context '#series_with_name' do
51
+ it 'should return a Series object' do
52
+ bucket_maker.series_with_name(existing_series).should be_kind_of(BucketMaker::Series)
53
+ end
54
+ end
55
+
56
+ context '#has_bucket_in_series?' do
57
+ it 'should return true for an existing bucket' do
58
+ bucket_maker.has_bucket_in_series?(existing_series, existing_bucket).should be_true
59
+ end
60
+
61
+ it 'should return false for a non existing bucket' do
62
+ bucket_maker.has_bucket_in_series?('non_existing_series', existing_bucket).should be_false
63
+ end
64
+ end
65
+
66
+ context '#has_group_in_bucket_in_series?' do
67
+ it 'should return true for an existing group' do
68
+ bucket_maker.has_group_in_bucket_in_series?(existing_series, existing_bucket, existing_group).should be_true
69
+ end
70
+
71
+ it 'should return false for a non existing group' do
72
+ bucket_maker.has_group_in_bucket_in_series?('non_existing_series', existing_bucket, existing_group).should be_false
73
+ end
74
+ end
75
+
76
+ context '#bucketize' do
77
+ it 'should return a valid group' do
78
+ bucket_maker.bucketize(existing_series, existing_bucket).should == existing_group
79
+ end
80
+ end
81
+
82
+ context '#bucketable?' do
83
+ it 'should return true for valid params' do
84
+ bucket_maker.bucketable?(user, existing_series, existing_bucket, existing_group).should be_true
85
+ end
86
+
87
+ it 'should return false for invalid params' do
88
+ bucket_maker.bucketable?(user, 'non_existing_series', existing_bucket, existing_group).should be_false
89
+ end
90
+ end
91
+ end