subly 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.
- data/.gitignore +6 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +1 -0
- data/lib/subly.rb +57 -0
- data/lib/subly/model.rb +37 -0
- data/lib/subly/version.rb +3 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/subly_model_spec.rb +23 -0
- data/spec/subly_spec.rb +62 -0
- data/subly.gemspec +23 -0
- metadata +96 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create ruby-1.8.7@subly
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.rdoc
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/subly.rb
ADDED
@@ -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
|
data/lib/subly/model.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
data/spec/subly_spec.rb
ADDED
@@ -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
|
data/subly.gemspec
ADDED
@@ -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
|