pjb3-flex-attributes 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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005 Eric Anderson
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/Manifest ADDED
@@ -0,0 +1,5 @@
1
+ flex-attributes.gemspec
2
+ lib/flex-attributes.rb
3
+ Manifest
4
+ README
5
+ MIT-LICENSE
data/README ADDED
@@ -0,0 +1,25 @@
1
+ = Flex Attributes
2
+
3
+ See ActiveRecord::FlexAttributes for usage information.
4
+
5
+ = Installation
6
+
7
+ Edit config/environment.rb:
8
+
9
+ config.gem 'pjb3-flex-attributes', :version => '~> 0.1', :lib => 'flex-attributes', :source => 'http://gems.github.com'
10
+
11
+ = RUNNING UNIT TESTS
12
+
13
+ == Creating the test database
14
+
15
+ The test databases will be created from the info specified in test/database.yml.
16
+ Either change that file to match your database or change your database to
17
+ match that file.
18
+
19
+ == Running with Rake
20
+
21
+ The easiest way to run the unit tests is through Rake. By default sqlite3
22
+ will be the database run. Just change your env variable DB to be the database
23
+ adaptor (specified in database.yml) that you want to use. The database and
24
+ permissions must already be setup but the tables will be created for you
25
+ from schema.rb.
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{flex-attributes}
3
+ s.version = "0.1"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Eric Anderson", "Paul Barry"]
7
+ s.date = %q{2008-08-26}
8
+ s.description = %q{Flex attributes allow for the common but questionable database design of storing attributes in a thin key/value table related to some model.}
9
+ s.email = %q{erci (at) afaik (dot) us}
10
+ s.extra_rdoc_files = ["lib/flex-attributes.rb", "README"]
11
+ s.files = ["flex-attributes.gemspec", "lib/flex-attributes.rb", "Manifest", "README", "MIT-LICENSE"]
12
+ s.has_rdoc = true
13
+ s.homepage = %q{http://github.com/pjb3/flex-attributes}
14
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Flex-Attributes", "--main", "README"]
15
+ s.require_paths = ["lib"]
16
+ s.rubyforge_project = %q{flex-attributes}
17
+ s.rubygems_version = %q{1.2.0}
18
+ s.summary = %q{Gem version of flex-attributes Rails plugin.}
19
+
20
+ if s.respond_to? :specification_version then
21
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
22
+ s.specification_version = 2
23
+
24
+ if current_version >= 3 then
25
+ else
26
+ end
27
+ else
28
+ end
29
+ end
@@ -0,0 +1,475 @@
1
+ module FlexAttributes
2
+
3
+ def self.included(base) # :nodoc:
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ # Flex attributes allow for the common but questionable database design of
8
+ # storing attributes in a thin key/value table related to some model.
9
+ #
10
+ # = Rational
11
+ #
12
+ # A good example of this is where you need to store
13
+ # lots (possible hundreds) of optional attributes on an object. My typical
14
+ # reference example is when you have a User object. You want to store the
15
+ # user's preferences between sessions. Every search, sort, etc in your
16
+ # application you want to keep track of so when the user visits that section
17
+ # of the application again you can simply restore the display to how it was.
18
+ #
19
+ # So your controller might have:
20
+ #
21
+ # Project.find :all, :conditions => current_user.project_search,
22
+ # :order => current_user.project_order
23
+ #
24
+ # But there could be hundreds of these little attributes that you really don't
25
+ # want to store directly on the user object. It would make your table have too
26
+ # many columns so it would be too much of a pain to deal with. Also there might
27
+ # be performance problems. So instead you might do something like
28
+ # this:
29
+ #
30
+ # class User < ActiveRecord::Base
31
+ # has_many :preferences
32
+ # end
33
+ #
34
+ # class Preferences < ActiveRecord::Base
35
+ # belongs_to :user
36
+ # end
37
+ #
38
+ # Now simply give the Preference model a "name" and "value" column and you are
39
+ # set..... except this is now too complicated. To retrieve a attribute you will
40
+ # need to do something like:
41
+ #
42
+ # Project.find :all,
43
+ # :conditions => current_user.preferences.find_by_name('project_search').value,
44
+ # :order => current_user.preferences.find_by_name('project_order').value
45
+ #
46
+ # Sure you could fix this through a few methods on your model. But what about
47
+ # saving?
48
+ #
49
+ # current_user.preferences.create :name => 'project_search',
50
+ # :value => "lastname LIKE 'jones%'"
51
+ # current_user.preferences.create :name => 'project_order',
52
+ # :value => "name"
53
+ #
54
+ # Again this seems to much. Again we could add some methods to our model to
55
+ # make this simpler but do we want to do this on every model. NO! So instead
56
+ # we use this plugin which does everything for us.
57
+ #
58
+ # = Capabilities
59
+ #
60
+ # The FlexAttributes plugin is capable of modeling this problem in a intuitive
61
+ # way. Instead of having to deal with a related model you treat all attributes
62
+ # (both on the model and related) as if they are all on the model. The plugin
63
+ # will try to save all attributes to the model (normal ActiveRecord behaviour)
64
+ # but if there is no column for an attribute it will try to save it to a
65
+ # related model whose purpose is to store these many sparsly populated
66
+ # attributes.
67
+ #
68
+ # The main design goals are:
69
+ #
70
+ # * Have the flex attributes feel like normal attributes. Simple gets and sets
71
+ # will add and remove records from the related model.
72
+ # * Allow for more than one related model. So for example on my User model I might
73
+ # have some flex attributes going into a contact_info table while others are
74
+ # going in a user_preferences table.
75
+ # * Allow a model to determine what a valid flex attribute is for a given
76
+ # related model so our model still can generate a NoMethodError.
77
+ # * Have flex attributes work with ActsAsVersioned. We want the versioned
78
+ # models to be able to be related with the correct attributes. If a flex
79
+ # attribute changes it should generate a new version of the model and the old
80
+ # version should still have the old value for the flex attribute.
81
+
82
+
83
+ module ClassMethods
84
+
85
+ # Will make the current class have flex attributes.
86
+ #
87
+ # class User < ActiveRecord::Base
88
+ # has_flex_attributes
89
+ # end
90
+ # eric = User.find_by_login 'eric'
91
+ # puts "My AOL instant message name is: #{eric.aim}"
92
+ # eric.phone = '555-123-4567'
93
+ # eric.save
94
+ #
95
+ # The above example should work even though "aim" and "phone" are not
96
+ # attributes on the User model.
97
+ #
98
+ # The following options are available on for has_flex_attributes to modify
99
+ # the behavior. Reasonable defaults are provided:
100
+ #
101
+ # class_name::
102
+ # The class for the related model. This defaults to the
103
+ # model name prepended to "Attribute". So for a "User" model the class
104
+ # name would be "UserAttribute". The class can actually exist (in that
105
+ # case the model file will be loaded through Rails dependency system) or
106
+ # if it does not exist a basic model will be dynamically defined for you.
107
+ # This allows you to implement custom methods on the related class by
108
+ # simply defining the class manually.
109
+ # table_name::
110
+ # The table for the related model. This defaults to the
111
+ # attribute model's table name.
112
+ # relationship_name::
113
+ # This is the name of the actual has_many
114
+ # relationship. Most of the type this relationship will only be used
115
+ # indirectly but it is there if the user wants more raw access. This
116
+ # defaults to the class name underscored then pluralized finally turned
117
+ # into a symbol.
118
+ # foreign_key::
119
+ # The key in the attribute table to relate back to the
120
+ # model. This defaults to the model name underscored prepended to "_id"
121
+ # name_field::
122
+ # The field which stores the name of the attribute in the related object
123
+ # value_field::
124
+ # The field that stores the value in the related object
125
+ # fields::
126
+ # A list of fields that are valid flex attributes. By default
127
+ # this is "nil" which means that all field are valid. Use this option if
128
+ # you want some fields to go to one flex attribute model while other
129
+ # fields will go to another. As an alternative you can override the
130
+ # #flex_attributes method which will return a list of all valid flex
131
+ # attributes. This is useful if you want to read the list of attributes
132
+ # from another source to keep your code DRY. This method is given a
133
+ # single argument which is the class for the related model. The following
134
+ # provide an example:
135
+ #
136
+ # class User < ActiveRecord::Base
137
+ # has_flex_attributes :class_name => 'UserContactInfo'
138
+ # has_flex_attributes :class_name => 'Preferences'
139
+ #
140
+ # def flex_attributes(model)
141
+ # case model
142
+ # when UserContactInfo
143
+ # %w(email phone aim yahoo msn)
144
+ # when Preference
145
+ # %w(project_search project_order user_search user_order)
146
+ # else Array.new
147
+ # end
148
+ # end
149
+ # end
150
+ #
151
+ # eric = User.find_by_login 'eric'
152
+ # eric.email = 'eric@example.com' # Will save to UserContactInfo model
153
+ # eric.project_order = 'name' # Will save to Preference
154
+ # eric.save # Carries out save so now values are in database
155
+ #
156
+ # Note the else clause in our case statement. Since an empty array is
157
+ # returned for all other models (perhaps added later) then we can be
158
+ # certain that only the above flex attributes are allowed.
159
+ #
160
+ # If both a :fields option and #flex_attributes method is defined the
161
+ # :fields option take precidence. This allows you to easily define the
162
+ # field list inline for one model while implementing #flex_attributes
163
+ # for another model and not having #flex_attributes need to determine
164
+ # what model it is answering for. In both cases the list of flex
165
+ # attributes can be a list of string or symbols
166
+ #
167
+ # A final alternative to :fields and #flex_attributes is the
168
+ # #is_flex_attribute? method. This method is given two arguments. The
169
+ # first is the attribute being retrieved/saved the second is the Model we
170
+ # are testing for. If you override this method then the #flex_attributes
171
+ # method or the :fields option will have no affect. Use of this method
172
+ # is ideal when you want to retrict the attributes but do so in a
173
+ # algorithmic way. The following is an example:
174
+ # class User < ActiveRecord::Base
175
+ # has_flex_attributes :class_name => 'UserContactInfo'
176
+ # has_flex_attributes :class_name => 'Preferences'
177
+ #
178
+ # def is_flex_attribute?(attr, model)
179
+ # case attr.to_s
180
+ # when /^contact_/ then true
181
+ # when /^preference_/ then true
182
+ # else
183
+ # false
184
+ # end
185
+ # end
186
+ # end
187
+ #
188
+ # eric = User.find_by_login 'eric'
189
+ # eric.contact_phone = '555-123-4567'
190
+ # eric.contact_email = 'eric@example.com'
191
+ # eric.preference_project_order = 'name'
192
+ # eric.some_attribute = 'blah' # If some_attribute is not defined on
193
+ # # the model then method not found is thrown
194
+ def has_flex_attributes(options={})
195
+
196
+ # Provide default options
197
+ options[:class_name] ||= self.class_name + 'Attribute'
198
+ options[:table_name] ||= options[:class_name].tableize
199
+ options[:relationship_name] ||= options[:class_name].tableize.to_sym
200
+ options[:foreign_key] ||= self.class_name.foreign_key
201
+ options[:base_foreign_key] ||= self.name.underscore.foreign_key
202
+ options[:name_field] ||= 'name'
203
+ options[:value_field] ||= 'value'
204
+ options[:fields].collect! {|f| f.to_s} unless options[:fields].nil?
205
+ options[:versioned] = options.has_key?(:versioned) ?
206
+ options[:versioned] : false
207
+
208
+ # Init option storage if necessary
209
+ cattr_accessor :flex_options
210
+ self.flex_options ||= Hash.new
211
+
212
+ # Return if already processed.
213
+ return if self.flex_options.keys.include? options[:class_name]
214
+
215
+ # Attempt to load related class. If not create it
216
+ begin
217
+ options[:class_name].constantize
218
+ rescue
219
+ Object.const_set(options[:class_name],
220
+ Class.new(ActiveRecord::Base)).class_eval do
221
+ def self.reloadable? #:nodoc:
222
+ false
223
+ end
224
+ end
225
+ end
226
+
227
+ # Store options
228
+ self.flex_options[options[:class_name]] = options
229
+
230
+ # Mix in instance methods
231
+ send :include, ActiveRecord::FlexAttributes::InstanceMethods
232
+
233
+ # Modify attribute class
234
+ attribute_class = options[:class_name].constantize
235
+ base_class = self.name.underscore.to_sym
236
+ attribute_class.class_eval do
237
+ belongs_to base_class, :foreign_key => options[:base_foreign_key]
238
+ alias_method :base, base_class # For generic access
239
+ if options[:versioned]
240
+ begin
241
+ version_column = column_names.include?('lock_version') ?
242
+ 'lock_version' : 'version'
243
+ def version # :nodoc:
244
+ lock_version
245
+ end if version_column == 'lock_version'
246
+ rescue
247
+ version_column = 'version'
248
+ end
249
+ acts_as_versioned :version_column => version_column
250
+ acts_as_versioned_association base_class, :both_sides => true
251
+ def version_condition_met? # :nodoc:
252
+ base.version_condition_met?
253
+ end
254
+ end
255
+ end
256
+
257
+ # Modify main class
258
+ class_eval do
259
+ has_many options[:relationship_name],
260
+ :class_name => options[:class_name],
261
+ :table_name => options[:table_name],
262
+ :foreign_key => options[:foreign_key],
263
+ :dependent => :destroy
264
+
265
+ if options[:versioned]
266
+ begin
267
+ version_column = column_names.include?('lock_version') ?
268
+ 'lock_version' : 'version'
269
+ def version # :nodoc:
270
+ lock_version
271
+ end if version_column == 'lock_version'
272
+ rescue
273
+ version_column = 'version'
274
+ end
275
+ acts_as_versioned :version_column => version_column
276
+ acts_as_versioned_association options[:relationship_name],
277
+ :both_sides => true
278
+ version_class.send :include,
279
+ ActiveRecord::FlexAttributes::InstanceMethods
280
+ end
281
+
282
+ # The following is only setup once
283
+ unless private_method_defined? :method_missing_without_flex_attributes
284
+
285
+ # Carry out delayed actions before save
286
+ after_validation_on_update :save_modified_flex_attributes
287
+
288
+ # Make attributes seem real
289
+ alias_method :method_missing_without_flex_attributes, :method_missing
290
+ alias_method :method_missing, :method_missing_with_flex_attributes
291
+
292
+ if options[:versioned]
293
+ version_class_alias_method :method_missing_without_flex_attributes, :method_missing
294
+ version_class_alias_method :method_missing, :method_missing_with_flex_attributes
295
+ end
296
+
297
+ private
298
+
299
+ alias_method :read_attribute_without_flex_attributes, :read_attribute
300
+ alias_method :read_attribute, :read_attribute_with_flex_attributes
301
+ alias_method :write_attribute_without_flex_attributes, :write_attribute
302
+ alias_method :write_attribute, :write_attribute_with_flex_attributes
303
+
304
+ if options[:versioned]
305
+ version_class_alias_method :read_attribute_without_flex_attributes, :read_attribute
306
+ version_class_alias_method :read_attribute, :read_attribute_with_flex_attributes
307
+ version_class_alias_method :write_attribute_without_flex_attributes, :write_attribute
308
+ version_class_alias_method :write_attribute, :write_attribute_with_flex_attributes
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ private
315
+
316
+ # Will alias a method on the versioned class
317
+ def version_class_alias_method(new, old)
318
+ version_class.send(:alias_method, new, old)
319
+ end
320
+
321
+ # Will return the version class when dealing with a versioned object
322
+ def version_class
323
+ "#{name}::Version".constantize
324
+ end
325
+ end
326
+
327
+ module InstanceMethods
328
+
329
+ # Will determine if the given attribute is a flex attribute on the
330
+ # given model. Override this in your class to provide custom logic if
331
+ # the #flex_attributes method or the :fields option are not flexible
332
+ # enough. If you override this method :fields and #flex_attributes will
333
+ # not apply at all unless you implement them yourself.
334
+ def is_flex_attribute?(attr, model)
335
+ attr = attr.to_s
336
+ return flex_options[model.name][:fields].include?(attr) unless
337
+ flex_options[model.name][:fields].nil?
338
+ return flex_attributes(model).collect {|f| f.to_s}.include?(attr) unless
339
+ flex_attributes(model).nil?
340
+ true
341
+ end
342
+
343
+ # Return a list of valid flex attributes for the given model. Return
344
+ # nil if any field is allowed. If you want to say no field is allowed
345
+ # then return an empty array. If you just have a static list the :fields
346
+ # option is most likely easier.
347
+ def flex_attributes(model); nil end
348
+
349
+ private
350
+
351
+ # Called after validation on update so that flex attributes behave
352
+ # like normal attributes in the fact that the database is not touched
353
+ # until save is called.
354
+ def save_modified_flex_attributes
355
+ return if @save_flex_attr.nil?
356
+ @save_flex_attr.each do |s|
357
+ model, attr_name = s
358
+ related_attr = flex_related_attr model, attr_name
359
+ unless related_attr.nil?
360
+ if related_attr.value.nil?
361
+ flex_related(model).delete related_attr
362
+ else
363
+ related_attr.save
364
+ end
365
+ end
366
+ end
367
+ @save_flex_attr = []
368
+ end
369
+
370
+ # Overrides ActiveRecord::Base#read_attribute
371
+ def read_attribute_with_flex_attributes(attr_name)
372
+ attr_name = attr_name.to_s
373
+ exec_if_related attr_name do |model|
374
+ return nil if !@remove_flex_attr.nil? && @remove_flex_attr.any? do |r|
375
+ r[0] == model && r[1] == attr_name
376
+ end
377
+ value_field = flex_options[model.name][:value_field]
378
+ related_attr = flex_related_attr model, attr_name
379
+ return nil if related_attr.nil?
380
+ return related_attr.send(value_field)
381
+ end
382
+ read_attribute_without_flex_attributes(attr_name)
383
+ end
384
+
385
+ # Overrides ActiveRecord::Base#write_attribute
386
+ def write_attribute_with_flex_attributes(attr_name, value)
387
+ attr_name = attr_name.to_s
388
+ exec_if_related attr_name do |model|
389
+ value_field = flex_options[model.name][:value_field]
390
+ @save_flex_attr ||= []
391
+ @save_flex_attr << [model, attr_name]
392
+ related_attr = flex_related_attr(model, attr_name)
393
+ if related_attr.nil?
394
+ # Used to check for nil? but this caused validation
395
+ # problems that are harder to solve. blank? is probably
396
+ # not correct but it works well for now.
397
+ unless value.blank?
398
+ name_field = flex_options[model.name][:name_field]
399
+ foreign_key = flex_options[model.name][:foreign_key]
400
+ flex_related(model).build name_field => attr_name,
401
+ value_field => value, foreign_key => self.id
402
+ end
403
+ return value
404
+ else
405
+ value_field = (value_field.to_s + '=').to_sym
406
+ return related_attr.send(value_field, value)
407
+ end
408
+ end
409
+ write_attribute_without_flex_attributes(attr_name, value)
410
+ end
411
+
412
+ # Implements flex-attributes as if real getter/setter methods
413
+ # were defined.
414
+ def method_missing_with_flex_attributes(method_id, *args, &block)
415
+ begin
416
+ method_missing_without_flex_attributes method_id, *args, &block
417
+ rescue NoMethodError => e
418
+ attr_name = method_id.to_s.sub(/\=$/, '')
419
+ exec_if_related attr_name do |model|
420
+ if method_id.to_s =~ /\=$/
421
+ return write_attribute_with_flex_attributes(attr_name, args[0])
422
+ else
423
+ return read_attribute_with_flex_attributes(attr_name)
424
+ end
425
+ end
426
+ raise e
427
+ end
428
+ end
429
+
430
+ # Retrieve the related flex attribute object
431
+ def flex_related_attr(model, attr)
432
+ name_field = flex_options[model.name][:name_field]
433
+ flex_related(model).to_a.find {|r| r.send(name_field) == attr}
434
+ end
435
+
436
+ # Retrieve the collection of related flex attributes
437
+ def flex_related(model)
438
+ relationship = flex_options[model.name][:relationship_name]
439
+ send relationship
440
+ end
441
+
442
+ # Yield only if attr_name is a flex_attribute
443
+ def exec_if_related(attr_name)
444
+ return false if self.class.column_names.include? attr_name
445
+ each_flex_relation do |model|
446
+ if is_flex_attribute? attr_name, model
447
+ yield model
448
+ end
449
+ end
450
+ end
451
+
452
+ # Yields for each flex relation.
453
+ def each_flex_relation
454
+ flex_options.keys.each {|kls| yield kls.constantize}
455
+ end
456
+
457
+ # Returns the options for the flex attributes
458
+ def flex_options
459
+ nonversioned_class(self.class).flex_options
460
+ end
461
+
462
+ # Will return the parent model if kls is a versioned class
463
+ def nonversioned_class(kls)
464
+ if kls.name =~ /\:\:Version$/
465
+ base_class = kls.name
466
+ base_class.sub! /\:\:Version$/, ''
467
+ return base_class.constantize
468
+ end
469
+ kls
470
+ end
471
+
472
+ end
473
+ end
474
+
475
+ ActiveRecord::Base.class_eval { include FlexAttributes }
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pjb3-flex-attributes
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Eric Anderson
8
+ - Paul Barry
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2008-08-26 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Flex attributes allow for the common but questionable database design of storing attributes in a thin key/value table related to some model.
18
+ email: erci (at) afaik (dot) us
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - lib/flex-attributes.rb
25
+ - README
26
+ files:
27
+ - flex-attributes.gemspec
28
+ - lib/flex-attributes.rb
29
+ - Manifest
30
+ - README
31
+ - MIT-LICENSE
32
+ has_rdoc: true
33
+ homepage: http://github.com/pjb3/flex-attributes
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --line-numbers
37
+ - --inline-source
38
+ - --title
39
+ - Flex-Attributes
40
+ - --main
41
+ - README
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: flex-attributes
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: Gem version of flex-attributes Rails plugin.
63
+ test_files: []
64
+