acts_as_featureable 0.0.2 → 0.0.3
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/.gitignore +3 -0
- data/CHANGELOG.md +6 -2
- data/Rakefile +2 -2
- data/acts_as_featureable.gemspec +19 -19
- data/app/controllers/features_controller.rb +88 -88
- data/app/helpers/features_helper.rb +7 -7
- data/app/models/feature.rb +1 -74
- data/app/views/features/_feature.html.erb +3 -3
- data/app/views/features/_features.html.erb +1 -1
- data/app/views/features/_form.html.erb +15 -15
- data/app/views/features/index.html.erb +1 -1
- data/lib/acts_as_featureable.rb +2 -2
- data/lib/acts_as_featureable/config.rb +28 -28
- data/lib/acts_as_featureable/engine.rb +7 -7
- data/lib/acts_as_featureable/feature_additions.rb +96 -0
- data/lib/acts_as_featureable/featureable_additions.rb +17 -0
- data/lib/acts_as_featureable/version.rb +1 -1
- data/lib/generators/features_generator.rb +12 -12
- data/lib/generators/templates/create_features.rb +12 -12
- data/lib/generators/templates/initializer.rb +13 -13
- data/spec/extra/model.rb +1 -1
- data/spec/extra/schema.rb +15 -15
- data/spec/factories/topics.rb +4 -4
- data/spec/helpers/features_helper_spec.rb +20 -20
- data/spec/models/feature_spec.rb +122 -127
- data/spec/models/featureable_spec.rb +4 -4
- data/spec/spec_helper.rb +6 -6
- metadata +4 -6
- data/lib/acts_as_featureable/feature_methods.rb +0 -20
- data/lib/acts_as_featureable/featureable_methods.rb +0 -17
- data/spec/dummy/log/test.log +0 -34618
@@ -1,9 +1,9 @@
|
|
1
1
|
module ActsAsFeatureable
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
config.generators do |g|
|
4
|
+
g.test_framework :rspec, :fixture => false
|
5
|
+
g.assets false
|
6
|
+
g.helper false
|
7
|
+
end
|
8
|
+
end
|
9
9
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ActsAsFeatureable
|
2
|
+
module FeatureAdditions
|
3
|
+
def self.included(feature_model)
|
4
|
+
feature_model.belongs_to :featureable, polymorphic: true
|
5
|
+
|
6
|
+
feature_model.scope :ordered, ->{ feature_model.order('position ASC') }
|
7
|
+
|
8
|
+
feature_model.before_validation :assign_title, :assign_summary, :assign_position
|
9
|
+
|
10
|
+
feature_model.validate :feature_limit_not_reached, :ensure_categories
|
11
|
+
|
12
|
+
feature_model.validates_presence_of :featureable_id, :featureable_type
|
13
|
+
|
14
|
+
feature_model.validates_numericality_of :position
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def ensure_categories
|
19
|
+
cats = ::ActsAsFeatureable::categories
|
20
|
+
# Allow all categories if set to false
|
21
|
+
if cats && self.category && !cats.include?(self.category.to_sym)
|
22
|
+
errors.add(:category, " is not in the list [#{cats.join(',')}]")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def feature_limit_not_reached
|
27
|
+
limit = ActsAsFeatureable.feature_limit
|
28
|
+
errors.add(:base, %q{
|
29
|
+
The feature limit of #{limit} has been reached. \
|
30
|
+
Please delete or change an existing feature.
|
31
|
+
}) if ActsAsFeatureable.categories ? Feature.where(category: self.category).count >= limit : Feature.count >= limit
|
32
|
+
end
|
33
|
+
|
34
|
+
def assign_title
|
35
|
+
# If there is no title given, check the model.
|
36
|
+
# Will check for the attributes in ActsAsFeatureable.auto_title_assign_list.
|
37
|
+
unless self.title
|
38
|
+
ActsAsFeatureable.auto_title_assign_list.each do |attr|
|
39
|
+
if featureable.respond_to?(attr)
|
40
|
+
self.title = featureable.send(attr)
|
41
|
+
break
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def assign_summary
|
48
|
+
# If there is no summary given, check the model.
|
49
|
+
# Will check for the attributes in ActsAsFeatureable.auto_summary_assign_list.
|
50
|
+
unless self.summary
|
51
|
+
ActsAsFeatureable.auto_summary_assign_list.each do |attr|
|
52
|
+
if featureable.respond_to?(attr)
|
53
|
+
self.summary = featureable.send(attr)
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def assign_position
|
61
|
+
# If there is no position given, or the position given is already taken,
|
62
|
+
# assign the lowest open position. Otherwise assign it.
|
63
|
+
all_positions = if ActsAsFeatureable.categories
|
64
|
+
Feature.select('features.position, features.category').where(category: self.category).map(&:position).sort
|
65
|
+
else
|
66
|
+
Feature.select('features.position').map(&:position).sort
|
67
|
+
end
|
68
|
+
|
69
|
+
if !self.position || (self.position && all_positions.include?(self.position))
|
70
|
+
# Find lowest, non-taken position
|
71
|
+
(1..ActsAsFeatureable.feature_limit).each do |n|
|
72
|
+
next if all_positions.include? n
|
73
|
+
self.position = n
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def feature_model.method_missing(mehtod_name, *args, &block)
|
80
|
+
if ActsAsFeatureable.categories &&
|
81
|
+
ActsAsFeatureable.categories.include?(mehtod_name)
|
82
|
+
self.scope mehtod_name, -> { self.where(category: mehtod_name) }
|
83
|
+
self.send(mehtod_name)
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def respond_to_missing?(method_name, include_private = false)
|
90
|
+
(ActsAsFeatureable.categories &&
|
91
|
+
ActsAsFeatureable.categories.include?(method_name)) ||
|
92
|
+
super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Acts
|
3
|
+
module FeatureableAdditions
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def acts_as_featureable
|
10
|
+
has_many :features, :as => :featureable, :dependent => :destroy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Acts::FeatureableAdditions)
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'rails/generators/migration'
|
2
2
|
|
3
3
|
class FeaturesGenerator < Rails::Generators::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def self.next_migration_number(path)
|
9
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_featureable_file
|
13
|
+
copy_file "initializer.rb", "config/initializers/acts_as_featureable.rb"
|
14
|
+
migration_template "create_features.rb", "db/migrate/create_features.rb"
|
15
|
+
end
|
16
16
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
class CreateFeatures < ActiveRecord::Migration
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
2
|
+
def change
|
3
|
+
create_table :features do |t|
|
4
|
+
t.references :featureable, :polymorphic => true
|
5
|
+
t.string :title
|
6
|
+
t.text :summary
|
7
|
+
t.integer :position, :null => false
|
8
|
+
t.string :category
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :features, [:featureable_type, :featureable_id]
|
13
|
+
end
|
14
14
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
ActsAsFeatureable.setup do |config|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
2
|
+
# Set the maximum limit of features availible for your app.
|
3
|
+
config.feature_limit = 10
|
4
|
+
|
5
|
+
# Set the order of auto title assigning.
|
6
|
+
config.auto_title_assign_list = [:title, :name]
|
7
|
+
|
8
|
+
# Set the order of auto summary assigning.
|
9
|
+
config.auto_summary_assign_list = [:summary, :caption, :tldr, :content, :text]
|
10
|
+
|
11
|
+
# Limit the categories you wish to use.
|
12
|
+
# => [:main, :articles, :images]
|
13
|
+
# false allows unlimited categories.
|
14
|
+
config.categories = false
|
15
15
|
end
|
data/spec/extra/model.rb
CHANGED
data/spec/extra/schema.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
ActiveRecord::Schema.define :version => 0 do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
2
|
+
create_table :features do |t|
|
3
|
+
t.references :featureable, :polymorphic => true
|
4
|
+
t.string :title
|
5
|
+
t.text :summary
|
6
|
+
t.integer :position, :null => false
|
7
|
+
t.string :category
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :features, [:featureable_type, :featureable_id]
|
12
|
+
|
13
|
+
create_table :topics do |t|
|
14
|
+
t.string :title
|
15
|
+
t.text :summary
|
16
|
+
end
|
17
17
|
end
|
data/spec/factories/topics.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe FeaturesHelper do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
4
|
+
before :each do
|
5
|
+
@topic = Topic.create!
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "features_form_for" do
|
9
|
+
it "should render the form for a new feature" do
|
10
|
+
helper.feature_form_for(@topic).should =~ /id="new_feature"/
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "features_for" do
|
15
|
+
it "should be empty if there are no features for the featureable" do
|
16
|
+
helper.features_for(@topic).should be_blank
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should render a list of features" do
|
20
|
+
@topic.features.create!
|
21
|
+
helper.features_for(@topic).should =~ /class="feature"/
|
22
|
+
end
|
23
|
+
end
|
24
24
|
end
|
data/spec/models/feature_spec.rb
CHANGED
@@ -1,131 +1,126 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Feature do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
it "should assign the next position in line if it already exists" do
|
127
|
-
subject
|
128
|
-
@topic.features.create!(:position => 1).position.should eql 2
|
129
|
-
end
|
4
|
+
before :each do
|
5
|
+
Feature.delete_all
|
6
|
+
@topic = Topic.create!
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { @topic.features.create! }
|
10
|
+
|
11
|
+
it { should be_valid }
|
12
|
+
it { should belong_to :featureable }
|
13
|
+
it { should validate_numericality_of :position }
|
14
|
+
it { should validate_presence_of :featureable_id }
|
15
|
+
it { should validate_presence_of :featureable_type }
|
16
|
+
|
17
|
+
## ENSURE CATEGORIES ##
|
18
|
+
it "should allow any categories by default" do
|
19
|
+
@topic.features.create!(category: 'anything').errors.count.should eql 0
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "with category limits" do
|
23
|
+
before :each do
|
24
|
+
ActsAsFeatureable.categories = [:default]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should allow a category that is in the list" do
|
28
|
+
@topic.features.create(category: :default).errors.count.should eq 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not allow a category that is not in the list" do
|
32
|
+
feature = @topic.features.create(category: :foo)
|
33
|
+
|
34
|
+
feature.errors.count.should eq 1
|
35
|
+
feature.errors.messages.keys.should include(:category)
|
36
|
+
end
|
37
|
+
|
38
|
+
## CREATE SCOPES ##
|
39
|
+
it "should create scopes for the default categories" do
|
40
|
+
Feature.default
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return all the features of the scope for the deafult categories" do
|
44
|
+
@topic.features.create!(category: :default)
|
45
|
+
|
46
|
+
Feature.default.size.should eq 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
## LIMIT VALIDATION ##
|
51
|
+
it "should only allow the default number of features to be made" do
|
52
|
+
# Create the limit of features
|
53
|
+
ActsAsFeatureable.feature_limit.times do
|
54
|
+
@topic.features.create!
|
55
|
+
end
|
56
|
+
|
57
|
+
# Attempt to create an additional feature
|
58
|
+
expect { @topic.features.create! }.to raise_error ActiveRecord::RecordInvalid
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "with category limits" do
|
62
|
+
before :each do
|
63
|
+
ActsAsFeatureable.categories = [:one, :two]
|
64
|
+
|
65
|
+
# Create the limit of features
|
66
|
+
ActsAsFeatureable.feature_limit.times do
|
67
|
+
@topic.features.create!(category: :one)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should only allow the default number of features per category to be made" do
|
72
|
+
# Attempt to create an additional feature
|
73
|
+
expect { @topic.features.create!(category: :one) }.to raise_error ActiveRecord::RecordInvalid
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow more features to be made of other categories when one is full" do
|
77
|
+
expect { @topic.features.create!(category: :two) }.to_not raise_error ActiveRecord::RecordInvalid
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
## TITLE ##
|
82
|
+
describe "with featureable" do
|
83
|
+
before :each do
|
84
|
+
@topic = Topic.create!(:title => 'Title')
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should assign associated model's title" do
|
88
|
+
feature = @topic.features.create!(:featureable => @topic)
|
89
|
+
|
90
|
+
feature.title.should eql @topic.title
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should overwrite associated model's title" do
|
94
|
+
feature = @topic.features.create!(:featureable => @topic, :title => 'New Title')
|
95
|
+
|
96
|
+
feature.title.should eql 'New Title'
|
97
|
+
end
|
98
|
+
|
99
|
+
## SUMMARY ##
|
100
|
+
it "should assign associated model's summary" do
|
101
|
+
feature = @topic.features.create!(:featureable => @topic)
|
102
|
+
|
103
|
+
feature.summary.should eql @topic.summary
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should overwrite associated model's summary" do
|
107
|
+
feature = @topic.features.create!(:featureable => @topic, :summary => 'New Summary')
|
108
|
+
|
109
|
+
feature.summary.should eql 'New Summary'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
## POSITION ##
|
114
|
+
it "should assign a default position" do
|
115
|
+
subject.position.should_not be_nil
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should assign a default position of 1 if there are none existing" do
|
119
|
+
subject.position.should eql 1
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should assign the next position in line if it already exists" do
|
123
|
+
subject
|
124
|
+
@topic.features.create!(:position => 1).position.should eql 2
|
125
|
+
end
|
130
126
|
end
|
131
|
-
|