subly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ bin/*
6
+ .idea/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create ruby-1.8.7@subly
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in subly.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 SmashTank Apps, LLC
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.
@@ -0,0 +1,35 @@
1
+ = Subly
2
+
3
+ This gem provides the ability to add subscriptions to models that is controlled from an external app
4
+
5
+ == Usage
6
+ subly accepts a single optional parameter of :is_methods
7
+ class Item < ActiveRecord::Base
8
+ subly :is_methods => [:premium, :standard]
9
+ end
10
+
11
+ == Migration
12
+ See the last todo, this needs to me a generator/rake task but I am tired and want to goto sleep
13
+ create_table :subly_models do |t|
14
+ t.string :subscriber_type
15
+ t.integer :subscriber_id
16
+ t.string :name
17
+ t.string :value
18
+ t.datetime :starts_at
19
+ t.datetime :ends_at
20
+ end
21
+ add_index :subly_models, [:subscriber_type,:subscriber_id], :name => 'subscriber_idx'
22
+ add_index :subly_models, :starts_at, :name => 'starts_idx'
23
+ add_index :subly_models, :ends_at, :name => 'ends_idx'
24
+
25
+ == Todo
26
+ * maybe add a controller
27
+ * do something with the value field, I think it useful but can't think of a use case yet.
28
+ * make the doco usefull
29
+ * make a generator for rails 2 and 3
30
+
31
+ == Copyright
32
+
33
+ Copyright (c) 2011 SmashTank Apps, LLC. See LICENSE.txt for
34
+ further details.
35
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,57 @@
1
+ require "subly/version"
2
+ require "subly/model"
3
+
4
+ module Subly
5
+ def subly(args = {})
6
+ self.has_many :sublies, :as => :subscriber, :class_name => 'Subly::Model'
7
+ self.accepts_nested_attributes_for :sublies, :allow_destroy => true, :reject_if => :all_blank
8
+
9
+ #we don't want to use method missing for all "is" methods
10
+ if is_methods = args.delete(:is_methods)
11
+ is_methods.collect(&:to_s).each do |is_name|
12
+ if self.instance_methods.include?(is_name)
13
+ warn("Subly: Method is_#{is_name}? is already available to #{self.class.to_s}")
14
+ else
15
+ self.class_eval <<-EOV
16
+ def is_#{is_name}?
17
+ self.has_active_subscription?('#{is_name}')
18
+ end
19
+ EOV
20
+ end
21
+ end
22
+ end
23
+
24
+ extend ClassMethods
25
+ include InstanceMethods
26
+ end
27
+
28
+ module ClassMethods
29
+ def with_subscription(name)
30
+ scoped(:include => :sublies, :conditions => {:subly_models => {:name => name}})
31
+ end
32
+
33
+ def with_active_subscription(name)
34
+ now = Time.now
35
+ with_subscription(name).scoped(:joins => :sublies, :conditions => ['subly_models.starts_at <= ? AND (ends_at IS NULL or ends_at > ?)', now, now])
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+ def add_subscription(name, args = {})
41
+ value = args[:value] || nil
42
+ start_date = args[:start_date] || Time.now
43
+ end_date = args[:end_date] || nil
44
+ self.sublies.create(:name => name, :value => value, :starts_at => start_date, :ends_at => end_date)
45
+ end
46
+
47
+ def has_subscription?(name)
48
+ self.sublies.by_name(name).count > 0
49
+ end
50
+
51
+ def has_active_subscription?(name)
52
+ self.sublies.active.by_name(name).count > 0
53
+ end
54
+ end
55
+ end
56
+
57
+ ActiveRecord::Base.send :extend, Subly
@@ -0,0 +1,37 @@
1
+ module Subly
2
+ class Model < ActiveRecord::Base
3
+ set_table_name 'subly_models'
4
+
5
+ belongs_to :subscriber, :polymorphic => true
6
+
7
+ validates_presence_of :subscriber_type
8
+ validates_presence_of :subscriber_id, :unless => Proc.new { |subly| subly.subscriber_type.nil? }
9
+ validate :ends_after_starts
10
+
11
+ def self.active
12
+ now = Time.now
13
+ scoped(:conditions => ['starts_at <= ? AND (ends_at > ? OR ends_at IS NULL)', now, now])
14
+ end
15
+
16
+ def self.expired
17
+ now = Time.now
18
+ scoped(:conditions => ['starts_at <= ? AND ends_at <= ?', now, now])
19
+ end
20
+
21
+ def self.for_subscriber(sub)
22
+ raise ArgumentError('wrong number of arguments (0 for 1)') if sub.blank?
23
+ scoped(:conditions => ['subscriber_type = ? AND subscriber_id = ?',sub.class.to_s, sub.id])
24
+ end
25
+
26
+ def self.by_name(name)
27
+ scoped(:conditions => {:name => name})
28
+ end
29
+
30
+ private
31
+ def ends_after_starts
32
+ unless ends_at.nil? || (ends_at > starts_at)
33
+ raise "ends_at must be after starts_at"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Subly
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,43 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'active_record'
4
+ require 'rspec'
5
+ require 'subly'
6
+ require 'sqlite3'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
+
12
+ #ActiveRecord::Schema.verbose = false
13
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
14
+
15
+ ActiveRecord::Base.configurations = true
16
+ ActiveRecord::Schema.define(:version => 1) do
17
+ create_table :items do |t|
18
+ t.datetime :created_at
19
+ t.datetime :updated_at
20
+ t.string :name
21
+ t.string :description
22
+ end
23
+
24
+ create_table :things do |t|
25
+ t.datetime :created_at
26
+ t.datetime :updated_at
27
+ t.string :name
28
+ t.string :description
29
+ end
30
+
31
+ create_table :subly_models do |t|
32
+ t.datetime :archived_at
33
+ t.string :subscriber_type
34
+ t.integer :subscriber_id
35
+ t.string :name
36
+ t.string :value
37
+ t.datetime :starts_at
38
+ t.datetime :ends_at
39
+ end
40
+ add_index :subly_models, [:subscriber_type,:subscriber_id], :name => 'subscriber_idx'
41
+ add_index :subly_models, :starts_at, :name => 'starts_idx'
42
+ add_index :subly_models, :ends_at, :name => 'ends_idx'
43
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class Item < ActiveRecord::Base
4
+ subly
5
+ end
6
+
7
+ class Thing < ActiveRecord::Base
8
+ subly
9
+ end
10
+
11
+ describe Subly::Model do
12
+ it "should not create a subscription without a subscriber_id" do
13
+ lambda{Subly::Model.create!(:name => 'Foo', :subscriber_type => 'Boo')}.should raise_error
14
+ end
15
+
16
+ it "should not create a subscription without a subscriber" do
17
+ lambda{Subly::Model.create!(:name => 'Foo', :subscriber_id => 3)}.should raise_error
18
+ end
19
+
20
+ it "subscription should not be created when ends before starts" do
21
+ lambda{Subly::Model.create!(:name => 'Foo', :subscriber_type => 'Foo', :subscriber_id => 1, :starts_at => Time.now + 1.minute, :ends_at => Time.now - 1.minute)}.should raise_error
22
+ end
23
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class Item < ActiveRecord::Base
4
+ subly :is_methods => [:subby]
5
+ end
6
+
7
+ class Thing < ActiveRecord::Base
8
+ subly
9
+ end
10
+
11
+ describe Subly do
12
+ it "should add a subscription" do
13
+ thing_one = Thing.create(:name => 'Thing One', :description => 'foo')
14
+ thing_one.add_subscription('sub name','sub value').should be_true
15
+ thing_one.reload
16
+ thing_one.sublies.count.should == 1
17
+ end
18
+
19
+ it "subscription should default to active" do
20
+ thing_one = Thing.create(:name => 'Thing One', :description => 'foo')
21
+ thing_one.add_subscription('sub name').should be_true
22
+ thing_one.reload
23
+ thing_one.has_active_subscription?('sub name').should be_true
24
+ end
25
+
26
+ it "has_subscription should return true even for expired subscription" do
27
+ thing_one = Thing.create(:name => 'Thing One', :description => 'foo')
28
+ thing_one.add_subscription('sub name', :start_date => Time.now - 1.day, :end_date => Time.now - 2.hours).should be_true
29
+ thing_one.reload
30
+ thing_one.has_subscription?('sub name').should be_true
31
+ end
32
+
33
+
34
+ it "should find models with active subscription" do
35
+ Thing.delete_all
36
+ thing_one = Thing.create(:name => 'Thing One', :description => 'foo')
37
+ thing_one.add_subscription('sub name').should be_true
38
+ Thing.with_active_subscription('sub name').should == [thing_one]
39
+ end
40
+
41
+ it "should find models with even expired subscriptions" do
42
+ Thing.delete_all
43
+ thing_one = Thing.create(:name => 'Thing One', :description => 'foo')
44
+ thing_one.add_subscription('sub name', :start_date => Time.now - 1.day, :end_date => Time.now - 2.hours).should be_true
45
+ Thing.with_subscription('sub name').should == [thing_one]
46
+ thing_one.destroy
47
+ end
48
+
49
+ it "should define methods for is" do
50
+ Item.instance_method(:is_subby?).should be_true
51
+ end
52
+
53
+ it "is method should be false if it does not have an active sub" do
54
+ Item.new.is_subby?.should be_false
55
+ end
56
+
57
+ it "is method should be true if it has an active sub" do
58
+ item = Item.create(:name => 'foo')
59
+ item.add_subscription('subby')
60
+ item.is_subby?.should be_true
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "subly/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "subly"
7
+ s.version = Subly::VERSION
8
+ s.authors = ['SmashTank Apps, LLC','Eric Harrison']
9
+ s.email = ['dev@smashtankapps.com']
10
+ s.homepage = "http://github.com/smashtank/subly"
11
+ s.summary = %q{Add subscriptions to a model that are controlled externally}
12
+ s.description = %q{This was purpose built to extend models with subscriptions that were controlled via SalesForce}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_paths = ["lib"]
17
+
18
+ s.required_rubygems_version = ">= 1.3.6"
19
+ s.add_development_dependency "activerecord", "~> 2.3.12"
20
+ s.add_development_dependency "rspec"
21
+ s.add_development_dependency "sqlite3"
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - SmashTank Apps, LLC
9
+ - Eric Harrison
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-12-02 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ requirement: &2153605660 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 2.3.12
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2153605660
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &2153605280 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2153605280
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3
39
+ requirement: &2153604820 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2153604820
48
+ description: This was purpose built to extend models with subscriptions that were
49
+ controlled via SalesForce
50
+ email:
51
+ - dev@smashtankapps.com
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - .rvmrc
58
+ - Gemfile
59
+ - LICENSE.txt
60
+ - README.rdoc
61
+ - Rakefile
62
+ - lib/subly.rb
63
+ - lib/subly/model.rb
64
+ - lib/subly/version.rb
65
+ - spec/spec_helper.rb
66
+ - spec/subly_model_spec.rb
67
+ - spec/subly_spec.rb
68
+ - subly.gemspec
69
+ homepage: http://github.com/smashtank/subly
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: 1.3.6
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.10
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Add subscriptions to a model that are controlled externally
93
+ test_files:
94
+ - spec/spec_helper.rb
95
+ - spec/subly_model_spec.rb
96
+ - spec/subly_spec.rb