yaddl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +32 -0
  4. data/lib/tasks/yaddl_tasks.rake +22 -0
  5. data/lib/yaddl.rb +810 -0
  6. data/lib/yaddl/version.rb +3 -0
  7. data/test/dummy/README.rdoc +28 -0
  8. data/test/dummy/Rakefile +6 -0
  9. data/test/dummy/app/assets/javascripts/application.js +13 -0
  10. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  11. data/test/dummy/app/controllers/application_controller.rb +5 -0
  12. data/test/dummy/app/helpers/application_helper.rb +2 -0
  13. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  14. data/test/dummy/bin/bundle +3 -0
  15. data/test/dummy/bin/rails +4 -0
  16. data/test/dummy/bin/rake +4 -0
  17. data/test/dummy/config.ru +4 -0
  18. data/test/dummy/config/application.rb +23 -0
  19. data/test/dummy/config/boot.rb +5 -0
  20. data/test/dummy/config/database.yml +25 -0
  21. data/test/dummy/config/environment.rb +5 -0
  22. data/test/dummy/config/environments/development.rb +29 -0
  23. data/test/dummy/config/environments/production.rb +80 -0
  24. data/test/dummy/config/environments/test.rb +36 -0
  25. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  26. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  27. data/test/dummy/config/initializers/inflections.rb +16 -0
  28. data/test/dummy/config/initializers/mime_types.rb +5 -0
  29. data/test/dummy/config/initializers/secret_token.rb +12 -0
  30. data/test/dummy/config/initializers/session_store.rb +3 -0
  31. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  32. data/test/dummy/config/locales/en.yml +23 -0
  33. data/test/dummy/config/routes.rb +56 -0
  34. data/test/dummy/db/test.sqlite3 +0 -0
  35. data/test/dummy/public/404.html +58 -0
  36. data/test/dummy/public/422.html +58 -0
  37. data/test/dummy/public/500.html +57 -0
  38. data/test/dummy/public/favicon.ico +0 -0
  39. data/test/test_helper.rb +19 -0
  40. data/test/yaddl_test.rb +19 -0
  41. metadata +153 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Brant Wedel
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.
@@ -0,0 +1,3 @@
1
+ = Yaddl
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Yaddl'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,22 @@
1
+ namespace :yaddl do
2
+ desc "Print generated yaml ddl to console"
3
+ task :review do
4
+ y = Yaddl::Yaddl::load("#{Rails.root}/db/*.yaddl")
5
+ y.review()
6
+ end
7
+ desc "Scaffold controllers views and models, skips migrations"
8
+ task :scaffold do
9
+ y = Yaddl::Yaddl::load("#{Rails.root}/db/*.yaddl")
10
+ y.generate("--force --no-assets --skip-migration --helpers=false")
11
+ end
12
+ desc "Regenerate models, skips migrations"
13
+ task :models do
14
+ y = Yaddl::Yaddl::load("#{Rails.root}/db/*.yaddl")
15
+ y.generate("--no-scaffolds")
16
+ end
17
+ desc "Generate migrations, models, controllers and views"
18
+ task :generate do
19
+ y = Yaddl::Yaddl::load("#{Rails.root}/db/*.yaddl")
20
+ y.generate("--force --no-assets --helpers=false")
21
+ end
22
+ end
@@ -0,0 +1,810 @@
1
+ class Yaddl::Yaddl
2
+
3
+ attr_accessor :markup
4
+ attr_accessor :models
5
+ attr_accessor :quiet
6
+
7
+ def self.load(filename)
8
+ y = Yaddl.new
9
+ if filename.include? "*"
10
+ y.markup = ""
11
+ Dir.glob(filename).each do |fn|
12
+ y.markup += File.read(fn) + "\r\n"
13
+ end
14
+ else
15
+ y.markup = File.read(filename)
16
+ end
17
+ y.models = {}
18
+ y
19
+ end
20
+
21
+ def parse_attribute(attribute)
22
+ at = attribute.split(/\:\*?/)
23
+ name = at[0]
24
+ type = "string"
25
+ if at.count > 1
26
+ type = at[1]
27
+ else
28
+ type = "integer" if name =~ /_id$|^id$|_nr$|^nr$|^number$|_number$/
29
+ type = "datetime" if name =~ /_at$|^date$|_date$|_on$|_time$/
30
+ end
31
+ h = { 'name' => at[0], 'type' => type }
32
+ h['default_type'] = at.count == 1
33
+ h
34
+ end
35
+
36
+ def review
37
+ parse
38
+ puts("","--- DATA #{models.to_yaml}","") unless @quiet
39
+ end
40
+
41
+ def generate(options = "")
42
+ @quiet = options.include?("--quiet")
43
+ scaffolds(options)
44
+ end
45
+
46
+ def indent(string, count, char = ' ')
47
+ string.gsub(/([^\n]*)(\n|$)/) do |match|
48
+ last_iteration = ($1 == "" && $2 == "")
49
+ line = ""
50
+ line << (char * count) unless last_iteration || $1 == ""
51
+ line << $1
52
+ line << $2
53
+ line
54
+ end
55
+ end
56
+
57
+ def scaffolds(options = "")
58
+ parse
59
+
60
+ puts("","--- SCAFFOLDS ---","") unless @quiet
61
+
62
+ models.reject{ |k,v| k[0] == "@"}.each do |name,model|
63
+ model['has_one'] ||= {}
64
+ model['has_many'] ||= {}
65
+ model['belongs_to'] ||= {}
66
+ model['attributes'] ||= {}
67
+ model['methods'] ||= {}
68
+ model['code'] ||= {}
69
+ model['code']['top'] ||= []
70
+ model['code']['before'] ||= []
71
+ model['code']['after'] ||= []
72
+ model['code']['controller'] ||= []
73
+
74
+ sc = "rails g scaffold #{name} " + model['attributes'].map{ |k,v| k + ':' + v['type'].sub(/yaml|hash|object|cache/i,"text") }.join(' ') + " " + model['belongs_to'].map{ |k,v| k + ':references' + (v['polymorphic'] ? "{polymorphic}" : "") }.join(' ')
75
+ puts("model: #{sc}") unless @quiet
76
+ `#{sc} #{options}`
77
+
78
+ File.delete("#{Rails.root}/app/views/#{name.underscore.pluralize}/index.json.jbuilder")
79
+ sc = "rails g scaffold #{name} " + model['methods'].reject{ | k, v| !v['primary_ref'] || v['hidden'] }.map{ |k,v| k + ':' + v['type'].to_s.sub(/yaml|hash|object|cache/i,"text").sub(/^$/,"string") }.join(' ') + " " + model['attributes'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':' + v['type'].sub(/yaml|hash|object|cache/i,"text") }.join(' ') + " " + model['belongs_to'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':references' + (v['polymorphic'] ? "{polymorphic}" : "") }.join(' ')
80
+ puts("index: #{sc}") unless @quiet
81
+ `#{sc} #{options.gsub("--force","")} --skip --no-migrations`
82
+
83
+ File.delete("#{Rails.root}/app/views/#{name.underscore.pluralize}/show.json.jbuilder")
84
+ File.delete("#{Rails.root}/app/views/#{name.underscore.pluralize}/show.html.erb")
85
+ sc = "rails g scaffold #{name} " + model['methods'].reject{ | k, v| k == "to_s" || v['hidden'] }.map{ |k,v| k + ':' + v['type'].to_s.sub(/yaml|hash|object|cache/i,"text").sub(/^$/,"string") }.join(' ') + " " + model['attributes'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':' + v['type'].sub(/yaml|hash|object|cache/i,"text") }.join(' ') + " " + model['belongs_to'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':references' + (v['polymorphic'] ? "{polymorphic}" : "") }.join(' ')
86
+ puts("show: #{sc}") unless @quiet
87
+ `#{sc} #{options.gsub("--force","")} --skip --no-migrations`
88
+
89
+ File.delete("#{Rails.root}/app/views/#{name.underscore.pluralize}/_form.html.erb")
90
+ sc = "rails g scaffold #{name} " + model['attributes'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':' + v['type'].sub(/yaml|hash|object|cache/i,"text") }.join(' ') + " " + model['belongs_to'].reject{ | k, v| v['hidden'] }.map{ |k,v| k + ':references' + (v['polymorphic'] ? "{polymorphic}" : "") }.join(' ')
91
+ puts("form: #{sc}") unless @quiet
92
+ `#{sc} #{options.gsub("--force","")} --skip --no-migrations`
93
+ end if !options.include?("--no-scaffold")
94
+
95
+
96
+ models.reject{ |k,v| k[0] == "@"}.each do |name,model|
97
+ model['has_one'] ||= {}
98
+ model['has_many'] ||= {}
99
+ model['belongs_to'] ||= {}
100
+ model['attributes'] ||= {}
101
+ model['methods'] ||= {}
102
+ model['code'] ||= {}
103
+ model['code']['top'] ||= []
104
+ model['code']['before'] ||= []
105
+ model['code']['after'] ||= []
106
+ model['code']['controller'] ||= []
107
+
108
+ file = "class #{name} < ActiveRecord::Base"
109
+ model['code']['top'].each do |code|
110
+ file += "\n #{code.each_line{|l| " " + l}}"
111
+ end
112
+ file += "\n" if model['code']['top'].length > 0
113
+
114
+ model['belongs_to'].each do |k,assoc|
115
+ file += "\n belongs_to :#{k}"
116
+ file += ", #{assoc.reject{|k,v| k == "hidden" || k == "class_names" || (k == "class_name" && assoc['polymorphic'])}.map{|k,v| v == true || v == false || v.is_a?(Array) || v.downcase == v ? "#{k}: #{":" unless v == true || v == false || v.is_a?(Array) }#{v}" : "#{k}: \"#{v}\"" }.join(', ')}" if assoc.reject{|k,v| k == "hidden" || k == "class_names" || (k == "class_name" && assoc['polymorphic'])}.count > 0
117
+ end
118
+
119
+ model['has_one'].each do |k,assoc|
120
+ file += "\n has_one :#{k.underscore}"
121
+ file += ", #{assoc.reject{|k,v| k == "hidden" || k == "class_names"}.map{|k,v| v == true || v == false || v.is_a?(Array) || v.downcase == v ? "#{k}: #{":" unless v == true || v == false || v.is_a?(Array) }#{v}" : "#{k}: \"#{v}\"" }.join(', ')}" if assoc.reject{|k,v| k == "hidden" || k == "class_names"}.count > 0
122
+ end
123
+
124
+ model['has_many'].reject{|k,v| v['polymorphic']}.each do |k,assoc|
125
+ file += "\n has_many :#{k.pluralize.underscore}"
126
+ file += ", #{assoc.reject{|k,v| k == "hidden" || k == "polymorphic" || k == "class_names"}.map{|k,v| v == true || v == false || v.is_a?(Array) || v.downcase == v ? "#{k}: #{":" unless v == true || v == false || v.is_a?(Array) }#{v}" : "#{k}: \"#{v}\"" }.join(', ')}" if assoc.reject{|k,v| k == "hidden" || k == "polymorphic" || k == "class_names"}.count > 0
127
+ end
128
+ model['has_many'].select{|k,v| v['polymorphic']}.each do |k,assoc|
129
+ file += "\n has_many :#{k.pluralize.underscore}"
130
+ file += ", #{(assoc.reject{|k,v| k == "hidden" || k == "polymorphic" || k == "class_names"}.map{|k,v| v == true || v == false || v.is_a?(Array) || v.downcase == v ? "#{k}: #{":" unless v == true || v == false || v.is_a?(Array) }#{v}" : "#{k}: \"#{v}\"" }+["as: :#{assoc['foreign_key'].gsub(/_id$/,"")}"]).join(', ')}" if assoc.reject{|k,v| k == "hidden" || k == "polymorphic" || k == "class_names"}.count > 0
131
+ end
132
+
133
+ file += "\n" # if model['belongs_to'].count + model['attributes'].count > 0
134
+ #file += "\n attr_accessible :created_at, :updated_at"
135
+ file += "\n attr_accessible #{model['belongs_to'].map{ |k,v| ":#{k}_id" }.join(', ')}" if model['belongs_to'].count > 0
136
+ file += "\n attr_accessible #{model['belongs_to'].reject{ |k,v| !v['polymorphic'] }.map{ |k,v| ":#{k}_type" }.join(', ')}" if model['belongs_to'].reject{ |k,v| !v['polymorphic'] }.count > 0
137
+ file += "\n attr_accessible #{model['attributes'].map{ |k,v| ":#{k}" }.join(', ')}" if model['attributes'].count > 0
138
+
139
+ file += "\n" if model['has_many'].reject{|k,v| v['dependent'] != 'destroy' }.count + model['has_one'].reject{|k,v| v['dependent'] != 'destroy' }.count > 0
140
+ file += "\n accepts_nested_attributes_for #{model['has_one'].reject{|k,v| v['dependent'] != 'destroy' }.map{ |k,v| ":#{k}" }.join(', ')}" if model['has_one'].reject{|k,v| v['dependent'] != 'destroy' }.count > 0
141
+ file += "\n accepts_nested_attributes_for #{model['has_many'].reject{|k,v| v['dependent'] != 'destroy' }.map{ |k,v| ":#{k}" }.join(', ')}" if model['has_many'].reject{|k,v| v['dependent'] != 'destroy' }.count > 0
142
+
143
+ file += "\n" if model['code']['before'].length > 0
144
+ model['code']['before'].each do |code|
145
+ file += "\n#{indent(code, 2)}"
146
+ end
147
+
148
+ model['methods'].each do |k,assoc|
149
+ if assoc['getter']
150
+ file += "\n"
151
+ assoc.each do |k,v|
152
+ file += "\n # #{k}: #{v}" if k != 'getter' && k != 'setter' && k != 'takes'
153
+ end
154
+ file += "\n def #{k}"
155
+ file += "\n #{assoc['getter']}"
156
+ file += "\n end"
157
+ end
158
+ if assoc['setter']
159
+ file += "\n"
160
+ assoc.each do |k,v|
161
+ file += "\n # #{k}: #{v}" if k != 'getter' && k != 'setter' && k != 'returns'
162
+ end
163
+ file += "\n def #{k}=(value)"
164
+ file += "\n #{assoc['setter']}"
165
+ file += "\n end"
166
+ end
167
+ end
168
+
169
+ file += "\n" if model['code']['after'].length > 0
170
+ model['code']['after'].each do |code|
171
+ file += "\n#{indent(code, 2)}"
172
+ end
173
+
174
+ file += "\nend\n"
175
+
176
+ puts("app/models/#{name.underscore}.rb") unless @quiet
177
+ File.open("#{Rails.root}/app/models/#{name.underscore}.rb", "w") do |f|
178
+ f.write(file)
179
+ end
180
+
181
+ next if options.include?("--no-scaffold")
182
+
183
+ puts("app/controllers/#{name.underscore.pluralize}_controller.rb") unless @quiet
184
+
185
+
186
+ file = File.read("#{Rails.root}/app/controllers/#{name.underscore.pluralize}_controller.rb")
187
+ file.sub!(" # GET /#{name.underscore.pluralize}
188
+ # GET /#{name.underscore.pluralize}.json
189
+ def index
190
+ @#{name.underscore.pluralize} = #{name}.all
191
+ end", "#{model['code']['controller'].map{|line| " #{line}
192
+ "}.join}#{"
193
+ " if model['code']['controller'].length > 0} # GET /#{name.underscore.pluralize}
194
+ # GET /#{name.underscore.pluralize}.json
195
+ def index
196
+ @#{name.underscore.pluralize} = #{name}.all#{ model['belongs_to'].map do |attr_name, assoc| "
197
+ if params[:#{attr_name.underscore}_id]
198
+ @#{name.underscore.pluralize} = @#{name.underscore.pluralize}.where(#{attr_name.underscore}_id: params[:#{attr_name.underscore}_id])
199
+ end
200
+ " end.join }#{ "
201
+ " if true || model['belongs_to'].count > 0 } end")
202
+
203
+
204
+ file.sub!(" # GET /#{name.underscore}/1
205
+ # GET /#{name.underscore}/1.json
206
+ def show
207
+ end"," # GET /#{name.underscore}/1
208
+ # GET /#{name.underscore}/1.json
209
+ def show
210
+ @#{name.underscore} = #{name}.new#{ model['belongs_to'].map do |attr_name, assoc| "
211
+ if params[:#{attr_name.underscore}_id]
212
+ @#{name.underscore}.#{attr_name.underscore}_id = params[:#{attr_name.underscore}_id]
213
+ end
214
+ " end.join } end")
215
+
216
+
217
+ file.sub!(" # GET /#{name.underscore.pluralize}/new
218
+ def new
219
+ @#{name.underscore} = #{name}.new
220
+ end"," # GET /#{name.underscore.pluralize}/new
221
+ def new
222
+ @#{name.underscore} = #{name}.new#{ model['belongs_to'].map do |attr_name, assoc| "
223
+ if params[:#{attr_name.underscore}_id]
224
+ @#{name.underscore}.#{attr_name.underscore}_id = params[:#{attr_name.underscore}_id]
225
+ end
226
+ " end.join } end")
227
+
228
+
229
+
230
+ File.open("#{Rails.root}/app/controllers/#{name.underscore.pluralize}_controller.rb", "w") do |f|
231
+ f.write(file)
232
+ end
233
+
234
+ puts("app/views/#{name.underscore.pluralize}/index.html.erb") unless @quiet
235
+ File.open("#{Rails.root}/app/views/#{name.underscore.pluralize}/index.html.erb", "w") do |f|
236
+
237
+ names = model['attributes'].reject{ |k,v| v['hidden'] }.merge(model['methods'].reject{ |k,v| v['hidden'] }.merge(model['belongs_to'].reject{ |k,v| v['hidden'] })).reject{ | k, v| !v['primary_ref'] }
238
+ names = model['attributes'].reject{ |k,v| v['hidden'] }.merge(model['belongs_to'].reject{ |k,v| v['hidden'] }) if names.count == 0
239
+
240
+ f.write "<h1>Listing #{name.titleize.pluralize}</h1>
241
+
242
+ <table>
243
+ <thead>
244
+ <tr>
245
+ #{ names.map do |k, v|
246
+ " <th>#{k.titlecase}</th>
247
+ "
248
+ end.join } <th></th>
249
+ <th></th>
250
+ <th></th>
251
+ </tr>
252
+ </thead>
253
+
254
+ <tbody>
255
+ <% @#{name.underscore.pluralize}.each do |#{name.underscore}| %>
256
+ <tr>
257
+ #{ names.map do |k, v|
258
+ " <th><%= #{name.underscore}.#{k.underscore} %></th>
259
+ "
260
+ end.join } <td><%= link_to 'Show', #{name.underscore} %></td>
261
+ <td><%= link_to 'Edit', edit_#{name.underscore}_path(#{name.underscore}) %></td>
262
+ <td><%= link_to 'Destroy', #{name.underscore}, method: :delete, data: { confirm: 'Are you sure?' } %></td>
263
+ </tr>
264
+ <% end %>
265
+ </tbody>
266
+ </table>
267
+
268
+ <br>
269
+
270
+ <%= link_to 'New #{name.titleize}', url_for([:new, #{(model['belongs_to'].map{ |k,v| "@" + k.underscore } + [":#{name.underscore}"] ).join(', ')}]) %>
271
+ "
272
+ end
273
+
274
+
275
+
276
+
277
+
278
+
279
+ puts("app/views/#{name.underscore.pluralize}/show.html.erb") unless @quiet
280
+ File.open("#{Rails.root}/app/views/#{name.underscore.pluralize}/show.html.erb", "w") do |f|
281
+
282
+ names = model['attributes'].reject{ |k,v| v['hidden'] }.merge(model['methods'].reject{ |k,v| v['hidden'] }.reject{ |k,v| v['returns'].to_s.downcase != v['returns'].to_s }).reject{ |name, v| name == "to_s"}
283
+
284
+ f.write "<p id=\"notice\"><%= notice %></p>
285
+
286
+ <h1>#{name.titleize}#{ " <%= @"+name.underscore + " %>" if model['methods'].reject{ |k,v| k != "to_s" }.count > 0 }</h1>
287
+
288
+ #{names.map do |attr_name, v|
289
+ "<p>
290
+ <strong>#{attr_name.titleize}:</strong>
291
+ <%= @#{name.underscore}.#{attr_name.underscore} %>
292
+ </p>
293
+
294
+ " end.join }#{
295
+ model['belongs_to'].merge(model['methods'].reject{ |k,v| v['returns'].to_s.downcase == v['returns'].to_s }).map do |attr_name,v|
296
+ "<p>
297
+ <strong>#{attr_name.titleize}:</strong>
298
+ <%= link_to @#{name.underscore}.#{attr_name.underscore}, @#{name.underscore}.#{attr_name.underscore} %>
299
+ </p>
300
+
301
+ " end.join }<%= link_to 'Edit', edit_#{name.underscore}_path(@#{name.underscore}) %> |
302
+ <%= link_to 'Back', url_for([#{(model['belongs_to'].map{ |k,v| "@" + k.underscore } + [":#{name.underscore.pluralize}"]).join(', ')}]) rescue #{name.underscore.pluralize}_path %>
303
+ #{ model['has_many'].map do |model,v|
304
+ "
305
+ <div class=\"has-many-index\">
306
+ <% notice = nil %>
307
+ <% @#{(v['class_name'] || model).underscore.downcase.pluralize} = @#{name.underscore}.#{model.underscore.pluralize} %>
308
+ <%= render template: \"#{(v['class_name'] || model).underscore.downcase.pluralize}/index\" %>
309
+ </div>" end.join }
310
+ "
311
+ end
312
+
313
+
314
+ puts("app/views/#{name.underscore.pluralize}/_form.html.erb") unless @quiet
315
+ f_content = File.read("#{Rails.root}/app/views/#{name.underscore.pluralize}/_form.html.erb")
316
+ File.open("#{Rails.root}/app/views/#{name.underscore.pluralize}/_form.html.erb", "w") do |f|
317
+
318
+ model['belongs_to'].each do |k,assoc|
319
+ if assoc['polymorphic']
320
+ f_content.gsub!(" <%= f.association :#{k} %>", " <%= f.input :#{k}_type, collection: #{assoc['class_names']} %><%= f.association :#{k}, collection: #{assoc['class_names'].map{ |word| "#{word}.all" }.join(" + ")} %>")
321
+ end
322
+ #file += ", #{assoc.reject{|k,v| k == "hidden" || k == "class_names" || (k == "class_name" && assoc['polymorphic'])}.map{|k,v| v == true || v == false || v.is_a?(Array) || v.downcase == v ? "#{k}: #{":" unless v == true || v == false || v.is_a?(Array) }#{v}" : "#{k}: \"#{v}\"" }.join(', ')}" if assoc.reject{|k,v| k == "hidden" || k == "class_names" || (k == "class_name" && assoc['polymorphic'])}.count > 0
323
+ end
324
+
325
+ f.write f_content
326
+ end
327
+
328
+
329
+ #puts "rails g scaffold #{name} " + model['attributes'].map{ |k,v| k + ':' + v['type'].sub(/yaml|hash|object|cache/i,"text") }.join(' ') + " " + model['has_one'].map{ |k,v| k + ':references' + (v['polymorphic'] ? "{polymorphic}" : "") }.join(' ') + " #{options}"
330
+ end
331
+
332
+ cleanup(ddl: true, mixins: false)
333
+
334
+ File.open("#{Rails.root}/db/schema.yaml", "w") do |f|
335
+ f.write models.to_yaml
336
+ end
337
+
338
+ puts("","--- DATA #{models.to_yaml}","") unless @quiet
339
+ end
340
+
341
+ def parse
342
+ #puts ""
343
+ #puts "--- PARSE ---"
344
+ self.models = {}
345
+ stack = [nil]
346
+ depth = 0
347
+ self.markup.gsub(/.---..*/m,"").lines.each do |line|
348
+ next if (line.strip == "" || line.strip.start_with?("#")) && !@multiline
349
+ parse_line(line, self.models, stack, depth)
350
+ end
351
+
352
+
353
+ lines = []
354
+ models.select{ |k,v| k[0] != "@"}.each do |model,v0|
355
+
356
+ if models["@Default"] != nil && (!models['has_many'] || models['has_many']['@defaults'] != nil)
357
+ name = "@default"
358
+ type = "@Default"
359
+ if name[0] == "@"
360
+ puts("#{model} > #{type}") unless @quiet
361
+ v0['includes'] ||= []
362
+ v0['includes'] << type
363
+ v0['includes'].uniq!
364
+ models[type]['ddl'].select{|v| v.start_with?(type + " > ")}.each do |ddl|
365
+ line = ddl.sub("#{type} > ","")
366
+ line.gsub!("___","")
367
+ puts(" #{line}") unless @quiet
368
+ lines << {'model' => model, 'content' => line}
369
+ end
370
+ end
371
+ end
372
+
373
+ v0['belongs_to'].each do |k,v1|
374
+ v1['class_names'].each do |v|
375
+ if v[0] == "@"
376
+ name = k
377
+ name += "_" unless name.end_with?("_")
378
+ type = v
379
+ if name[0] != "@"
380
+ next if name.include? "___"
381
+ puts("#{model} > #{name}:#{type}") unless @quiet
382
+ v0['includes'] ||= []
383
+ v0['includes'] << "#{name}:#{type}"
384
+ v0['includes'].uniq!
385
+ models[type]['ddl'].select{|v| v.start_with?(type + " > ")}.each do |ddl|
386
+ line = ddl.sub("#{type} > ","")
387
+ #name2 = line.sub(/ *[\+\-\*\=\&]*([@A-Za-z0-9_]+).*/,'\1')
388
+ #data_type = nil
389
+ #data_type = line.sub(/^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:?([@A-Za-z0-9_]+).*/,'\1') if line =~ /^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:[@A-Za-z0-9_]+.*/
390
+ #flags = line.sub(/\s*([\-\+\*\&\=]+).*/,'\1') if line =~ /\s*([\-\+\*\&\=]+).*/
391
+ line.gsub!("___",name)
392
+ puts(" #{line}") unless @quiet
393
+ lines << {'model' => model, 'content' => line}
394
+ end
395
+ end
396
+ end
397
+ end
398
+ end if v0['belongs_to']
399
+
400
+ v0['has_one'].each do |k,v1|
401
+ v1['class_names'].each do |v|
402
+ if v[0] == "@"
403
+ name = k
404
+ type = v
405
+ if name[0] == "@"
406
+ puts("#{model} > #{type}") unless @quiet
407
+ v0['includes'] ||= []
408
+ v0['includes'] << type
409
+ v0['includes'].uniq!
410
+ models[type]['ddl'].select{|v| v.start_with?(type + " > ")}.each do |ddl|
411
+ line = ddl.sub("#{type} > ","")
412
+ line.gsub!("___","")
413
+ puts(" #{line}") unless @quiet
414
+ lines << {'model' => model, 'content' => line}
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end if v0['has_one']
420
+ end
421
+
422
+ cleanup(ddl: true, mixins: false)
423
+
424
+ lines.each do |line|
425
+ stack = [line['model'],nil,nil,nil,nil]
426
+ depth = 1
427
+ parse_line(line['content'], models, stack, depth)
428
+ end
429
+
430
+ #throw "DDL"
431
+ puts("","--- DDL ---","") unless @quiet
432
+
433
+ models
434
+ end
435
+
436
+ def cleanup(options = {})
437
+
438
+ options[:ddl] ||= false
439
+ options[:mixins] ||= false
440
+
441
+ #models.reject!{ |k,v| k[0] == "@"}
442
+ models.each do |model,v0|
443
+
444
+ v0.delete('ddl') if options[:ddl] == true
445
+
446
+ v0['has_one'].select{true}.each do |k,v1|
447
+ v0['has_one'].delete(k) if k[0] == "@"
448
+ v1['class_names'].select{true}.each do |v|
449
+ if v[0] == "@"
450
+ v0['has_one'].delete(k)
451
+ break
452
+ end
453
+ end
454
+ end if v0['has_one']
455
+ v0.delete('has_one') if v0['has_one'] == {}
456
+
457
+ v0['has_many'].select{true}.each do |k,v1|
458
+ v0['has_many'].delete(k) if k[0] == "@"
459
+ v1['class_names'].select{true}.each do |v|
460
+ if v[0] == "@"
461
+ v0['has_many'].delete(k)
462
+ break
463
+ end
464
+ end
465
+ end if v0['has_many']
466
+ v0.delete('has_many') if v0['has_many'] == {}
467
+
468
+ v0['belongs_to'].select{true}.each do |k,v1|
469
+ v0['belongs_to'].delete(k) if k[0] == "@"
470
+ v1['class_names'].select{true}.each do |v|
471
+ if v[0] == "@"
472
+ v0['belongs_to'].delete(k)
473
+ break
474
+ end
475
+ end
476
+ end if v0['belongs_to']
477
+ v0.delete('belongs_to') if v0['belongs_to'] == {}
478
+
479
+ v0.delete('attributes') if v0['attributes'] == {}
480
+ v0.delete('methods') if v0['methods'] == {}
481
+
482
+ if v0['code']
483
+ v0['code'].select{true}.each do |k,v|
484
+ v0['code'].delete(k) if v0['code'][k] == []
485
+ end
486
+ v0.delete('code') if v0['code'] == {}
487
+ end
488
+
489
+ end
490
+
491
+ end
492
+
493
+
494
+ def parse_line(line, models, stack, depth)
495
+ spaces = line[/\A */].size
496
+
497
+ @multiline ||= false
498
+ unless @multiline
499
+ if line.strip =~ /^(def|module|class|if|unless) / || line.strip =~ / do \|.+\|$/ || line.strip =~ / do$/
500
+ @multiline = true
501
+ @multiline_spaces = spaces
502
+ @multiline_end = /^#{" " * @multiline_spaces}end$/
503
+ @multiline_buffer = line[@multiline_spaces..line.length]
504
+ return
505
+ elsif line.strip =~ /^[a-z_]+ |^acts_as_.*/
506
+ if line.strip =~ /"^include |^require |^extend |^acts_as_[a-z_]+/
507
+ line = "!top{#{line.strip}}"
508
+ else
509
+ line = "!!{#{line.strip}}"
510
+ end
511
+ end
512
+ else
513
+ if line.strip == ""
514
+ @multiline_buffer += "\n"
515
+ else
516
+ @multiline_buffer += line[@multiline_spaces..line.length]
517
+ end
518
+ if line =~ @multiline_end
519
+ line = "!after{" + @multiline_buffer.strip + "}"
520
+ spaces = @multiline_spaces
521
+ @multiline = false
522
+ else
523
+ return
524
+ end
525
+ end
526
+
527
+ line.strip!
528
+
529
+ return if line == ""
530
+
531
+ depth = spaces / 2
532
+ parent = nil
533
+ parent = stack[depth]
534
+ if parent && parent[0] != "@"
535
+ line.gsub!("MMMs",parent.pluralize)
536
+ line.gsub!("MMMS",parent.pluralize)
537
+ line.gsub!("MMM",parent)
538
+ line.gsub!("mmms",parent.underscore.downcase.pluralize)
539
+ line.gsub!("mmm",parent.underscore.downcase)
540
+ end
541
+
542
+ if line[0] == "{"
543
+
544
+ depth = spaces / 2
545
+
546
+ parent = nil
547
+ parent = stack[depth]
548
+
549
+ models[parent] ||= {} if parent
550
+
551
+ models[parent]['ddl'] ||= [] if parent
552
+ models[parent]['ddl'] << (parent ? "#{parent} > " : '') + line if parent
553
+
554
+ models[parent]['code'] ||= {} if parent
555
+ models[parent]['code']['after'] ||= [] if parent
556
+ models[parent]['code']['after'] << line[1..-2] if parent
557
+
558
+ return
559
+ elsif line[0] == "!"
560
+
561
+ depth = spaces / 2
562
+
563
+ parent = nil
564
+ parent = stack[depth]
565
+
566
+ models[parent] ||= {} if parent
567
+
568
+ models[parent]['ddl'] ||= [] if parent
569
+ models[parent]['ddl'] << (parent ? "#{parent} > " : '') + line if parent
570
+
571
+ place = line.sub(/^\!\!?(.*\s*)\{.*/m,'\1').strip
572
+ place = 'before' if place == ""
573
+ _unique = line[1] == "!"
574
+ line.sub!(line.sub(/^(\!\!?.*\s*)\{.*/m,'\1'),'')
575
+ models[parent]['code'] ||= {} if parent
576
+ models[parent]['code'][place] ||= [] if parent
577
+ if line[0] == "{"
578
+ if !_unique || !models[parent]['code'][place].include?(line[1..-2])
579
+ models[parent]['code'][place] << line[1..-2]
580
+ end
581
+ else
582
+ if !_unique || !models[parent]['code'][place].include?(line)
583
+ models[parent]['code'][place] << line
584
+ end
585
+ end
586
+
587
+ return
588
+ end
589
+
590
+ name = line.sub(/ *[\+\-\*\=\&]*([@A-Za-z0-9_]+).*/,'\1')
591
+ data_type = nil
592
+ polymorphic = false
593
+
594
+ data_type = line.sub(/^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:?([@A-Za-z0-9_]+).*/,'\1') if line =~ /^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:[@A-Za-z0-9_]+.*/
595
+ if line =~ /^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:\*[@A-Za-z0-9_]+.*/
596
+ data_type = line.sub(/^ *[\+\-\*\=\&]*[@A-Za-z0-9_]+\:\*?([@A-Za-z0-9_]+).*/,'\1')
597
+ polymorphic = true
598
+ end
599
+
600
+ type = "attribute"
601
+ type = "model" if name[0] =~ /[@A-Z]/
602
+ type = "model" if data_type && data_type[0] =~ /[@A-Z]/
603
+
604
+ if data_type && type == "model"
605
+ model = data_type
606
+ elsif type == "model"
607
+ model = name
608
+ end
609
+ model = name if type == "model" && model == nil
610
+
611
+ attributes = []
612
+ attributes = line.sub(/.*\((.*)\).*/,'\1').split(/\s/) if line =~ /.*\((.*)\).*/
613
+
614
+ data = nil
615
+ data = Regexp.last_match[0][1..-2] if line =~ /[\{].*[\}]/xms
616
+
617
+ flags = nil
618
+ flags = line.sub(/\s*([\-\+\*\&\=]+).*/,'\1') if line =~ /\s*([\-\+\*\&\=]+).*/
619
+
620
+ multiplicity = "has_one"
621
+ multiplicity = "has_many" if flags == "*" || flags == ".*" || flags == "*." || flags == "-*" || flags == "*-"
622
+ multiplicity = "has_and_belongs_to_many" if flags == "**" || flags == "+**" || flags == "**+"
623
+ multiplicity = "belongs_to" if flags == "+" || flags == "-+" || flags == "+-" || flags == ".+" || flags == "+."
624
+ multiplicity = "has_many" if flags == "*+" || flags == "+*" || flags == "++"
625
+
626
+ dependent = "destroy"
627
+ dependent = "nullify" if flags == "*+" || flags == "+*" || flags == "++" || flags == "+"
628
+ dependent = nil if flags == "**+" || flags == "+**"
629
+
630
+ hidden = false
631
+ hidden = true if flags == "-" || flags == "-+" || flags == "+-"
632
+ #read_only = false
633
+ #read_only = true if flags == "." || flags == ".+"
634
+ type = "method" if flags == "="
635
+ type = "property" if flags == "=="
636
+ type = "cached" if flags == "&" && type != "model"
637
+
638
+ depth = spaces / 2
639
+ stack[depth+1] = model if type == "model"
640
+
641
+ parent = nil
642
+ parent = stack[depth]
643
+
644
+ #puts "#{parent}#{flags}#{"." if !flags && parent}#{name}:#{model}#{"(" + attributes.join(' ') + ")" if attributes.count > 0}" if parent || attributes.count > 0
645
+
646
+ models[parent] ||= {} if parent
647
+ models[model] ||= {} if type == "model"
648
+
649
+ models[parent]['ddl'] ||= [] if parent
650
+ models[parent]['ddl'] << (parent ? "#{parent} > " : '') + line if parent
651
+ models[model]['ddl'] ||= [] if model
652
+ models[model]['ddl'] << "~" + (parent ? "#{parent} > " : '') + line if model
653
+
654
+ unless (type == "method" || type == "property" || type == "attribute")
655
+ attributes.each do |attribute|
656
+
657
+ _flags = attribute.sub(/([\-\+\*\&\=]+).*/,'\1') if attribute =~ /([\-\+\*\&\=]+).*/
658
+ _mode = 'attributes'
659
+ _mode = 'methods' if _flags == "="
660
+ _hidden = false
661
+ _hidden = true if _flags == "-"
662
+
663
+ at = parse_attribute(attribute.sub(/^[\-\=]+/,""))
664
+ models[model][_mode] ||= {}
665
+ models[model][_mode][at['name']] ||= {}
666
+ if !models[model][_mode][at['name']]['type'] || !at['default_type']
667
+ models[model][_mode][at['name']]['type'] = at['type']
668
+ end
669
+ models[model][_mode][at['name']]['hidden'] = true if _hidden
670
+ models[model][_mode][at['name']]['primary_ref'] ||= []
671
+ models[model][_mode][at['name']]['primary_ref'] << parent
672
+ end
673
+ end
674
+
675
+ if parent && (type == "attribute" || type == "cached")
676
+ at = parse_attribute("#{name}#{":" + data_type if data_type}")
677
+ models[parent]['attributes'] ||= {}
678
+ models[parent]['attributes'][at['name']] = {}
679
+ models[parent]['attributes'][at['name']]['hidden'] = true if hidden
680
+ if !models[parent]['attributes'][at['name']]['type'] || !at['default_type']
681
+ models[parent]['attributes'][at['name']]['type'] = at['type']
682
+ end
683
+ end
684
+
685
+ if parent && (type == "method" || type == "property")
686
+ at = parse_attribute("#{name}#{":" + data_type if data_type}")
687
+ models[parent]['methods'] ||= {}
688
+ models[parent]['methods'][at['name']] ||= {}
689
+ if type == "method"
690
+ models[parent]['methods'][at['name']]['returns'] = at['type']
691
+ unless models[parent]['methods'][at['name']]['setter']
692
+ models[parent]['methods'][at['name']]['getter'] = data.to_s
693
+ else
694
+ models[parent]['methods'][at['name']]['getter'] += "\n " + data if data
695
+ end
696
+ else
697
+ models[parent]['methods'][at['name']]['takes'] = at['type']
698
+ unless models[parent]['methods'][at['name']]['getter']
699
+ models[parent]['methods'][at['name']]['setter'] = data.to_s
700
+ else
701
+ models[parent]['methods'][at['name']]['setter'] += "\n " + data if data
702
+ end
703
+ end
704
+ end
705
+
706
+ if type == "model" && parent
707
+ if multiplicity == "has_many"
708
+ models[parent]['has_many'] ||= {}
709
+ models[parent]['has_many'][name.pluralize.underscore] ||= (model && name != model) ? { 'class_name' => model } : {}
710
+ models[parent]['has_many'][name.pluralize.underscore]['dependent'] = dependent if dependent
711
+ models[parent]['has_many'][name.pluralize.underscore]['foreign_key'] = name.underscore + "_id" if model && name != model
712
+ models[parent]['has_many'][name.pluralize.underscore]['polymorphic'] = true if polymorphic
713
+
714
+ models[parent]['has_many'][name.pluralize.underscore]['class_names'] ||= []
715
+ models[parent]['has_many'][name.pluralize.underscore]['class_names'] << model
716
+ models[parent]['has_many'][name.pluralize.underscore]['class_names'].uniq!
717
+
718
+ models[model]['belongs_to'] ||= {}
719
+ if model && name != model
720
+ models[model]['belongs_to'][name.pluralize.underscore] ||= { 'class_name' => parent }
721
+ models[model]['belongs_to'][name.pluralize.underscore]['hidden'] = true if hidden
722
+ models[model]['belongs_to'][name.pluralize.underscore]['polymorphic'] = true if polymorphic
723
+
724
+ models[model]['belongs_to'][name.pluralize.underscore]['class_names'] ||= []
725
+ models[model]['belongs_to'][name.pluralize.underscore]['class_names'] << parent
726
+ models[model]['belongs_to'][name.pluralize.underscore]['class_names'].uniq!
727
+ else
728
+ models[model]['belongs_to'][parent.underscore] ||= {}
729
+ models[model]['belongs_to'][parent.underscore]['hidden'] = true if hidden
730
+ models[model]['belongs_to'][parent.underscore]['polymorphic'] = true if polymorphic
731
+
732
+ models[model]['belongs_to'][parent.underscore]['class_names'] ||= []
733
+ models[model]['belongs_to'][parent.underscore]['class_names'] << parent
734
+ models[model]['belongs_to'][parent.underscore]['class_names'].uniq!
735
+ end
736
+ end
737
+ if multiplicity == "belongs_to"
738
+ models[parent]['belongs_to'] ||= {}
739
+ models[parent]['belongs_to'][name.underscore] ||= model && name != model ? { 'class_name' => model }: {}
740
+ models[parent]['belongs_to'][name.underscore]['hidden'] = true if hidden
741
+ models[parent]['belongs_to'][name.underscore]['polymorphic'] = true if polymorphic
742
+
743
+ models[parent]['belongs_to'][name.underscore]['class_names'] ||= []
744
+ models[parent]['belongs_to'][name.underscore]['class_names'] << model
745
+ models[parent]['belongs_to'][name.underscore]['class_names'].uniq!
746
+
747
+ models[model]['has_many'] ||= {}
748
+ models[model]['has_many'][parent.pluralize.underscore] ||= (model && name != model) ? { 'class_name' => parent } : {}
749
+ models[model]['has_many'][parent.pluralize.underscore]['dependent'] = dependent if dependent
750
+ models[model]['has_many'][parent.pluralize.underscore]['polymorphic'] = true if polymorphic
751
+
752
+ models[model]['has_many'][parent.pluralize.underscore]['class_names'] ||= []
753
+ models[model]['has_many'][parent.pluralize.underscore]['class_names'] << model
754
+ models[model]['has_many'][parent.pluralize.underscore]['class_names'].uniq!
755
+ end
756
+ if multiplicity == "has_one"
757
+ if model && name != model
758
+ models[parent]['belongs_to'] ||= {}
759
+ models[parent]['belongs_to'][name.underscore] ||= model && name != model ? { 'class_name' => model }: {}
760
+ models[parent]['belongs_to'][name.underscore]['dependent'] = dependent if dependent
761
+ models[parent]['belongs_to'][name.underscore]['hidden'] = true if hidden
762
+ models[parent]['belongs_to'][name.underscore]['polymorphic'] = true if polymorphic
763
+
764
+ models[parent]['belongs_to'][name.underscore]['class_names'] ||= []
765
+ models[parent]['belongs_to'][name.underscore]['class_names'] << model
766
+ models[parent]['belongs_to'][name.underscore]['class_names'].uniq!
767
+
768
+ models[model]['has_many'] ||= {}
769
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize] ||= model && name != model ? { 'class_name' => parent, 'foreign_key' => name + "_id" }: {}
770
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize]['dependent'] = dependent if dependent
771
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize]['polymorphic'] = true if polymorphic
772
+
773
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize]['class_names'] ||= []
774
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize]['class_names'] << parent
775
+ models[model]['has_many'][name.underscore + "_" + parent.underscore.pluralize]['class_names'].uniq!
776
+ else
777
+ models[parent]['has_one'] ||= {}
778
+ models[parent]['has_one'][name.underscore] = model && name != model ? { 'class_name' => model }: {}
779
+ models[parent]['has_one'][name.underscore]['dependent'] = dependent if dependent
780
+ models[parent]['has_one'][name.underscore]['hidden'] = true if hidden
781
+ models[parent]['has_one'][name.underscore]['polymorphic'] = true if polymorphic
782
+
783
+ models[parent]['has_one'][name.underscore]['class_names'] ||= []
784
+ models[parent]['has_one'][name.underscore]['class_names'] << model
785
+ models[parent]['has_one'][name.underscore]['class_names'].uniq!
786
+
787
+ models[model]['belongs_to'] ||= {}
788
+ models[model]['belongs_to'][parent.underscore] ||= model && name != model ? { 'class_name' => parent, 'foreign_key' => name + "_id" }: {}
789
+ models[model]['belongs_to'][parent.underscore]['hidden'] = true if hidden
790
+ models[model]['belongs_to'][parent.underscore]['polymorphic'] = true if polymorphic
791
+
792
+ models[model]['belongs_to'][parent.underscore]['class_names'] ||= []
793
+ models[model]['belongs_to'][parent.underscore]['class_names'] << parent
794
+ models[model]['belongs_to'][parent.underscore]['class_names'].uniq!
795
+ end
796
+
797
+ #models[model]['belongs_to'] ||= {}
798
+ #models[model]['belongs_to'][parent.underscore] ||= {}
799
+ #models[model]['has_many'] ||= {}
800
+ #models[model]['has_many'][parent.pluralize.underscore] ||= model && name != model ? { 'foreign_key' => name }: {}
801
+ end
802
+ end
803
+
804
+ end
805
+
806
+
807
+
808
+
809
+
810
+ end