schofield 0.2.2 → 0.3.0

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.
@@ -1,27 +1,27 @@
1
1
  module Schofield
2
-
2
+
3
3
  module Generators
4
4
 
5
5
  class Level
6
-
6
+
7
7
  COLUMNS_TO_IGNORE = %w( ^created_at$ ^updated_at$ _content_type$ _file_size$ _updated_at$ _type$ ^type$ ^crypted_password$ ^password_salt$ ^persistence_token$ ^perishable_token$ ^login_count$ ^failed_login_count$ ^last_request_at$ ^current_login_at$ ^last_login_at$ ^current_login_ip$ ^last_login_ip$ )
8
-
8
+
9
9
  class << self; attr_reader :columns_to_ignore end
10
-
10
+
11
11
  def self.tables_to_ignore= tables
12
12
  @columns_to_ignore = ( COLUMNS_TO_IGNORE + tables.map{ |m| "^#{m.singularize}_id$" } )
13
13
  end
14
-
15
-
16
- attr_reader :name, :parent_associations, :attributes, :superclass
14
+
15
+
16
+ attr_reader :model, :name, :parent_associations, :attributes, :superclass
17
17
  attr_accessor :child_associations, :subclasses
18
18
 
19
19
  delegate :validations,
20
20
  :validations?,
21
- :attr_protecteds,
22
- :attr_protecteds?,
23
- :acts_as_markdowns,
24
- :acts_as_markdowns?,
21
+ :attr_accessors,
22
+ :attr_accessors?,
23
+ :attr_accessibles,
24
+ :attr_accessibles?,
25
25
  :attachments?,
26
26
  :to_s_string,
27
27
  :attached_files,
@@ -41,135 +41,135 @@ module Schofield
41
41
  add_parent_associations
42
42
  add_attributes
43
43
  end
44
-
45
-
44
+
45
+
46
46
  # Inheritence
47
-
47
+
48
48
  def subclass?
49
49
  @superclass.present?
50
50
  end
51
-
51
+
52
52
  def superclass?
53
53
  @subclasses.any?
54
54
  end
55
-
56
-
55
+
56
+
57
57
  # Join table
58
-
58
+
59
59
  def join?
60
60
  @join ||= @model.columns.select { |c| c.name.match(/_id$/) }.length == 2 && @model.columns.select { |c| !%w( id created_at updated_at position ).include?(c.name) }.length == 2
61
61
  end
62
-
62
+
63
63
  def other_parent_name parent_name
64
64
  @parent_associations.find{ |a| a.parent_name != parent_name }.parent_name
65
65
  end
66
-
67
-
66
+
67
+
68
68
  # Polymorphism
69
-
69
+
70
70
  def polymorphic?
71
71
  @polymorphic ||= polymorphic_name.present?
72
72
  end
73
-
73
+
74
74
  def polymorphic_name
75
75
  match_data = nil
76
76
  @polymorphic_name ||= @model.columns.find { |c| match_data = c.name.match(/^(.+)_type$/) } ? match_data[1] : nil
77
77
  end
78
-
79
-
78
+
79
+
80
80
  # Nesting
81
-
81
+
82
82
  def nested?
83
- @parent_associations.find(&:nest?).present?
83
+ @parent_associations.any?(&:nest?)
84
84
  end
85
-
85
+
86
86
  def nests?
87
- @child_associations.find(&:nest?).present?
87
+ @child_associations.any?(&:nest?)
88
88
  end
89
-
89
+
90
90
  def nested_associations
91
91
  if superclass? then []
92
92
  else
93
93
  associations = @child_associations + ( subclass? ? @superclass.child_associations : [] )
94
94
  end
95
95
  end
96
-
96
+
97
97
  def nested_levels
98
98
  nested_associations.select(&:nest?).map(&:child)
99
99
  end
100
-
101
-
100
+
101
+
102
102
  # Associations and cardinality
103
-
103
+
104
104
  def belongs_to?
105
105
  @parent_associations.any?
106
106
  end
107
-
107
+
108
108
  def belongs_to_one_names
109
109
  @parent_associations.select(&:one_to_one?).map(&:parent_name)
110
110
  end
111
-
111
+
112
112
  def belongs_to_one?
113
- @parent_associations.find(&:one_to_one?).present?
113
+ @parent_associations.any?(&:one_to_one?)
114
114
  end
115
-
115
+
116
116
  def has_ones?
117
- @child_associations.find(&:one_to_one?).present?
117
+ @child_associations.any?(&:one_to_one?)
118
118
  end
119
-
119
+
120
120
  def has_manies?
121
- @child_associations.find(&:one_to_many?).present?
121
+ @child_associations.any?(&:one_to_many?)
122
122
  end
123
-
123
+
124
124
  def has_ones
125
125
  @child_associations.select(&:one_to_one?).map(&:child)
126
126
  end
127
-
127
+
128
128
  def has_manies
129
129
  @child_associations.select(&:one_to_many?).map(&:child)
130
130
  end
131
-
132
-
131
+
132
+
133
133
  # Combos
