has_dynamic_fields 0.0.1 → 0.0.2
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/VERSION +1 -1
- data/has_dynamic_fields.gemspec +85 -0
- data/lib/has_dynamic_fields.rb +2 -0
- data/lib/has_dynamic_fields/base.rb +14 -0
- data/lib/has_dynamic_fields/has_dynamic_fields.rb +297 -0
- data/lib/has_dynamic_fields/railtie.rb +21 -0
- data/test/test_has_dynamic_fields.rb +7 -0
- metadata +7 -1
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "has_dynamic_fields"
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jason Ayre"]
|
12
|
+
s.date = "2012-04-16"
|
13
|
+
s.description = "Lets your models act dynamic in a clean EAV style"
|
14
|
+
s.email = "jasonayre@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"has_dynamic_fields.gemspec",
|
28
|
+
"lib/generators/dynamic_field_migration_generator.rb",
|
29
|
+
"lib/generators/dynamic_field_scaffold_generator.rb",
|
30
|
+
"lib/generators/templates/add_field_migration.rb",
|
31
|
+
"lib/generators/templates/fieldgroup_model_template.rb",
|
32
|
+
"lib/generators/templates/fieldgroups_table_migration.rb",
|
33
|
+
"lib/generators/templates/fieldoptions_model_template.rb",
|
34
|
+
"lib/generators/templates/fieldoptions_table_migration.rb",
|
35
|
+
"lib/generators/templates/fields_model_template.rb",
|
36
|
+
"lib/generators/templates/fields_table_migration.rb",
|
37
|
+
"lib/generators/templates/fieldvalues_model_template.rb",
|
38
|
+
"lib/generators/templates/fieldvalues_table_migration.rb",
|
39
|
+
"lib/generators/templates/remove_field_migration.rb",
|
40
|
+
"lib/has_dynamic_fields.rb",
|
41
|
+
"lib/has_dynamic_fields/base.rb",
|
42
|
+
"lib/has_dynamic_fields/has_dynamic_fields.rb",
|
43
|
+
"lib/has_dynamic_fields/railtie.rb",
|
44
|
+
"lib/tasks/dynamic_field_migrate.rake",
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"test/helper.rb",
|
47
|
+
"test/test_has_dynamic_fields.rb"
|
48
|
+
]
|
49
|
+
s.homepage = "http://github.com/jasonayre/has_dynamic_fields"
|
50
|
+
s.licenses = ["MIT"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = "1.8.22"
|
53
|
+
s.summary = "Lets your models act dynamically in a clean EAV style"
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_runtime_dependency(%q<seed-fu>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
61
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<seed-fu>, [">= 0"])
|
68
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
69
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
71
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
73
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<seed-fu>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
78
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
79
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
80
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
81
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
82
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require "active_record"
|
2
|
+
module HasDynamicFields
|
3
|
+
|
4
|
+
module Base
|
5
|
+
def has_dynamic_fields(options = {})
|
6
|
+
|
7
|
+
options[:entity_class_name] ||= "Entity"
|
8
|
+
options[:entity_table_name] ||= options[:entity_class_name].tableize
|
9
|
+
options[:entity_foreign_key] ||= "#{options[:entity_class_name].tableize.singularize}_id".to_sym
|
10
|
+
options[:entity_singular] ||= options[:entity_class_name].tableize.singularize
|
11
|
+
options[:entity_plural] ||= options[:entity_class_name].tableize
|
12
|
+
|
13
|
+
options[:value_class_name] ||= "DynamicFieldValue"
|
14
|
+
options[:value_table_name] ||= options[:value_class_name].tableize
|
15
|
+
options[:value_foreign_key] ||= options[:value_class_name].tableize.singularize.to_sym
|
16
|
+
options[:value_singular] ||= options[:value_class_name].tableize.singularize
|
17
|
+
options[:value_plural] ||= options[:value_class_name].tableize
|
18
|
+
|
19
|
+
options[:field_class_name] ||= "DynamicField"
|
20
|
+
options[:field_table_name] ||= options[:field_class_name].tableize
|
21
|
+
options[:field_foreign_key] ||= "#{options[:field_class_name].tableize.singularize}_id".to_sym
|
22
|
+
options[:field_singular] ||= options[:field_class_name].tableize.singularize
|
23
|
+
options[:field_plural] ||= options[:field_class_name].tableize
|
24
|
+
|
25
|
+
options[:fieldgroup_class_name] ||= "DynamicFieldGroup"
|
26
|
+
options[:fieldgroup_table_name] ||= options[:fieldgroup_class_name].tableize
|
27
|
+
options[:fieldgroup_foreign_key] ||= "#{options[:fieldgroup_class_name].tableize.singularize}_id".to_sym
|
28
|
+
options[:fieldgroup_singular] ||= options[:fieldgroup_class_name].tableize.singularize
|
29
|
+
options[:fieldgroup_plural] ||= options[:fieldgroup_class_name].tableize
|
30
|
+
|
31
|
+
options[:fieldoptions_class_name] ||= "DynamicFieldOptions"
|
32
|
+
options[:fieldoptions_table_name] ||= options[:fieldoptions_class_name].tableize
|
33
|
+
options[:fieldoptions_foreign_key] ||= "#{options[:fieldoptions_class_name].tableize.singularize}_id".to_sym
|
34
|
+
options[:fieldoptions_singular] ||= options[:fieldoptions_class_name].tableize.singularize
|
35
|
+
options[:fieldoptions_plural] ||= options[:fieldoptions_class_name].tableize
|
36
|
+
|
37
|
+
options[:entity_klass] = options[:entity_class_name].constantize
|
38
|
+
options[:field_klass] = options[:field_class_name].constantize
|
39
|
+
options[:value_klass] = options[:value_class_name].constantize
|
40
|
+
options[:fieldgroup_klass] = options[:fieldgroup_class_name].constantize
|
41
|
+
options[:fieldoptions_klass] = options[:fieldoptions_class_name].constantize
|
42
|
+
|
43
|
+
|
44
|
+
cattr_accessor :aad_options
|
45
|
+
self.aad_options ||= Hash.new
|
46
|
+
|
47
|
+
# Return if already present
|
48
|
+
return if self.aad_options.keys.include? options[:entity_class_name]
|
49
|
+
|
50
|
+
self.aad_options = options
|
51
|
+
|
52
|
+
include InstanceMethods
|
53
|
+
|
54
|
+
entity_klass = options[:entity_class_name].constantize
|
55
|
+
field_klass = options[:field_class_name].constantize
|
56
|
+
value_klass = options[:value_class_name].constantize
|
57
|
+
fieldgroup_klass = options[:fieldgroup_class_name].constantize
|
58
|
+
fieldoptions_klass = options[:fieldoptions_class_name].constantize
|
59
|
+
|
60
|
+
#eval entity class
|
61
|
+
class_eval do
|
62
|
+
|
63
|
+
#this can be confusing, because I chose to use a horizontal scaling eav pattern due to file upload/carrierwave,
|
64
|
+
#this may not make a ton of sense if you look at it out of context and without looking @ your db.
|
65
|
+
#entity has one value, but that value row contains all the possible field values, entity type specific field values..
|
66
|
+
#are controlled through fieldgroup, each entity has 1 fieldgroup (category,channel,type, whatever), and 1 value row
|
67
|
+
|
68
|
+
has_one options[:value_singular].to_sym
|
69
|
+
accepts_nested_attributes_for options[:value_singular].to_sym, :allow_destroy => true
|
70
|
+
belongs_to options[:fieldgroup_singular].to_sym
|
71
|
+
|
72
|
+
#because field relationships are not automatically saved, we need to set flag then do it explicitly
|
73
|
+
before_update :update_dynamic_fields, :if => :update_dynamic_fields?
|
74
|
+
before_create :create_dynamic_field_value
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
#eval value class
|
79
|
+
value_klass.class_eval do
|
80
|
+
|
81
|
+
if (ActiveRecord::Base.connection.table_exists?(options[:field_table_name]) && ActiveRecord::Base.connection.table_exists?(options[:value_table_name]))
|
82
|
+
|
83
|
+
#mount file fields if carrierwave being used
|
84
|
+
if value_klass.send(:respond_to?, :mount_uploader)
|
85
|
+
@file_fields = field_klass.where(:fieldtype => "file")
|
86
|
+
@file_fields.each do |field|
|
87
|
+
mount_uploader "field_#{field.id}".to_sym, "#{value_klass.name}Uploader".constantize
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
belongs_to options[:entity_singular].to_sym
|
93
|
+
belongs_to options[:fieldgroup_singular].to_sym
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
field_klass.class_eval do
|
98
|
+
|
99
|
+
#note your field model must have at least a column or method called name
|
100
|
+
before_create :format_name
|
101
|
+
after_create :dynamic_field_add_column_migration
|
102
|
+
before_destroy :dynamic_field_remove_column_migration
|
103
|
+
|
104
|
+
has_many options[:fieldoptions_plural].to_sym
|
105
|
+
|
106
|
+
belongs_to options[:fieldgroup_singular].to_sym
|
107
|
+
|
108
|
+
cattr_accessor :aad_options
|
109
|
+
self.aad_options = options
|
110
|
+
|
111
|
+
def dynamic_field_add_column_migration
|
112
|
+
|
113
|
+
except ||= %w{created_at updated_at}
|
114
|
+
except_column_types = [:decimal, :date, :datetime]
|
115
|
+
except << self.class.name.constantize.columns.collect {|k| k.name if except_column_types.include?(k.type) }.reject { |val| val == nil }
|
116
|
+
|
117
|
+
except.flatten!
|
118
|
+
|
119
|
+
`rails g dynamic_field_migration add_field_#{self.id}_to_#{self.aad_options[:value_table_name]} field_#{self.id}:string`
|
120
|
+
|
121
|
+
SeedFu::Writer.write("db/fixtures/#{self.aad_options[:field_table_name]}.rb", :class_name => self.aad_options[:field_class_name], :constraints => [:name, self.aad_options[:fieldgroup_foreign_key]]) do |writer|
|
122
|
+
self.class.name.constantize.all.each do |f|
|
123
|
+
@attrs = f.attributes.reject { |k,v| except.include?(k) }
|
124
|
+
writer.add(@attrs)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
def dynamic_field_remove_column_migration
|
131
|
+
|
132
|
+
except ||= %w{created_at updated_at}
|
133
|
+
except_column_types = [:decimal, :date, :datetime]
|
134
|
+
except << self.class.name.constantize.columns.collect {|k| k.name if except_column_types.include?(k.type) }.reject { |val| val == nil }
|
135
|
+
|
136
|
+
except.flatten!
|
137
|
+
|
138
|
+
`rails g dynamic_field_migration remove_field_#{self.id}_from_#{self.aad_options[:value_table_name]} field_#{self.id}`
|
139
|
+
`rake db:migrate_dynamic_fields`
|
140
|
+
SeedFu::Writer.write("db/fixtures/#{self.aad_options[:field_table_name]}.rb", :class_name => self.aad_options[:field_class_name], :constraints => [:name, self.aad_options[:fieldgroup_foreign_key]]) do |writer|
|
141
|
+
self.class.name.constantize.all.each do |f|
|
142
|
+
@attrs = f.attributes.reject { |k,v| except.include?(k) }
|
143
|
+
writer.add(@attrs)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def format_name
|
149
|
+
self.name = self.name.split(" ").join("_").downcase
|
150
|
+
end
|
151
|
+
|
152
|
+
def write_seed_data
|
153
|
+
SeedFu::Writer.write("db/fixtures/#{self.aad_options[:field_table_name]}.rb", :class_name => self.aad_options[:field_class_name], :constraints => [:name, self.aad_options[:fieldgroup_foreign_key]]) do |writer|
|
154
|
+
self.class.name.constantize.all.each do |f|
|
155
|
+
@attrs = f.attributes.reject { |k,v| except.include?(k) }
|
156
|
+
writer.add(@attrs)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
fieldoptions_klass.class_eval do
|
165
|
+
|
166
|
+
belongs_to options[:field_singular].to_sym
|
167
|
+
after_update :write_seed_data
|
168
|
+
|
169
|
+
if self.respond_to? :mount_uploader
|
170
|
+
mount_uploader :image, "#{options[:fieldoptions_class_name]}Uploader".constantize
|
171
|
+
end
|
172
|
+
|
173
|
+
cattr_accessor :aad_options
|
174
|
+
self.aad_options = options
|
175
|
+
|
176
|
+
def write_seed_data
|
177
|
+
|
178
|
+
except ||= %w{created_at updated_at}
|
179
|
+
except_column_types = [:decimal, :date, :datetime]
|
180
|
+
except << self.class.name.constantize.columns.collect {|k| k.name if except_column_types.include?(k.type) }.reject { |val| val == nil }
|
181
|
+
|
182
|
+
except.flatten!
|
183
|
+
|
184
|
+
SeedFu::Writer.write("db/fixtures/#{self.aad_options[:fieldoptions_table_name]}.rb", :class_name => self.aad_options[:fieldoptions_class_name], :constraints => [:value, self.aad_options[:field_foreign_key]]) do |writer|
|
185
|
+
self.class.name.constantize.all.each do |f|
|
186
|
+
@attrs = f.attributes.reject { |k,v| except.include?(k) }
|
187
|
+
writer.add(@attrs)
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
# todo: make fieldgroup optional
|
196
|
+
fieldgroup_klass.class_eval do
|
197
|
+
has_many options[:field_table_name].to_sym
|
198
|
+
has_many options[:value_table_name].to_sym, :through => options[:field_table_name].to_sym, :source => options[:entity_singular].to_sym
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
module InstanceMethods
|
204
|
+
|
205
|
+
def show_options
|
206
|
+
puts self.class.aad_options.inspect
|
207
|
+
puts self.aad_options.inspect
|
208
|
+
end
|
209
|
+
|
210
|
+
def update_dynamic_fields?
|
211
|
+
instance_variable_get("@update_dynamic_fields")
|
212
|
+
end
|
213
|
+
|
214
|
+
def update_dynamic_fields=(val)
|
215
|
+
instance_variable_set("@update_dynamic_fields",val)
|
216
|
+
end
|
217
|
+
|
218
|
+
def update_dynamic_fields
|
219
|
+
self.send(self.aad_options[:value_singular].to_sym).save
|
220
|
+
end
|
221
|
+
|
222
|
+
def create_dynamic_field_value
|
223
|
+
#save relationship upon initial creation, so that fieldgroup specific fields can now be entered
|
224
|
+
#todo: make the whole fieldgroup optional for simple talbles
|
225
|
+
|
226
|
+
value_model = self.aad_options[:value_klass]
|
227
|
+
value_model = value_model.create!({ self.aad_options[:fieldgroup_singular].to_sym => self.send(self.aad_options[:fieldgroup_singular]) })
|
228
|
+
|
229
|
+
self.send("#{self.aad_options[:value_singular]}=".to_sym, value_model)
|
230
|
+
end
|
231
|
+
|
232
|
+
def dynamic_field_keys
|
233
|
+
#todo: rename this dynamic_fieldgroup_field_keys for simple tables
|
234
|
+
return if self.send(self.aad_options[:fieldgroup_singular].to_sym).blank?
|
235
|
+
keys = []
|
236
|
+
dynamic_fieldgroup_field_keys = self.send(self.aad_options[:fieldgroup_singular].to_sym).send(self.aad_options[:field_plural].to_sym)
|
237
|
+
dynamic_fieldgroup_field_keys.each do |o|
|
238
|
+
keys << o.name.to_sym
|
239
|
+
end
|
240
|
+
keys
|
241
|
+
end
|
242
|
+
|
243
|
+
def dynamic_field_inputs
|
244
|
+
fields = []
|
245
|
+
dynamic_fields = self.send(self.aad_options[:fieldgroup_singular].to_sym).send(self.aad_options[:field_plural].to_sym)
|
246
|
+
dynamic_fields.each do |field|
|
247
|
+
fields << field
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def method_missing(name, *args)
|
252
|
+
|
253
|
+
return if self.send(self.aad_options[:fieldgroup_singular].to_sym).blank?
|
254
|
+
m = name.to_s
|
255
|
+
type = :reader
|
256
|
+
attribute_name = name.to_s.sub(/=$/) do
|
257
|
+
type = :writer
|
258
|
+
""
|
259
|
+
end
|
260
|
+
|
261
|
+
fieldgroup_fields = self.send(self.aad_options[:fieldgroup_singular].to_sym).send(self.aad_options[:field_plural].to_sym)
|
262
|
+
@dynamic_field_keys ||= instance_variable_set("@dynamic_field_keys", fieldgroup_fields.collect {|o| o.name.to_sym })
|
263
|
+
@dynamic_fields ||= instance_variable_set("@dynamic_fields", fieldgroup_fields)
|
264
|
+
|
265
|
+
if @dynamic_field_keys.include?(attribute_name.to_sym)
|
266
|
+
|
267
|
+
field = @dynamic_fields.select { |field| field.name.to_sym == attribute_name.to_sym && field.send(self.aad_options[:fieldgroup_singular]).id == self.send(self.aad_options[:fieldgroup_singular]).id }.first
|
268
|
+
|
269
|
+
case(type)
|
270
|
+
|
271
|
+
when :writer
|
272
|
+
self.update_dynamic_attributes=(true)
|
273
|
+
self.class.send(:define_method, name) do |value|
|
274
|
+
self.send(self.aad_options[:value_singular].to_sym).send("field_#{field.id}=".to_sym, value)
|
275
|
+
end
|
276
|
+
else
|
277
|
+
self.class.send(:define_method, name) do
|
278
|
+
self.send(self.aad_options[:value_singular]).send("field_#{field.id}".to_sym, *args)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
#former only set the methods now we actually have to execute them
|
282
|
+
send(name, *args)
|
283
|
+
|
284
|
+
else
|
285
|
+
# commenting out super because its throwing undefined method field_ changed in carrierwave? i think this is bad
|
286
|
+
# super
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "has_dynamic_fields"
|
2
|
+
require "rails"
|
3
|
+
|
4
|
+
module HasDynamicFields
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
initializer 'has_dynamic_fields.ar_extensions' do |app|
|
7
|
+
require 'has_dynamic_fields/has_dynamic_fields' if defined?(Rails)
|
8
|
+
# ActiveRecord::Base.extend HasDynamicFields::Base
|
9
|
+
ActiveRecord::Base.send :extend, HasDynamicFields::Base
|
10
|
+
end
|
11
|
+
|
12
|
+
generators do
|
13
|
+
require "generators/dynamic_field_migration_generator"
|
14
|
+
end
|
15
|
+
|
16
|
+
rake_tasks do
|
17
|
+
load "tasks/dynamic_field_migrate.rake"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_dynamic_fields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -138,6 +138,7 @@ files:
|
|
138
138
|
- README.rdoc
|
139
139
|
- Rakefile
|
140
140
|
- VERSION
|
141
|
+
- has_dynamic_fields.gemspec
|
141
142
|
- lib/generators/dynamic_field_migration_generator.rb
|
142
143
|
- lib/generators/dynamic_field_scaffold_generator.rb
|
143
144
|
- lib/generators/templates/add_field_migration.rb
|
@@ -150,9 +151,14 @@ files:
|
|
150
151
|
- lib/generators/templates/fieldvalues_model_template.rb
|
151
152
|
- lib/generators/templates/fieldvalues_table_migration.rb
|
152
153
|
- lib/generators/templates/remove_field_migration.rb
|
154
|
+
- lib/has_dynamic_fields.rb
|
155
|
+
- lib/has_dynamic_fields/base.rb
|
156
|
+
- lib/has_dynamic_fields/has_dynamic_fields.rb
|
157
|
+
- lib/has_dynamic_fields/railtie.rb
|
153
158
|
- lib/tasks/dynamic_field_migrate.rake
|
154
159
|
- spec/spec_helper.rb
|
155
160
|
- test/helper.rb
|
161
|
+
- test/test_has_dynamic_fields.rb
|
156
162
|
homepage: http://github.com/jasonayre/has_dynamic_fields
|
157
163
|
licenses:
|
158
164
|
- MIT
|