rear 0.2.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.
- checksums.yaml +15 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +20 -0
- data/LICENSE +19 -0
- data/README.md +101 -0
- data/Rakefile +79 -0
- data/assets/api.js +307 -0
- data/assets/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css +8 -0
- data/assets/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js +26 -0
- data/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/assets/bootstrap/css/bootstrap.min.css +9 -0
- data/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/assets/bootstrap/img/glyphicons-halflings.png +0 -0
- data/assets/bootstrap/js/bootstrap.min.js +6 -0
- data/assets/jquery.js +5 -0
- data/assets/noty/jquery.noty.js +520 -0
- data/assets/noty/layouts/top.js +34 -0
- data/assets/noty/layouts/topRight.js +43 -0
- data/assets/noty/promise.js +432 -0
- data/assets/noty/themes/default.js +156 -0
- data/assets/select2-bootstrap.css +86 -0
- data/assets/select2/select2-spinner.gif +0 -0
- data/assets/select2/select2.css +652 -0
- data/assets/select2/select2.min.js +22 -0
- data/assets/select2/select2.png +0 -0
- data/assets/select2/select2x2.png +0 -0
- data/assets/ui.css +75 -0
- data/assets/xhr.js +4 -0
- data/bin/rear +65 -0
- data/docs/Assocs.md +100 -0
- data/docs/Columns.md +404 -0
- data/docs/Deploy.md +62 -0
- data/docs/FileManager.md +75 -0
- data/docs/Filters.md +341 -0
- data/docs/Setup.md +201 -0
- data/lib/rear.rb +13 -0
- data/lib/rear/actions.rb +98 -0
- data/lib/rear/constants.rb +61 -0
- data/lib/rear/controller_setup.rb +249 -0
- data/lib/rear/helpers.rb +17 -0
- data/lib/rear/helpers/class.rb +46 -0
- data/lib/rear/helpers/columns.rb +68 -0
- data/lib/rear/helpers/filters.rb +147 -0
- data/lib/rear/helpers/generic.rb +73 -0
- data/lib/rear/helpers/order.rb +47 -0
- data/lib/rear/helpers/pager.rb +35 -0
- data/lib/rear/helpers/render.rb +37 -0
- data/lib/rear/home_controller.rb +10 -0
- data/lib/rear/input.rb +341 -0
- data/lib/rear/orm.rb +73 -0
- data/lib/rear/rear.rb +74 -0
- data/lib/rear/setup.rb +9 -0
- data/lib/rear/setup/associations.rb +33 -0
- data/lib/rear/setup/columns.rb +109 -0
- data/lib/rear/setup/filters.rb +314 -0
- data/lib/rear/setup/generic.rb +59 -0
- data/lib/rear/setup/menu.rb +39 -0
- data/lib/rear/templates/editor/ace.slim +7 -0
- data/lib/rear/templates/editor/assocs.slim +10 -0
- data/lib/rear/templates/editor/boolean.slim +5 -0
- data/lib/rear/templates/editor/bulk_edit.slim +75 -0
- data/lib/rear/templates/editor/checkbox.slim +5 -0
- data/lib/rear/templates/editor/ckeditor.slim +7 -0
- data/lib/rear/templates/editor/date.slim +9 -0
- data/lib/rear/templates/editor/datetime.slim +9 -0
- data/lib/rear/templates/editor/layout.slim +103 -0
- data/lib/rear/templates/editor/password.slim +1 -0
- data/lib/rear/templates/editor/radio.slim +5 -0
- data/lib/rear/templates/editor/select.slim +3 -0
- data/lib/rear/templates/editor/string.slim +1 -0
- data/lib/rear/templates/editor/text.slim +1 -0
- data/lib/rear/templates/editor/time.slim +9 -0
- data/lib/rear/templates/error.slim +36 -0
- data/lib/rear/templates/filters/boolean.slim +10 -0
- data/lib/rear/templates/filters/checkbox.slim +15 -0
- data/lib/rear/templates/filters/date.slim +10 -0
- data/lib/rear/templates/filters/datetime.slim +10 -0
- data/lib/rear/templates/filters/layout.slim +26 -0
- data/lib/rear/templates/filters/radio.slim +14 -0
- data/lib/rear/templates/filters/select.slim +9 -0
- data/lib/rear/templates/filters/string.slim +3 -0
- data/lib/rear/templates/filters/text.slim +3 -0
- data/lib/rear/templates/filters/time.slim +10 -0
- data/lib/rear/templates/home.slim +0 -0
- data/lib/rear/templates/layout.slim +78 -0
- data/lib/rear/templates/pager.slim +22 -0
- data/lib/rear/templates/pane/ace.slim +2 -0
- data/lib/rear/templates/pane/assocs.slim +62 -0
- data/lib/rear/templates/pane/boolean.slim +2 -0
- data/lib/rear/templates/pane/checkbox.slim +5 -0
- data/lib/rear/templates/pane/ckeditor.slim +2 -0
- data/lib/rear/templates/pane/date.slim +2 -0
- data/lib/rear/templates/pane/datetime.slim +2 -0
- data/lib/rear/templates/pane/layout.slim +111 -0
- data/lib/rear/templates/pane/password.slim +2 -0
- data/lib/rear/templates/pane/quickview.slim +21 -0
- data/lib/rear/templates/pane/radio.slim +5 -0
- data/lib/rear/templates/pane/select.slim +5 -0
- data/lib/rear/templates/pane/string.slim +2 -0
- data/lib/rear/templates/pane/text.slim +2 -0
- data/lib/rear/templates/pane/time.slim +2 -0
- data/lib/rear/utils.rb +288 -0
- data/rear.gemspec +27 -0
- data/test/helpers.rb +33 -0
- data/test/models/ar.rb +52 -0
- data/test/models/dm.rb +53 -0
- data/test/models/sq.rb +58 -0
- data/test/setup.rb +4 -0
- data/test/templates/adhoc/book/editor/name.slim +1 -0
- data/test/templates/adhoc/book/editor/string.slim +1 -0
- data/test/templates/adhoc/book/pane/name.slim +1 -0
- data/test/templates/adhoc/book/pane/string.slim +1 -0
- data/test/templates/shared/shared-templates/editor/string.slim +1 -0
- data/test/templates/shared/shared-templates/pane/string.slim +1 -0
- data/test/test__assocs.rb +249 -0
- data/test/test__columns.rb +269 -0
- data/test/test__crud.rb +81 -0
- data/test/test__custom_templates.rb +147 -0
- data/test/test__filters.rb +228 -0
- metadata +220 -0
data/lib/rear/input.rb
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
class RearInput
|
|
2
|
+
include RearConstants
|
|
3
|
+
include RearUtils
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :string_name, :type
|
|
6
|
+
attr_reader :dom_id, :css_class
|
|
7
|
+
|
|
8
|
+
def initialize name, type = COLUMNS__DEFAULT_TYPE, attrs = {}, brand_new_item = nil, &proc
|
|
9
|
+
@name, @string_name, @type = name, name.to_s, type
|
|
10
|
+
@brand_new_item = brand_new_item
|
|
11
|
+
@dom_id = ['Rear', 'Input', 'DOMIDFor', @string_name.capitalize.gsub(/\W/, ''), __id__].join
|
|
12
|
+
@css_class = 'CSSClassFor' << @dom_id
|
|
13
|
+
|
|
14
|
+
@pane_template = pane_template @type
|
|
15
|
+
@pane_value = proc do
|
|
16
|
+
val = item[column.name]
|
|
17
|
+
if val.is_a?(String)
|
|
18
|
+
val.size > COLUMNS__PANE_MAX_LENGTH ?
|
|
19
|
+
val[0..COLUMNS__PANE_MAX_LENGTH] + ' ...' : val
|
|
20
|
+
else
|
|
21
|
+
val
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@editor_template = editor_template @type
|
|
26
|
+
@editor_value = proc { item[column.name] }
|
|
27
|
+
|
|
28
|
+
@active_options = proc { item[column.name] }
|
|
29
|
+
|
|
30
|
+
@html_attrs = Hash[[:*, :pane, :editor].map {|s| [s, {}]}]
|
|
31
|
+
html_attrs(attrs)
|
|
32
|
+
self.instance_exec(&proc) if proc
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def name?
|
|
36
|
+
return if disabled?
|
|
37
|
+
return if readonly?
|
|
38
|
+
multiple? || checkbox? ? '%s[]' % name : name.to_s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def label label = nil
|
|
42
|
+
@label = label if label
|
|
43
|
+
@label
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def row row = nil
|
|
47
|
+
@row = row.to_s if row
|
|
48
|
+
@row
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def row?
|
|
52
|
+
@row
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# set HTML attributes to be used on current column on both pane and editor pages
|
|
56
|
+
# @note will override any attrs set globally via `html_attrs` at class level
|
|
57
|
+
def html_attrs attrs = {}
|
|
58
|
+
set_html_attrs(attrs) if attrs.any?
|
|
59
|
+
@html_attrs[:*] || {}
|
|
60
|
+
end
|
|
61
|
+
alias attrs html_attrs
|
|
62
|
+
|
|
63
|
+
# set HTML attributes to be used on current column on pane pages
|
|
64
|
+
# @note will override any attrs set globally via `pane_attrs` or `html_attrs` at class level
|
|
65
|
+
def pane_attrs attrs = {}
|
|
66
|
+
set_html_attrs(attrs, :pane) if attrs.any?
|
|
67
|
+
pane_attrs? || html_attrs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def pane_attrs?
|
|
71
|
+
(a = @html_attrs[:pane]) && a.any? && a
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# set HTML attributes to be used on current column on editor pages
|
|
75
|
+
# @note will override any attrs set globally via `editor_attrs` or `html_attrs` at class level
|
|
76
|
+
def editor_attrs attrs = {}
|
|
77
|
+
set_html_attrs(attrs, :editor) if attrs.any?
|
|
78
|
+
editor_attrs? || html_attrs
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def editor_attrs?
|
|
82
|
+
(a = @html_attrs[:editor]) && a.any? && a
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def pane_template template = nil, &proc
|
|
86
|
+
caller.each {|l| puts l} if template == :integer
|
|
87
|
+
if template
|
|
88
|
+
@pane_template = 'pane/%s.slim' % template
|
|
89
|
+
elsif template == false
|
|
90
|
+
@pane_template = nil
|
|
91
|
+
end
|
|
92
|
+
@pane_template = proc if proc
|
|
93
|
+
@pane_template
|
|
94
|
+
end
|
|
95
|
+
alias pane pane_template
|
|
96
|
+
|
|
97
|
+
def pane_value &proc
|
|
98
|
+
@pane_value = proc if proc
|
|
99
|
+
@pane_value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def editor_template template = nil, &proc
|
|
103
|
+
if template
|
|
104
|
+
@editor_template = 'editor/%s.slim' % template
|
|
105
|
+
elsif template == false
|
|
106
|
+
@editor_template = nil
|
|
107
|
+
end
|
|
108
|
+
@editor_template = proc if proc
|
|
109
|
+
@editor_template
|
|
110
|
+
end
|
|
111
|
+
alias editor editor_template
|
|
112
|
+
|
|
113
|
+
def editor_value &proc
|
|
114
|
+
@editor_value = proc if proc
|
|
115
|
+
@editor_value
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def value &proc
|
|
119
|
+
pane_value &proc
|
|
120
|
+
editor_value &proc
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# required on :radio, :checkbox and :select columns.
|
|
124
|
+
# options provided as Array or Hash
|
|
125
|
+
# use an Array when keys are the same as values.
|
|
126
|
+
# use a Hash when keys are different from values.
|
|
127
|
+
# if block given it should return an Array of selected keys or a single key.
|
|
128
|
+
# if block not given, no options will be selected.
|
|
129
|
+
#
|
|
130
|
+
# @example use an Array. will send 'Orange' to ORM
|
|
131
|
+
#
|
|
132
|
+
# column :fruit, :select do
|
|
133
|
+
# options('Apple', 'Orange', 'Peach') { 'Orange' }
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
#
|
|
137
|
+
# @example use a Hash. will send '2' to ORM
|
|
138
|
+
#
|
|
139
|
+
# column :fruit, :select do
|
|
140
|
+
# options(1 => 'Apple', 2 => 'Orange', 3 => 'Peach') { 2 }
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# @note when using :checkbox type or :select type with :multiple option,
|
|
144
|
+
# Espresso will send an Array of keys to your ORM.
|
|
145
|
+
# usually ORM will handle received array automatically,
|
|
146
|
+
# however, if you want to send a string rather than array,
|
|
147
|
+
# use `before` callback to coerce the array into string.
|
|
148
|
+
#
|
|
149
|
+
# @example this example will send ['Orange', 'Peach'] Array
|
|
150
|
+
# which will be caught by `before :save` callback
|
|
151
|
+
# and coerced into string
|
|
152
|
+
#
|
|
153
|
+
# before :save do
|
|
154
|
+
# params[:fruit] = params[:fruit].join(',')
|
|
155
|
+
# end
|
|
156
|
+
# column :fruit, :select, :multiple => true do
|
|
157
|
+
# options('Apple', 'Orange', 'Peach') { ['Orange', 'Peach'] }
|
|
158
|
+
# end
|
|
159
|
+
#
|
|
160
|
+
# @example will send ['2', '3'] Array to your ORM
|
|
161
|
+
# column :fruit, :checkbox do
|
|
162
|
+
# options(1 => 'Apple', 2 => 'Orange', 3 => 'Peach') { [2, 3] }
|
|
163
|
+
# end
|
|
164
|
+
#
|
|
165
|
+
def options *args, &proc
|
|
166
|
+
return @options || {} if args.empty?
|
|
167
|
+
@options = args.inject({}) do |f,c|
|
|
168
|
+
c.is_a?(Hash) ? f.merge(c) : f.merge(c => c)
|
|
169
|
+
end
|
|
170
|
+
@active_options = proc if proc
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def active_options
|
|
174
|
+
@active_options
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def optioned?
|
|
178
|
+
select? || checkbox? || radio?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def pane?
|
|
182
|
+
@pane_template
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def editor?
|
|
186
|
+
@editor_template
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def readonly!
|
|
190
|
+
@readonly = true
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def readonly?
|
|
194
|
+
return if @brand_new_item
|
|
195
|
+
@readonly
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def disable!
|
|
199
|
+
@disabled = true
|
|
200
|
+
end
|
|
201
|
+
alias disabled! disable!
|
|
202
|
+
|
|
203
|
+
def disabled?
|
|
204
|
+
@disabled
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def multiple!
|
|
208
|
+
@multiple = true
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def multiple?
|
|
212
|
+
@multiple
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def checkbox?
|
|
216
|
+
type == :checkbox
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def select?
|
|
220
|
+
type == :select
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def radio?
|
|
224
|
+
type == :radio
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def textual?
|
|
228
|
+
type == :text || type == :rte
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def boolean?
|
|
232
|
+
type == :boolean
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# when ordering by some column, "ORDER BY" will use only the selected column.
|
|
236
|
+
# this column-specific setup allow to order returned items by multiple columns.
|
|
237
|
+
#
|
|
238
|
+
# # @example
|
|
239
|
+
# class News
|
|
240
|
+
# include DataMapper::Resource
|
|
241
|
+
#
|
|
242
|
+
# property :id, Serial
|
|
243
|
+
# property :name, String
|
|
244
|
+
# property :date, Date
|
|
245
|
+
# property :status, Integer
|
|
246
|
+
# end
|
|
247
|
+
#
|
|
248
|
+
# Rear.register News do
|
|
249
|
+
# input :date do
|
|
250
|
+
# order_by :date, :id
|
|
251
|
+
# end
|
|
252
|
+
# end
|
|
253
|
+
#
|
|
254
|
+
# this method are also useful when you need to sort items by a "decorative" column,
|
|
255
|
+
# meant a column that does not exists in db but you need it on pane pages
|
|
256
|
+
# to render more informative rows.
|
|
257
|
+
# For ex. display category of each article when rendering articles
|
|
258
|
+
#
|
|
259
|
+
# # @example
|
|
260
|
+
# class Article
|
|
261
|
+
# include DataMapper::Resource
|
|
262
|
+
# # ...
|
|
263
|
+
#
|
|
264
|
+
# belongs_to :category
|
|
265
|
+
# end
|
|
266
|
+
#
|
|
267
|
+
# Rear.register do
|
|
268
|
+
# # defining a "decorative" column to display categories of each article
|
|
269
|
+
# input :Categories do
|
|
270
|
+
# pane { item.categories.map {|c| c.name}.join(', ') }
|
|
271
|
+
# # when Categories column clicked on pane page,
|
|
272
|
+
# # we want articles to be sorted by category id
|
|
273
|
+
# order_by :category_id
|
|
274
|
+
# end
|
|
275
|
+
# end
|
|
276
|
+
#
|
|
277
|
+
# @note do not pass ordering vector when setting costom `order_by` for columns.
|
|
278
|
+
# vector will be added automatically, so pass only column names.
|
|
279
|
+
# if vector passed, ordering will broke badly.
|
|
280
|
+
#
|
|
281
|
+
def order_by *columns
|
|
282
|
+
@order_by = columns if columns.any?
|
|
283
|
+
@order_by || [name]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def order_by?
|
|
287
|
+
@order_by
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# allow to define a list of snippets you need to insert into edited content.
|
|
291
|
+
# relevant only on :ace / :ckeditor columns.
|
|
292
|
+
def snippets *snippets, &proc
|
|
293
|
+
snippets.any? && @snippets = snippets
|
|
294
|
+
proc && @snippets = proc
|
|
295
|
+
@snippets
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# various opts for CKEditor
|
|
299
|
+
#
|
|
300
|
+
# @param [Hash] opts
|
|
301
|
+
# @option opts :path
|
|
302
|
+
# physical path to folder containing images/videos to be picked up by file browser
|
|
303
|
+
# @option opts :prefix
|
|
304
|
+
# file browser will build URL to file/video by extracting
|
|
305
|
+
# path(set via `:path` option) from full path to file.
|
|
306
|
+
# that's it, if path is "/foo/bar"
|
|
307
|
+
# and full path to file is "/foo/bar/baz/image.jpg",
|
|
308
|
+
# the URL used in browser will be "/baz/image.jpg".
|
|
309
|
+
# `:prefix` option allow to define a string to be prepended to "/baz/image.jpg".
|
|
310
|
+
# @option opts :lang
|
|
311
|
+
# localizing CKEditors
|
|
312
|
+
def ckeditor opts = {}
|
|
313
|
+
@ckeditor_opts = opts
|
|
314
|
+
end
|
|
315
|
+
def ckeditor_opts; @ckeditor_opts || {}; end
|
|
316
|
+
|
|
317
|
+
private
|
|
318
|
+
def set_html_attrs html_attrs, scope = :*
|
|
319
|
+
html_attrs = Hash[@html_attrs[scope].merge(html_attrs)]
|
|
320
|
+
|
|
321
|
+
html_attrs.each_key do |k|
|
|
322
|
+
readonly! if k == :readonly
|
|
323
|
+
disabled! if k == :disabled
|
|
324
|
+
multiple! if k == :multiple
|
|
325
|
+
end
|
|
326
|
+
html_attrs.delete(:readonly) if @brand_new_item
|
|
327
|
+
|
|
328
|
+
html_attrs.delete(:pane) == false && pane(false)
|
|
329
|
+
html_attrs.delete(:editor) == false && editor(false)
|
|
330
|
+
|
|
331
|
+
unless @label = html_attrs.delete(:label)
|
|
332
|
+
@label = name.to_s.gsub(/_/, ' ')
|
|
333
|
+
@label.capitalize! unless name == :id
|
|
334
|
+
@label << '?' if type == :boolean
|
|
335
|
+
end
|
|
336
|
+
@row = html_attrs.delete(:row)
|
|
337
|
+
|
|
338
|
+
@html_attrs[scope] = normalize_html_attrs(html_attrs)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
end
|
data/lib/rear/orm.rb
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class RearORM
|
|
2
|
+
include RearUtils
|
|
3
|
+
|
|
4
|
+
attr_reader :model, :pkey
|
|
5
|
+
|
|
6
|
+
def initialize model, pkey = :id
|
|
7
|
+
@model, @pkey, @orm = model, pkey, orm(model)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def [] id
|
|
11
|
+
sequel? ?
|
|
12
|
+
model[id] :
|
|
13
|
+
model.first(conditions: {pkey => id})
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def count conditions = {}
|
|
17
|
+
if sequel?
|
|
18
|
+
sequel_dataset(model, conditions).count
|
|
19
|
+
else
|
|
20
|
+
model.count(conditions)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def filter conditions = {}
|
|
25
|
+
if sequel?
|
|
26
|
+
sequel_dataset(model, conditions).all
|
|
27
|
+
else
|
|
28
|
+
model.all(conditions)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def assoc_filter assoc, item, conditions
|
|
33
|
+
if sequel?
|
|
34
|
+
sequel_dataset(item.send('%s_dataset' % assoc), conditions).all
|
|
35
|
+
else
|
|
36
|
+
result = item.send(assoc, conditions)
|
|
37
|
+
result.respond_to?(:size) ? result : [result].compact
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def assoc_count assoc, item, conditions
|
|
42
|
+
if sequel?
|
|
43
|
+
sequel_dataset(item.send('%s_dataset' % assoc), conditions).count
|
|
44
|
+
else
|
|
45
|
+
if result = item.send(assoc)
|
|
46
|
+
result.respond_to?(:count) ? result.count(conditions) : 1
|
|
47
|
+
else
|
|
48
|
+
0
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def delete_multiple *ids
|
|
54
|
+
ids.flatten!
|
|
55
|
+
model.destroy(ids) if @orm == :ar
|
|
56
|
+
model.all(pkey => ids).destroy! if @orm == :dm
|
|
57
|
+
model.filter(pkey => ids).destroy if @orm == :sq
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def sequel?; @orm == :sq end
|
|
61
|
+
|
|
62
|
+
def sequel_dataset dataset, conditions = {}
|
|
63
|
+
filters, limit, offset, order =
|
|
64
|
+
conditions.values_at(:conditions, :limit, :offset, :order)
|
|
65
|
+
ds = limit ? dataset.limit(*[limit, offset].compact) : dataset
|
|
66
|
+
ds.filter(filters || {}).order(*[order].compact)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def singularize smth
|
|
70
|
+
model.send(:singularize, smth) if sequel?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
data/lib/rear/rear.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Rear
|
|
2
|
+
class << self
|
|
3
|
+
MODELS, CONTROLLERS = {}, []
|
|
4
|
+
|
|
5
|
+
def register *models, &proc
|
|
6
|
+
models.flatten.each do |model|
|
|
7
|
+
(MODELS[model] ||= []).push(proc)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
alias setup register
|
|
11
|
+
|
|
12
|
+
def included base
|
|
13
|
+
if EUtils.is_app?(base)
|
|
14
|
+
RearControllerSetup.init(base)
|
|
15
|
+
CONTROLLERS << base
|
|
16
|
+
else
|
|
17
|
+
raise ArgumentError, '%s is not a Espresso controller' % base
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def controllers
|
|
22
|
+
@controllers ||= begin
|
|
23
|
+
MODELS.each_pair do |model,procs|
|
|
24
|
+
model = RearUtils.extract_constant(model)
|
|
25
|
+
controller = RearUtils.initialize_model_controller(model)
|
|
26
|
+
procs.compact.each {|proc| controller.class_exec(model, &proc)}
|
|
27
|
+
CONTROLLERS << controller
|
|
28
|
+
end
|
|
29
|
+
CONTROLLERS.uniq.each do |controller|
|
|
30
|
+
controller.model && controller.assocs.each_value do |assocs|
|
|
31
|
+
assocs.each_value do |assoc|
|
|
32
|
+
if remote_model = assoc[:remote_model]
|
|
33
|
+
CONTROLLERS << RearUtils.associated_model_controller(remote_model)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
MODELS.clear.freeze
|
|
39
|
+
CONTROLLERS.unshift RearHomeController
|
|
40
|
+
CONTROLLERS.uniq!
|
|
41
|
+
CONTROLLERS.freeze
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def menu
|
|
46
|
+
@menu ||= begin
|
|
47
|
+
containers = {}
|
|
48
|
+
controllers = Rear.controllers.reject {|c| c == RearHomeController}.
|
|
49
|
+
select {|c| c.label}.inject({}) do |f,c|
|
|
50
|
+
c.menu_group? ?
|
|
51
|
+
((containers[c.menu_group] ||= []).push(c); f) : f.merge(c=>[c])
|
|
52
|
+
end
|
|
53
|
+
controllers.merge(containers).sort do |a,b|
|
|
54
|
+
b.last.inject(0) {|t,c| t += c.position} <=> a.last.inject(0) {|t,c| t += c.position}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def app
|
|
60
|
+
@app ||= E.new.mount(controllers)
|
|
61
|
+
end
|
|
62
|
+
alias to_app app
|
|
63
|
+
alias mount! app
|
|
64
|
+
|
|
65
|
+
def call env
|
|
66
|
+
app.call env
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def run *args
|
|
70
|
+
app.run *args
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|