134
-
134
+
135
135
  def routes?
136
136
  !nested? && !superclass? && !belongs_to_one? && !join?
137
137
  end
138
-
138
+
139
139
  def controllers?
140
140
  !superclass? && name != 'user' && !belongs_to_one?
141
141
  end
142
-
142
+
143
143
  def views?
144
144
  controllers? && !join?
145
145
  end
146
-
146
+
147
147
  def models?
148
148
  name != 'user'
149
149
  end
150
-
150
+
151
151
  def tables?
152
152
  !belongs_to_one? && !superclass?
153
153
  end
154
-
155
-
156
-
154
+
155
+
156
+
157
157
  # Sortable
158
-
158
+
159
159
  def sortable?
160
160
  @sortable
161
161
  end
162
-
163
-
162
+
163
+
164
164
  # Form
165
-
165
+
166
166
  def multipart?
167
- attachments? || has_ones.find(&:attachments?).present?
167
+ attachments? || has_ones.any?(&:attachments?)
168
168
  end
169
-
170
-
169
+
170
+
171
171
  # Association ancestry
172
-
172
+
173
173
  def ancestry
174
174
  level = self
175
175
  nested_ins = []
@@ -189,99 +189,99 @@ module Schofield
189
189
  nested_ins.reverse
190
190
  end
191
191
 
192
-
192
+
193
193
  # Form fields
194
-
194
+
195
195
  def attribute_of_nesting_parent? attribute
196
196
  attribute.model_name && parent_associations.select(&:nest?).map(&:parent_name).include?(attribute.model_name)
197
197
  end
198
-
198
+
199
199
  def polymorphic_attribute? attribute
200
200
  polymorphic_name && attribute.model_name == polymorphic_name
201
201
  end
202
-
202
+
203
203
  def form_field? attribute
204
- attribute.name != 'position' && !polymorphic_attribute?(attribute) && !attribute_of_nesting_parent?(attribute)
204
+ !%w( position slug ).include?(attribute.name) && attribute.name !~ /_fingerprint$/ && !polymorphic_attribute?(attribute) && !attribute_of_nesting_parent?(attribute)
205
205
  end
206
-
207
-
208
-
206
+
207
+
208
+
209
209
  private
210
-
210
+
211
211
  def add_attributes
212
212
  @attributes = Attributes.new
213
213
  @model.columns.each do |column|
