dm-taggings 0.11.0 → 1.0.0
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/.rspec +2 -0
- data/Gemfile +25 -100
- data/Rakefile +6 -7
- data/VERSION +1 -1
- data/dm-taggings.gemspec +57 -54
- data/lib/dm-taggings.rb +5 -5
- data/lib/dm-taggings/is/taggable.rb +37 -35
- data/lib/dm-taggings/is/tagger.rb +12 -6
- data/lib/dm-taggings/is/tagging.rb +3 -3
- data/lib/dm-taggings/spec/taggable_shared_spec.rb +108 -114
- data/lib/dm-taggings/spec/tagger_shared_spec.rb +16 -15
- data/spec/fixtures/models.rb +28 -25
- data/spec/integration/post_spec.rb +5 -5
- data/spec/integration/tag_spec.rb +1 -1
- data/spec/integration/taggable_spec.rb +4 -4
- data/spec/integration/user_spec.rb +5 -8
- data/spec/spec_helper.rb +11 -1
- metadata +47 -50
- data/.gitignore +0 -9
- data/spec/spec.opts +0 -2
@@ -39,9 +39,6 @@ module DataMapper
|
|
39
39
|
|
40
40
|
# Add instance-methods
|
41
41
|
include DataMapper::Is::Tagger::InstanceMethods
|
42
|
-
|
43
|
-
cattr_accessor(:taggable_object_classes)
|
44
|
-
self.taggable_object_classes = []
|
45
42
|
end
|
46
43
|
|
47
44
|
raise "options[:for] is missing" unless options[:for]
|
@@ -50,6 +47,14 @@ module DataMapper
|
|
50
47
|
end
|
51
48
|
|
52
49
|
module ClassMethods
|
50
|
+
def self.extended(base)
|
51
|
+
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
|
+
def self.taggable_object_classes
|
53
|
+
@@taggable_object_classes ||= []
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
end
|
57
|
+
|
53
58
|
# Return if a model is tagger
|
54
59
|
#
|
55
60
|
# @return [TrueClass]
|
@@ -71,10 +76,12 @@ module DataMapper
|
|
71
76
|
self.taggable_object_classes << taggable_object_class
|
72
77
|
|
73
78
|
has n, taggable_object_class.tagging_relationship_name,
|
79
|
+
:model => taggable_object_class.tagging_class,
|
74
80
|
:constraint => :destroy
|
75
81
|
|
76
82
|
has n, taggable_object_class.taggable_relationship_name,
|
77
|
-
:
|
83
|
+
:model => taggable_object_class.name,
|
84
|
+
:through => taggable_object_class.tagging_relationship_name,
|
78
85
|
:constraint => :destroy
|
79
86
|
end
|
80
87
|
end
|
@@ -94,7 +101,7 @@ module DataMapper
|
|
94
101
|
#
|
95
102
|
# @api public
|
96
103
|
def tag!(taggable, options={})
|
97
|
-
unless self.taggable_object_classes.include?(taggable.class)
|
104
|
+
unless self.class.taggable_object_classes.include?(taggable.class)
|
98
105
|
raise "Object of type #{taggable.class} isn't taggable!"
|
99
106
|
end
|
100
107
|
|
@@ -112,4 +119,3 @@ module DataMapper
|
|
112
119
|
end # Tagger
|
113
120
|
end # Is
|
114
121
|
end # DataMapper
|
115
|
-
|
@@ -2,12 +2,12 @@ module Tagging
|
|
2
2
|
include DataMapper::Resource
|
3
3
|
|
4
4
|
property :id, Serial
|
5
|
-
|
5
|
+
|
6
|
+
belongs_to :tag
|
6
7
|
|
7
8
|
is :remixable, :suffix => "tag"
|
8
9
|
|
9
10
|
def tagger=(tagger)
|
10
|
-
send("#{DataMapper::Inflector.underscore(tagger.class.name).to_sym}=", tagger)
|
11
|
+
send("#{DataMapper::Inflector.underscore(DataMapper::Inflector.demodulize(tagger.class.name)).to_sym}=", tagger)
|
11
12
|
end
|
12
13
|
end
|
13
|
-
|
@@ -1,23 +1,21 @@
|
|
1
1
|
share_examples_for 'A taggable resource' do
|
2
2
|
def create_taggable(attrs={})
|
3
|
-
|
3
|
+
taggable.create(@taggable_attributes.merge(attrs))
|
4
4
|
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
@taggable_attributes ||= {}
|
6
|
+
def taggable
|
7
|
+
raise "taggable should be defined"
|
8
|
+
end
|
12
9
|
|
13
|
-
|
14
|
-
|
10
|
+
let(:foo_tag) { Tag["foo"] }
|
11
|
+
let(:bar_tag) { Tag["bar"] }
|
15
12
|
|
16
|
-
|
13
|
+
before :all do
|
14
|
+
@taggable_attributes ||= {}
|
17
15
|
end
|
18
16
|
|
19
17
|
describe "public class methods" do
|
20
|
-
subject {
|
18
|
+
subject { taggable }
|
21
19
|
|
22
20
|
it { should respond_to(:is_taggable) }
|
23
21
|
it { should respond_to(:taggable?) }
|
@@ -27,40 +25,52 @@ share_examples_for 'A taggable resource' do
|
|
27
25
|
it { should respond_to(:tagging_class) }
|
28
26
|
it { should respond_to(:tagging_parent_name) }
|
29
27
|
it { should respond_to(:taggable_relationship_name) }
|
28
|
+
it { should respond_to(:taggable_options) }
|
29
|
+
|
30
|
+
describe ".taggable_options" do
|
31
|
+
let(:expected_options) { { :tag_list_separator => ',' } }
|
32
|
+
|
33
|
+
it "should set the defaults" do
|
34
|
+
taggable.taggable_options.should == expected_options
|
35
|
+
end
|
36
|
+
end
|
30
37
|
|
31
38
|
describe ".taggable?" do
|
32
39
|
it "should return true" do
|
33
|
-
|
40
|
+
taggable.taggable?.should be(true)
|
34
41
|
end
|
35
42
|
end
|
36
43
|
|
37
44
|
describe "relationships" do
|
38
|
-
subject {
|
45
|
+
subject { taggable.relationships.named?(taggable.tagging_relationship_name) }
|
39
46
|
|
40
|
-
it { should
|
47
|
+
it { should be(true) }
|
41
48
|
|
42
49
|
describe "tagging constraint" do
|
43
|
-
subject {
|
50
|
+
subject { taggable.tagging_relationship.constraint }
|
44
51
|
it { subject.should eql(:destroy!) }
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
48
55
|
describe ".tagged_with" do
|
49
|
-
|
50
|
-
|
51
|
-
|
56
|
+
let(:resource_one) { create_taggable(:tag_list => "red, green, blue") }
|
57
|
+
let(:resource_two) { create_taggable(:tag_list => "orange, yellow") }
|
58
|
+
|
59
|
+
before do
|
60
|
+
resource_one
|
61
|
+
resource_two
|
52
62
|
end
|
53
63
|
|
64
|
+
subject { taggable.tagged_with(["red", "yellow", "purple"]) }
|
65
|
+
|
54
66
|
it "should return correct resources" do
|
55
|
-
|
56
|
-
result.size.should eql(2)
|
57
|
-
result.should include(@resource_one, @resource_two)
|
67
|
+
should == [ resource_one, resource_two ]
|
58
68
|
end
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
62
72
|
describe "public instance methods" do
|
63
|
-
subject {
|
73
|
+
subject { taggable.new }
|
64
74
|
|
65
75
|
it { should respond_to(:tag) }
|
66
76
|
it { should respond_to(:tag!) }
|
@@ -70,189 +80,173 @@ share_examples_for 'A taggable resource' do
|
|
70
80
|
it { should respond_to(:taggings) }
|
71
81
|
|
72
82
|
describe ".tag" do
|
73
|
-
|
74
|
-
|
75
|
-
@taggings = @resource.tag([@foo_tag, @bar_tag])
|
76
|
-
end
|
83
|
+
let(:resource) { create_taggable }
|
84
|
+
let(:tags) { resource.tag([ foo_tag, bar_tag ]) }
|
77
85
|
|
78
|
-
it "should set new
|
79
|
-
|
86
|
+
it "should set new tags" do
|
87
|
+
tags.should == resource.tags
|
80
88
|
end
|
81
89
|
|
82
|
-
it "should not
|
83
|
-
|
90
|
+
it "should not save new taggings" do
|
91
|
+
resource.taggings.all? { |tagging| tagging.new? }.should be(true)
|
84
92
|
end
|
85
93
|
end
|
86
94
|
|
87
95
|
describe ".tag!" do
|
88
|
-
|
89
|
-
|
90
|
-
|
96
|
+
let(:resource) { create_taggable }
|
97
|
+
let(:new_tags) { [ foo_tag, bar_tag ] }
|
98
|
+
let(:tags) { resource.tag!(new_tags) }
|
99
|
+
|
100
|
+
|
101
|
+
it "returns added tags" do
|
102
|
+
tags.should == new_tags
|
91
103
|
end
|
92
104
|
|
93
|
-
it "
|
94
|
-
|
105
|
+
it "creates new taggings" do
|
106
|
+
resource.tags.should == tags
|
95
107
|
end
|
96
108
|
end
|
97
109
|
|
98
110
|
describe ".untag" do
|
99
|
-
|
100
|
-
|
101
|
-
@resource = create_taggable
|
102
|
-
@taggings = @resource.tag!([@foo_tag, @bar_tag])
|
111
|
+
let(:tags) { [ foo_tag, bar_tag ] }
|
112
|
+
let(:resource) { create_taggable(:tags => tags) }
|
103
113
|
|
104
|
-
|
114
|
+
describe "all" do
|
115
|
+
before do
|
116
|
+
resource.untag
|
105
117
|
end
|
106
118
|
|
107
|
-
it "
|
108
|
-
|
119
|
+
it "removes taggings from loaded collection" do
|
120
|
+
resource.tags.should be_empty
|
109
121
|
end
|
110
122
|
|
111
|
-
it "
|
112
|
-
|
123
|
+
it "doesn't destroy the taggings" do
|
124
|
+
resource.reload.tags.should == tags
|
113
125
|
end
|
114
126
|
end
|
115
127
|
|
116
|
-
describe "specific
|
117
|
-
before
|
118
|
-
|
119
|
-
@taggings = @resource.tag!([@foo_tag, @bar_tag])
|
120
|
-
|
121
|
-
@resource.untag([@foo_tag])
|
128
|
+
describe "specific tags" do
|
129
|
+
before do
|
130
|
+
resource.untag([ foo_tag ])
|
122
131
|
end
|
123
132
|
|
124
|
-
it "should remove the
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
it "should remove the related tag" do
|
129
|
-
@resource.tags.should_not include(@foo_tag)
|
133
|
+
it "should remove the tag from loaded collection" do
|
134
|
+
resource.tags.should == [ bar_tag ]
|
130
135
|
end
|
131
136
|
end
|
132
137
|
|
133
138
|
describe "when save is called" do
|
134
|
-
before
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
@resource.untag
|
139
|
-
end
|
140
|
-
|
141
|
-
it "should return true" do
|
142
|
-
pending "Currently DataMapper doesn't support saving an empty collection" do
|
143
|
-
@resource.save.should be(true)
|
144
|
-
end
|
139
|
+
before do
|
140
|
+
resource.untag
|
141
|
+
resource.save
|
142
|
+
resource.reload
|
145
143
|
end
|
146
144
|
|
147
145
|
it "should destroy taggings" do
|
148
|
-
|
149
|
-
@resource.reload.taggings.should be_empty
|
150
|
-
end
|
146
|
+
resource.taggings.should be_empty
|
151
147
|
end
|
152
148
|
|
153
149
|
it "should destroy tags" do
|
154
|
-
|
155
|
-
@resource.reload.tags.should be_empty
|
156
|
-
end
|
150
|
+
resource.tags.should be_empty
|
157
151
|
end
|
158
152
|
end
|
159
153
|
end
|
160
154
|
|
161
155
|
describe ".untag!" do
|
156
|
+
let(:tags) { [ foo_tag, bar_tag ] }
|
157
|
+
let(:resource) { create_taggable(:tags => tags) }
|
158
|
+
|
162
159
|
describe "all" do
|
163
|
-
before
|
164
|
-
@
|
165
|
-
|
160
|
+
before do
|
161
|
+
@result = resource.untag!
|
162
|
+
resource.reload
|
163
|
+
end
|
166
164
|
|
167
|
-
|
165
|
+
it "returns removed tags" do
|
166
|
+
@result == tags
|
168
167
|
end
|
169
168
|
|
170
|
-
it "
|
171
|
-
|
169
|
+
it "destroys the taggings" do
|
170
|
+
resource.tags.should be_empty
|
172
171
|
end
|
173
172
|
end
|
174
173
|
|
175
|
-
describe "specific
|
176
|
-
before
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
@resource.untag!([@foo_tag])
|
181
|
-
@resource.reload
|
174
|
+
describe "specific tags" do
|
175
|
+
before do
|
176
|
+
resource.untag!([ foo_tag ])
|
177
|
+
resource.reload
|
182
178
|
end
|
183
179
|
|
184
|
-
subject {
|
180
|
+
subject { resource.tags }
|
185
181
|
|
186
|
-
it { should_not include(
|
187
|
-
it { should include(
|
182
|
+
it { should_not include(foo_tag) }
|
183
|
+
it { should include(bar_tag) }
|
188
184
|
end
|
189
185
|
end
|
190
186
|
|
191
187
|
describe ".tag_list=" do
|
192
188
|
describe "with a list of tag names" do
|
193
189
|
describe "with blank values" do
|
194
|
-
|
195
|
-
@resource = create_taggable(:tag_list => "foo, , ,bar, , ")
|
196
|
-
end
|
190
|
+
let(:resource) { create_taggable(:tag_list => "foo, , ,bar, , ") }
|
197
191
|
|
198
192
|
it "should add new tags and reject blank names" do
|
199
|
-
|
193
|
+
resource.tags.should == [ Tag["foo"], Tag["bar"] ]
|
200
194
|
end
|
201
195
|
end
|
202
196
|
|
203
197
|
describe "when tags are removed and added" do
|
204
|
-
|
205
|
-
|
206
|
-
|
198
|
+
let(:resource) { create_taggable(:tag_list => "foo, bar") }
|
199
|
+
|
200
|
+
before do
|
201
|
+
resource.update(:tag_list => "foo, bar, pub")
|
207
202
|
end
|
208
203
|
|
209
204
|
it "should add new tags" do
|
210
|
-
|
205
|
+
resource.tags.should == [ Tag["foo"], Tag["bar"], Tag["pub"] ]
|
211
206
|
end
|
212
207
|
end
|
213
208
|
|
214
209
|
describe "when tags are added" do
|
215
|
-
|
216
|
-
|
217
|
-
|
210
|
+
let(:resource) { create_taggable(:tag_list => "foo, bar") }
|
211
|
+
|
212
|
+
before do
|
213
|
+
resource.update(:tag_list => "bar, pub")
|
218
214
|
end
|
219
215
|
|
220
216
|
it "should add new tags" do
|
221
|
-
|
217
|
+
resource.tags.should include(Tag["bar"], Tag["pub"])
|
222
218
|
end
|
223
219
|
|
224
220
|
it "should remove tags" do
|
225
|
-
|
221
|
+
resource.tags.should_not include(Tag["foo"])
|
226
222
|
end
|
227
223
|
end
|
228
224
|
end
|
229
225
|
|
230
226
|
describe "when no list of tag names is given" do
|
227
|
+
let(:resource) { create_taggable(:tag_list => "foo, bar") }
|
228
|
+
|
231
229
|
before :all do
|
232
|
-
|
233
|
-
@resource.update(:tag_list => "")
|
230
|
+
resource.update(:tag_list => "")
|
234
231
|
end
|
235
232
|
|
236
233
|
it "should destroy taggings" do
|
237
|
-
|
234
|
+
resource.reload.taggings.should be_blank
|
238
235
|
end
|
239
236
|
|
240
237
|
it "should remove the tags" do
|
241
|
-
|
238
|
+
resource.reload.tags.should be_blank
|
242
239
|
end
|
243
240
|
end
|
244
241
|
end
|
245
242
|
|
246
243
|
describe ".tag_list" do
|
247
|
-
|
248
|
-
|
249
|
-
@expected = @tag_names.join(', ')
|
250
|
-
@resource = create_taggable(:tag_list => @expected)
|
251
|
-
end
|
244
|
+
let(:expected_tag_names) { %w(nice cool awesome).join(',') }
|
245
|
+
let(:resource) { create_taggable(:tag_list => expected_tag_names) }
|
252
246
|
|
253
|
-
|
254
|
-
|
255
|
-
|
247
|
+
subject { resource.tag_list }
|
248
|
+
|
249
|
+
it { should == expected_tag_names }
|
256
250
|
end
|
257
251
|
end
|
258
252
|
end
|
@@ -1,32 +1,33 @@
|
|
1
1
|
share_examples_for 'A tagger resource' do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
|
-
@foo_tag = Tag["foo"]
|
8
|
-
@bar_tag = Tag["bar"]
|
2
|
+
def taggable
|
3
|
+
raise "taggable should be defined"
|
4
|
+
end
|
9
5
|
|
10
|
-
|
6
|
+
def tagger
|
7
|
+
raise "tagger should be defined"
|
11
8
|
end
|
12
9
|
|
13
|
-
|
10
|
+
let(:foo_tag) { Tag["foo"] }
|
11
|
+
let(:bar_tag) { Tag["bar"] }
|
12
|
+
let(:tags) { [ foo_tag, bar_tag ] }
|
13
|
+
|
14
|
+
subject { tagger }
|
14
15
|
|
15
16
|
[ :is_tagger, :tagger?, :add_taggable_object_classes, :taggable_object_classes ].each do |method|
|
16
17
|
it { should respond_to(method) }
|
17
18
|
end
|
18
19
|
|
19
20
|
describe ".tagger?" do
|
20
|
-
subject {
|
21
|
+
subject { tagger.tagger? }
|
21
22
|
it { should be(true) }
|
22
23
|
end
|
23
24
|
|
24
25
|
describe "#tag!" do
|
25
26
|
before :all do
|
26
|
-
@tagger_resource =
|
27
|
-
@taggable_resource =
|
27
|
+
@tagger_resource = tagger.create
|
28
|
+
@taggable_resource = taggable.create
|
28
29
|
|
29
|
-
@tags = @tagger_resource.tag!(@taggable_resource, :with =>
|
30
|
+
@tags = @tagger_resource.tag!(@taggable_resource, :with => tags)
|
30
31
|
end
|
31
32
|
|
32
33
|
it "should tag the taggable resource" do
|
@@ -34,12 +35,12 @@ share_examples_for 'A tagger resource' do
|
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should associate tagger with taggable" do
|
37
|
-
@tagger_resource.reload.send(DataMapper::Inflector.underscore(
|
38
|
+
@tagger_resource.reload.send(DataMapper::Inflector.pluralize(DataMapper::Inflector.underscore(DataMapper::Inflector.demodulize(taggable.name)))).should include(@taggable_resource)
|
38
39
|
end
|
39
40
|
|
40
41
|
it "should associate taggings with tagger" do
|
41
42
|
@taggable_resource.taggings.each do |tagging|
|
42
|
-
tagging.send(DataMapper::Inflector.underscore(
|
43
|
+
tagging.send(DataMapper::Inflector.underscore(DataMapper::Inflector.demodulize(tagger.name))).should eql(@tagger_resource)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|