dm-is-remixable 0.9.7 → 0.9.8
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/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
|