214
214
  add_attribute(column) unless ignore?(column) || (polymorphic? && column.name =~ /^#{polymorphic_name}_id$/)
215
215
  end
216
216
  end
217
-
217
+
218
218
  def add_attribute column
219
219
  attribute = @attributes.new_attribute(column, belongs_to_one_names.include?(column.name.gsub(/_id$/, '')))
220
220
  set_sortable if attribute.name == 'position'
221
221
  end
222
-
222
+
223
223
  def set_sortable
224
224
  unless superclass?
225
225
  @sortable = true
226
226
  Levels.sortable = @name
227
227
  end
228
228
  end
229
-
229
+
230
230
  # Nesting refers to nested routes, embedding refers to nested_attributes_for
231
231
  # Assuming that a polymorphic model has no parents other than it's polymorphically associated parents
232
232
  # If model has a one-to-one relationship with a parent, no nesting will occur as child will be embedded in parent
233
233
  # Only allowing child to be nested under one parent or if polymorphic, the polymorphically associated parents
234
234
  def add_parent_associations
235
-
235
+
236
236
  if polymorphic?
237
-
237
+
238
238
  answer = Responses.get("Which models are #{polymorphic_name}?")
239
239
  answer.split(/[^\w]+/).map(&:underscore).each do |parent_name|
240
240
  @parent_associations << Association.new(self, parent_name, one_to_one?(polymorphic_name), polymorphic_name)
241
241
  end
242
-
242
+
243
243
  else
244
-
244
+
245
245
  any_one_to_ones = false
246
246
  @model.columns.each do |column|
247
- if (match_data = column.name.match(/^(.+)_id$/)).present?
247
+ if (match_data = column.name.match(/^(.+)_id$/)).present? && Levels.exists?(match_data[1])
248
248
  parent_name = match_data[1]
249
249
  is_one_to_one = one_to_one?(parent_name)
250
250
  any_one_to_ones = true if is_one_to_one
251
251
  @parent_associations << Association.new(self, parent_name, is_one_to_one)
252
252
  end
253
253
  end
254
-
254
+
255
255
  if @parent_associations.any? && !any_one_to_ones
256
256
  @parent_associations.length == 1 ? ask_if_nested : ask_where_to_nest
257
257
  end
258
258
  end
259
259
  end
260
-
260
+
261
261
  def one_to_one? parent_name
262
262
  @one_to_one ||= %w( y yes ).include?(Responses.get("#{parent_name.gsub('_', ' ')} HAS ONE #{@human_name}? [N]").downcase)
263
263
  end
264
-
264
+
265
265
  def ask_if_nested
266
266
  question = "Do you wish to nest #{@human_name} in #{@parent_associations.first.parent_name}? [yes]"
267
267
  @parent_associations.first.nest = true unless %w( n no ).include?(Responses.get(question).downcase.strip)
268
- end
269
-
268
+ end
269
+
270
270
  def ask_where_to_nest
271
271
  question = "Where do you wish to nest #{@human_name}? nowhere(n), "
272
272
  question += @parent_associations.enum_with_index.map{ |n,i| "#{n.parent_name.gsub('_', ' ')}(#{i})" }.join(', ') + ' [n]'
273
273
  answer = Responses.get(question)
274
274
  @parent_associations[answer.to_i].nest = true unless answer == 'n' || answer.blank?
275
275
  end
276
-
276
+
277
277
  def ignore? column
278
278
  self.class.columns_to_ignore.each do |string|
279
279
  return true if column.name.match(/#{string}/)
280
280
  end
281
281
  column.primary
282
282
  end
283
-
283
+
284
284
  end
285
-
285
+
286
286
  end
287
287
  end
@@ -1,18 +1,19 @@
1
1
  module Schofield
2
-
2
+
3
3
  module Generators
4
4
 
5
5
  class Levels
6
-
6
+
7
7
  class << self; attr_reader :all, :hierarchy, :sortables end
8
-
8
+
9
9
  @all = []
10
10
  @names = {}
11
11
  @sortables = {}
12
-
13
-
14
-
12
+
13
+
14
+
15
15
  def self.models= models
16
+ @model_names = models.map{ |m| m.name.underscore }
16
17
  models.each do |model|
17
18
  if polymorphic_model?(model)
18
19
  superclass = add_level(model)
@@ -25,28 +26,32 @@ module Schofield
25
26
  end
26
27
  end
27
28
  end
28
-
29
+
29
30
  def self.sortables
30
31
  @sortables.keys
31
32
  end
32
-
33
+
33
34
  def self.sortable=sortable
34
35
  @sortables[sortable] = true
35
36
  end
36
-
37
+
37
38
  def self.find name
38
39
  @all[@names[name]]
39
40
  end
40
41
 
41
-
42
-
42
+ def self.exists? name
43
+ @model_names.include?(name.to_s)
44
+ end
45
+
46
+
47
+
43
48
  private
44
-
45
-
49
+
50
+
46
51
  def self.polymorphic_model? model
47
52
  model.columns.find { |c| c.name == 'type' }.present?
48
53
  end
49
-
54
+
50
55
  def self.subclasses model
51
56
  begin
52
57
  answer = Responses.get "Which models extend #{model.name}?"
@@ -61,14 +66,14 @@ module Schofield
61
66
  end
62
67
  subclasses
63
68
  end
64
-
69
+
65
70
  def self.add_level model, superclass=nil
66
71
  level = Level.new(model, superclass)
67
72
  @all << level
68
73
  @names[model.name.underscore] = @all.length - 1
69
74
  level
70
75
  end
71
-
76
+
72
77
  end
73
78
  end
74
79
  end
@@ -0,0 +1,17 @@
1
+ module Schofield
2
+
3
+ module Generators
4
+
5
+ class Navigation
6
+
7
+ def self.generate
8
+ Levels.all.select(&:routes?).inject("\n %ul\n %li.toplevel") do |memo, level|
9
+ memo += "\n\n - if permitted_to?(:read, :#{level.name.pluralize})"
10
+ memo += "\n %li{ :class => nav_classes('#{level.model.name}') }= link_to '#{level.model.name.titleize.pluralize}', admin_#{level.name.pluralize}_path"
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -1,14 +1,14 @@
1
1
  module Schofield
2
-
2
+
3
3
  module Generators
4
-
4
+
5
5
  class Responses
6
-
6
+
7
7
  class << self; attr_accessor :generator, :re_ask end
8
-
8
+
9
9
  @file = File.join(Rails.root, 'tmp', 'answers.txt')
10
-
11
-
10
+
11
+
12
12
  def self.get question
13
13
  @question = question
14
14
  if re_ask || (answer = past_answer).nil?
@@ -16,31 +16,31 @@ module Schofield
16
16
  end
17
17
  answer || ''
18
18
  end
19
-
19
+
20
20
  def self.ask
21
21
  re_ask = true
22
22
  answer = generator.ask(@question)
23
23
  @answers ||= {}
24
24
  @answers[@question] = answer || ''
25
25
  end
26
-
26
+
27
27
  def self.past_answer
28
28
  self.past_answers[@question]
29
29
  end
30
-
30
+
31
31
  def self.past_answers
32
32
  @answers ||= File.exists?(@file) ? File.open(@file, 'rb') { |f| Marshal.load(f) } : {}
33
33
  end
34
-
34
+
35
35
  def self.save
36
36
  File.open(@file, 'wb') { |io| Marshal.dump(@answers, io) }
37
37
  end
38
-
38
+
39
39
  def self.say string
40
40
  generator.say(string)
41
41
  end
42
-
42
+
43
43
  end
44
-
44
+
45
45
  end
46
46
  end