dm-is-remixable 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +43 -0
- data/Rakefile +2 -2
- data/TODO +1 -13
- data/lib/dm-is-remixable/is/remixable.rb +26 -22
- data/lib/dm-is-remixable/is/version.rb +1 -1
- data/spec/data/commentable.rb +1 -0
- data/spec/data/image.rb +36 -0
- data/spec/integration/remixable_spec.rb +96 -7
- data/spec/spec_helper.rb +1 -1
- metadata +5 -15
data/README.txt
CHANGED
@@ -125,3 +125,46 @@ class Video
|
|
125
125
|
|
126
126
|
#... methods, properties, etc ...#
|
127
127
|
end
|
128
|
+
|
129
|
+
|
130
|
+
Further, remixables can namespace methods that should exist in the generated and remixing classes, if these
|
131
|
+
modules are present the are attached appropriately to the other classes.
|
132
|
+
|
133
|
+
module ExampleRemixable
|
134
|
+
include DataMapper::Resource
|
135
|
+
is :remixable
|
136
|
+
|
137
|
+
#... your properies ...
|
138
|
+
|
139
|
+
# Class methods that will be attached to class doing the remixing...
|
140
|
+
#
|
141
|
+
# These methods would be attached to the User class given:
|
142
|
+
# User.remixes n, :images
|
143
|
+
#
|
144
|
+
module RemixerClassMethods
|
145
|
+
end
|
146
|
+
|
147
|
+
# Instances methods that will be attached to objects of the class doing the remixing...
|
148
|
+
#
|
149
|
+
# These methods would be attached to User objects given:
|
150
|
+
# User.remixes n, :images
|
151
|
+
#
|
152
|
+
module RemixerInstanceMethods
|
153
|
+
end
|
154
|
+
|
155
|
+
# Class methods that will be attached to genereated remixed class
|
156
|
+
#
|
157
|
+
# These methods would be attached to the UserImage class given:
|
158
|
+
# User.remixes n, :images
|
159
|
+
#
|
160
|
+
module RemixeeClassMethods
|
161
|
+
end
|
162
|
+
|
163
|
+
# Instances methods that will be attached to objects of the genereated remixed class
|
164
|
+
#
|
165
|
+
# These methods would be attached to UserImage objects given:
|
166
|
+
# User.remixes n, :images
|
167
|
+
#
|
168
|
+
module RemixeeInstanceMethods
|
169
|
+
end
|
170
|
+
end
|
data/Rakefile
CHANGED
@@ -10,12 +10,12 @@ AUTHOR = "Cory O'Daniel"
|
|
10
10
|
EMAIL = "dm-is-remixable [a] coryodaniel [d] com"
|
11
11
|
GEM_NAME = "dm-is-remixable"
|
12
12
|
GEM_VERSION = DataMapper::Is::Remixable::VERSION
|
13
|
-
GEM_DEPENDENCIES = [[
|
13
|
+
GEM_DEPENDENCIES = [['dm-core', "~>#{GEM_VERSION}"]]
|
14
14
|
GEM_CLEAN = ["log", "pkg"]
|
15
15
|
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO ] }
|
16
16
|
|
17
17
|
PROJECT_NAME = "datamapper"
|
18
|
-
PROJECT_URL = "http://github.com/sam/dm-more/tree/master/dm-
|
18
|
+
PROJECT_URL = "http://github.com/sam/dm-more/tree/master/dm-is-remixable"
|
19
19
|
PROJECT_DESCRIPTION = PROJECT_SUMMARY = "dm-is-remixable allow you to create reusable data functionality"
|
20
20
|
|
21
21
|
require ROOT.parent + 'tasks/hoe'
|
data/TODO
CHANGED
@@ -14,17 +14,5 @@ TODO
|
|
14
14
|
- Test nested remixing User remixes Photogenic; Photogenic Remixes comments
|
15
15
|
- Test double+ remixing. User remixes Commentable; enhance Commentable remix Commentable
|
16
16
|
|
17
|
-
- Harvest Class methods (including it into Remixed Model gets the instance methods, but not class methods...)
|
18
|
-
|
19
|
-
- Squash protection;
|
20
|
-
IF ClassA => remix ModuleB, :table_name => "squashme"
|
21
|
-
AND ClassC => remix ModuleB, :table_name => "squashme" #SQUASHED THAT TABLE
|
22
|
-
|
23
17
|
- Remixable.related(*remixed_models)
|
24
|
-
Taggable.related(Article, JobPostings)
|
25
|
-
|
26
|
-
|
27
|
-
CONSIDERATIONS
|
28
|
-
==============
|
29
|
-
- Customizing Assocations (http://datamapper.org/docs/associations.html)
|
30
|
-
- Adding Conditions to Associations (http://datamapper.org/docs/associations.html)
|
18
|
+
Taggable.related(Article, JobPostings)
|
@@ -55,6 +55,7 @@ module DataMapper
|
|
55
55
|
extend DataMapper::Is::Remixable::RemixeeClassMethods
|
56
56
|
include DataMapper::Is::Remixable::RemixeeInstanceMethods
|
57
57
|
@is_remixable = true
|
58
|
+
|
58
59
|
# support clean suffixes for nested modules
|
59
60
|
default_suffix = Extlib::Inflection.demodulize(self.name).singular.snake_case
|
60
61
|
suffix(options.delete(:suffix) || default_suffix)
|
@@ -93,7 +94,8 @@ module DataMapper
|
|
93
94
|
# This is the class that will be created from the Remixable Module
|
94
95
|
# The storage_name can be changed via 'enhance' in the class that is remixing
|
95
96
|
# Default: self.name.downcase + "_" + remixable.suffix.pluralize
|
96
|
-
# :as <String>
|
97
|
+
# :as <String> Alters the name that the remixable items will be available through, this WILL NOT
|
98
|
+
# create the standard accessor
|
97
99
|
# Default: tableize(:class_name)
|
98
100
|
# :for|:on <String> Class name to join to through Remixable
|
99
101
|
# This will create a M:M relationship THROUGH the remixable, rather than
|
@@ -114,7 +116,7 @@ module DataMapper
|
|
114
116
|
#
|
115
117
|
# Tables: users, user_addresses
|
116
118
|
# Classes: User, UserAddress
|
117
|
-
# User.user_addresses << UserAddress.new
|
119
|
+
# User.user_addresses << UserAddress.new => Raise No Method Exception since it was alias with :as
|
118
120
|
# User.addresses << UserAddress.new
|
119
121
|
# --------------------------------------------
|
120
122
|
# --------------------------------------------
|
@@ -175,6 +177,15 @@ module DataMapper
|
|
175
177
|
remixable_key = Extlib::Inflection.demodulize(remixable_module.name).snake_case.to_sym
|
176
178
|
populate_remixables_mapping(model, options.merge(:remixable_key => remixable_key))
|
177
179
|
|
180
|
+
# attach RemixerClassMethods and RemixerInstanceMethods to remixer if defined by remixee
|
181
|
+
if Object.full_const_defined? "#{remixable_module}::RemixerClassMethods"
|
182
|
+
extend Object.full_const_get("#{remixable_module}::RemixerClassMethods")
|
183
|
+
end
|
184
|
+
|
185
|
+
if Object.full_const_defined? "#{remixable_module}::RemixerInstanceMethods"
|
186
|
+
include Object.full_const_get("#{remixable_module}::RemixerInstanceMethods")
|
187
|
+
end
|
188
|
+
|
178
189
|
#Create relationships between Remixer and remixed class
|
179
190
|
if options[:other_model]
|
180
191
|
# M:M Class-To-Class w/ Remixable Module as intermediate table
|
@@ -185,9 +196,6 @@ module DataMapper
|
|
185
196
|
# has n and belongs_to (or One-To-Many)
|
186
197
|
remix_one_to_many cardinality, model, options
|
187
198
|
end
|
188
|
-
|
189
|
-
#Add accessor alias
|
190
|
-
attach_accessor(options) unless options[:as].nil?
|
191
199
|
else
|
192
200
|
DataMapper.logger.warn "#{__FILE__}:#{__LINE__} warning: already remixed constant #{options[:class_name]}"
|
193
201
|
end
|
@@ -232,7 +240,6 @@ module DataMapper
|
|
232
240
|
# belongs_to :bot
|
233
241
|
# belongs_to :tag
|
234
242
|
# end
|
235
|
-
|
236
243
|
def enhance(remixable,remixable_model=nil, &block)
|
237
244
|
# always use innermost singular snake_cased constant name
|
238
245
|
remixable_name = remixable.to_s.singular.snake_case.to_sym
|
@@ -253,18 +260,6 @@ module DataMapper
|
|
253
260
|
|
254
261
|
private
|
255
262
|
|
256
|
-
# - attach_accessor
|
257
|
-
# ==== Description
|
258
|
-
# Creates additional alias for r/w accessor
|
259
|
-
# ==== Parameters
|
260
|
-
# options <Hash> options hash
|
261
|
-
def attach_accessor(options)
|
262
|
-
self.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
263
|
-
alias #{options[:as].to_sym} #{options[:table_name].to_sym}
|
264
|
-
alias #{options[:as].to_sym}= #{options[:table_name].to_sym}=
|
265
|
-
EOS
|
266
|
-
end
|
267
|
-
|
268
263
|
# - populate_remixables_mapping
|
269
264
|
# ==== Description
|
270
265
|
# Populates the Hash of remixables with information about the remixable
|
@@ -290,7 +285,7 @@ module DataMapper
|
|
290
285
|
# model <Class> remixed model that 'self' is relating to
|
291
286
|
# options <Hash> options hash
|
292
287
|
def remix_one_to_many(cardinality, model, options)
|
293
|
-
self.has cardinality, options[:table_name].
|
288
|
+
self.has cardinality, (options[:as] || options[:table_name]).to_sym, :class_name => model.name
|
294
289
|
model.property Extlib::Inflection.foreign_key(self.name).intern, Integer, :nullable => false
|
295
290
|
model.belongs_to Extlib::Inflection.tableize(self.name).intern
|
296
291
|
end
|
@@ -311,7 +306,7 @@ module DataMapper
|
|
311
306
|
|
312
307
|
# Is M:M between two different classes or the same class
|
313
308
|
unless self.name == options[:other_model].name
|
314
|
-
self.has cardinality, options[:table_name].
|
309
|
+
self.has cardinality, (options[:as] || options[:table_name]).to_sym, :class_name => model.name
|
315
310
|
options[:other_model].has cardinality, options[:table_name].intern
|
316
311
|
|
317
312
|
model.belongs_to Extlib::Inflection.tableize(self.name).intern
|
@@ -319,7 +314,7 @@ module DataMapper
|
|
319
314
|
else
|
320
315
|
raise Exception, "options[:via] must be specified when Remixing a module between two of the same class" unless options[:via]
|
321
316
|
|
322
|
-
self.has cardinality, options[:table_name].
|
317
|
+
self.has cardinality, (options[:as] || options[:table_name]).to_sym, :class_name => model.name
|
323
318
|
model.belongs_to Extlib::Inflection.tableize(self.name).intern
|
324
319
|
model.belongs_to options[:via].intern, :class_name => options[:other_model].name, :child_key => ["#{options[:via]}_id".intern]
|
325
320
|
end
|
@@ -350,6 +345,15 @@ module DataMapper
|
|
350
345
|
model.property(prop.name, prop.type, prop.options)
|
351
346
|
end
|
352
347
|
|
348
|
+
# attach remixed model access to RemixeeClassMethods and RemixeeInstanceMethods if defined
|
349
|
+
if Object.full_const_defined? "#{remixable}::RemixeeClassMethods"
|
350
|
+
model.send :extend, Object.full_const_get("#{remixable}::RemixeeClassMethods")
|
351
|
+
end
|
352
|
+
|
353
|
+
if Object.full_const_defined? "#{remixable}::RemixeeInstanceMethods"
|
354
|
+
model.send :include, Object.full_const_get("#{remixable}::RemixeeInstanceMethods")
|
355
|
+
end
|
356
|
+
|
353
357
|
model
|
354
358
|
end
|
355
359
|
|
@@ -358,7 +362,7 @@ module DataMapper
|
|
358
362
|
# - RemixeeClassMethods
|
359
363
|
# ==== Description
|
360
364
|
# Methods available to any model that is :remixable
|
361
|
-
module RemixeeClassMethods
|
365
|
+
module RemixeeClassMethods
|
362
366
|
# - suffix
|
363
367
|
# ==== Description
|
364
368
|
# modifies the storage name suffix, which is by default based on the Remixable Module name
|
data/spec/data/commentable.rb
CHANGED
data/spec/data/image.rb
CHANGED
@@ -6,4 +6,40 @@ module Image
|
|
6
6
|
property :id, Integer, :key => true, :serial => true
|
7
7
|
property :description, String
|
8
8
|
property :path, String
|
9
|
+
|
10
|
+
# These methods will be available to the class remixing this module
|
11
|
+
# If 'User' remixes 'Images', these methods will be available to a User class
|
12
|
+
#
|
13
|
+
module RemixerClassMethods
|
14
|
+
def test_remixer_class_method
|
15
|
+
'CLASS METHOD FOR REMIXER'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# These methods will be available to instantiated objects of the remixing this module
|
20
|
+
# If 'User' remixes 'Images', these methods will be available to a User object
|
21
|
+
#
|
22
|
+
module RemixerInstanceMethods
|
23
|
+
def test_remixer_instance_method
|
24
|
+
'INSTANCE METHOD FOR REMIXER'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# These methods will be available to the Generated Remixed Class
|
29
|
+
# If 'User' remixes 'Images', these methods will be available to UserImage class
|
30
|
+
#
|
31
|
+
module RemixeeClassMethods
|
32
|
+
def test_remixee_class_method
|
33
|
+
'CLASS METHOD FOR REMIXEE'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# These methods will be available to an instantiated Generated Remixed Class
|
38
|
+
# If 'User' remixes 'Images', these methods will be available to a UserImage object
|
39
|
+
#
|
40
|
+
module RemixeeInstanceMethods
|
41
|
+
def test_remixee_instance_method
|
42
|
+
'INSTANCE METHOD FOR REMIXEE'
|
43
|
+
end
|
44
|
+
end
|
9
45
|
end
|
@@ -33,7 +33,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
33
33
|
|
34
34
|
it "should not allow enhancements of modules that aren't remixed" do
|
35
35
|
lambda {
|
36
|
-
User.enhance
|
36
|
+
User.enhance :images
|
37
37
|
}.should raise_error
|
38
38
|
end
|
39
39
|
|
@@ -135,7 +135,6 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
135
135
|
it "should allow creating an accessor alias" do
|
136
136
|
article = Article.new
|
137
137
|
article.should respond_to("pics")
|
138
|
-
article.should respond_to("article_images")
|
139
138
|
end
|
140
139
|
|
141
140
|
it "should copy properties from the Remixable Module to the Remixed Model" do
|
@@ -196,8 +195,10 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
196
195
|
article.pics.path.should == image2.path
|
197
196
|
end
|
198
197
|
|
198
|
+
# Example:
|
199
|
+
# Users are Commentable by many Users
|
200
|
+
#
|
199
201
|
it "should allow M:M unary relationships through the Remixable Module" do
|
200
|
-
#User => Commentable => User
|
201
202
|
user = User.new
|
202
203
|
user.first_name = "Tester"
|
203
204
|
user2 = User.new
|
@@ -206,16 +207,20 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
206
207
|
comment = UserComment.new
|
207
208
|
comment.comment = "YOU SUCK!"
|
208
209
|
comment.commentor = user2
|
209
|
-
user.user_comments << comment
|
210
210
|
|
211
|
-
|
211
|
+
user.comments << comment
|
212
|
+
|
213
|
+
user2.comments.length.should be(0)
|
212
214
|
|
213
215
|
comment.commentor.first_name.should == "Testy"
|
214
|
-
|
216
|
+
|
217
|
+
user.comments.length.should be(1)
|
215
218
|
end
|
216
219
|
|
220
|
+
# Example:
|
221
|
+
# Articles are Commentable by many Users
|
222
|
+
#
|
217
223
|
it "should allow M:M relationships through the Remixable Module" do
|
218
|
-
#Article => Commentable => User
|
219
224
|
user = User.new
|
220
225
|
article = Article.new
|
221
226
|
|
@@ -235,5 +240,89 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
235
240
|
article.comments.first.should be(ac)
|
236
241
|
user.article_comments.first.should be(ac)
|
237
242
|
end
|
243
|
+
|
244
|
+
# Example:
|
245
|
+
# Remixable Image add functionality to any class that remixes it
|
246
|
+
# Image::RemixerClassMethods defines a method called 'total_images' that counts the total number of images for the class
|
247
|
+
# Image::RemixerInstanceMethods defines a method called 'most_viewed_image' that find the most viewed image for an object
|
248
|
+
#
|
249
|
+
# User.remixes n, :images
|
250
|
+
# User.total_images => count of all images owned by all users
|
251
|
+
# User.first.most_viewed_image => would return the most viewed image
|
252
|
+
#
|
253
|
+
it "should add a remixables' 'RemixerClassMethods' modules to the remixing class" do
|
254
|
+
Article.respond_to?(:test_remixer_class_method).should be(true)
|
255
|
+
Article.test_remixer_class_method.should == 'CLASS METHOD FOR REMIXER'
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should add a remixables' 'RemixerInstanceMethods' modules to the remixing class" do
|
259
|
+
Article.new.respond_to?(:test_remixer_instance_method).should be(true)
|
260
|
+
Article.new.test_remixer_instance_method.should == 'INSTANCE METHOD FOR REMIXER'
|
261
|
+
end
|
262
|
+
|
263
|
+
# Example:
|
264
|
+
# Remixable Image add functionality to any class that remixes it
|
265
|
+
# Image::RemixeeClassMethods defines a method called 'damaged_files' would return a list of all images with invalid checksums (or whatev)
|
266
|
+
# Image::RemixeeInstanceMethods defines a method called 'mime_type' that find the mime type of the particular image
|
267
|
+
#
|
268
|
+
# Article.remixes n, :images
|
269
|
+
# # => yields and ArticleImage Class
|
270
|
+
# ArticleImage.damaged_files => list of all images with invalid checksums
|
271
|
+
# ArticleImage.first.mime_type => would return the mime type of that image
|
272
|
+
#
|
273
|
+
it "should add a remixables' 'RemixeeClassMethods' modules to the generated remixed class" do
|
274
|
+
ArticleImage.respond_to?(:test_remixee_class_method).should be(true)
|
275
|
+
ArticleImage.test_remixee_class_method.should == 'CLASS METHOD FOR REMIXEE'
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should add a remixables' 'RemixeeInstanceMethods' modules to the generated remixed class" do
|
279
|
+
ArticleImage.new.respond_to?(:test_remixee_instance_method).should be(true)
|
280
|
+
ArticleImage.new.test_remixee_instance_method.should == 'INSTANCE METHOD FOR REMIXEE'
|
281
|
+
end
|
282
|
+
|
283
|
+
# Example:
|
284
|
+
# User.remixes n, :images, :as => "pics"
|
285
|
+
# User.first.pics would be the acessor for images
|
286
|
+
# User.first.user_images should raise method not found
|
287
|
+
#
|
288
|
+
it 'should remove the original attribute accessor when attaching an optional one' do
|
289
|
+
Article.new.respond_to?(:pics).should be(true)
|
290
|
+
User.new.respond_to?(:user_addresses).should be(true)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Currently:
|
294
|
+
# Submission.remixes n, :comments
|
295
|
+
# SubmissionComment.new.user = User.first => throws exception, accessor name is 'users' instead
|
296
|
+
#
|
297
|
+
# Example:
|
298
|
+
# User.remix 1, :images
|
299
|
+
# # => User.image & UserImage.user
|
300
|
+
#
|
301
|
+
# User.remix n, :images
|
302
|
+
# # => User.images & UserImage.user
|
303
|
+
#
|
304
|
+
# User.remix n, :comments, :for => 'User', :via => 'commentor'
|
305
|
+
# # => User.comments & UserComment.user & UserComment.commentor
|
306
|
+
#
|
307
|
+
it 'should pluralize accessor names with respect to cardinality' do
|
308
|
+
pending
|
309
|
+
end
|
310
|
+
|
311
|
+
# Note:
|
312
|
+
# Currently the :via flag allows one to specify another name for the field, but it always appends _id
|
313
|
+
#
|
314
|
+
# Example:
|
315
|
+
# User w/ PK being 'login_name'
|
316
|
+
# User.remixes n, :comments, :for => 'User', :via => 'commentor'
|
317
|
+
#
|
318
|
+
# Comment Table:
|
319
|
+
# * id
|
320
|
+
# * text
|
321
|
+
# * user_login_name
|
322
|
+
# * commentor_id #=> should be able to specify it to be commentor_login_name
|
323
|
+
#
|
324
|
+
it 'should allow the primary and child field names to be specified while remixing' do
|
325
|
+
pending
|
326
|
+
end
|
238
327
|
end
|
239
328
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-is-remixable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cory O'Daniel
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-12-09 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -18,19 +18,9 @@ dependencies:
|
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- -
|
21
|
+
- - ~>
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.9.
|
24
|
-
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: hoe
|
27
|
-
type: :development
|
28
|
-
version_requirement:
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.8.2
|
23
|
+
version: 0.9.8
|
34
24
|
version:
|
35
25
|
description: dm-is-remixable allow you to create reusable data functionality
|
36
26
|
email:
|
@@ -69,7 +59,7 @@ files:
|
|
69
59
|
- spec/spec.opts
|
70
60
|
- spec/spec_helper.rb
|
71
61
|
has_rdoc: true
|
72
|
-
homepage: http://github.com/sam/dm-more/tree/master/dm-
|
62
|
+
homepage: http://github.com/sam/dm-more/tree/master/dm-is-remixable
|
73
63
|
post_install_message:
|
74
64
|
rdoc_options:
|
75
65
|
- --main
|