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.
Files changed (121) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +7 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +20 -0
  5. data/LICENSE +19 -0
  6. data/README.md +101 -0
  7. data/Rakefile +79 -0
  8. data/assets/api.js +307 -0
  9. data/assets/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css +8 -0
  10. data/assets/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js +26 -0
  11. data/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
  12. data/assets/bootstrap/css/bootstrap.min.css +9 -0
  13. data/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
  14. data/assets/bootstrap/img/glyphicons-halflings.png +0 -0
  15. data/assets/bootstrap/js/bootstrap.min.js +6 -0
  16. data/assets/jquery.js +5 -0
  17. data/assets/noty/jquery.noty.js +520 -0
  18. data/assets/noty/layouts/top.js +34 -0
  19. data/assets/noty/layouts/topRight.js +43 -0
  20. data/assets/noty/promise.js +432 -0
  21. data/assets/noty/themes/default.js +156 -0
  22. data/assets/select2-bootstrap.css +86 -0
  23. data/assets/select2/select2-spinner.gif +0 -0
  24. data/assets/select2/select2.css +652 -0
  25. data/assets/select2/select2.min.js +22 -0
  26. data/assets/select2/select2.png +0 -0
  27. data/assets/select2/select2x2.png +0 -0
  28. data/assets/ui.css +75 -0
  29. data/assets/xhr.js +4 -0
  30. data/bin/rear +65 -0
  31. data/docs/Assocs.md +100 -0
  32. data/docs/Columns.md +404 -0
  33. data/docs/Deploy.md +62 -0
  34. data/docs/FileManager.md +75 -0
  35. data/docs/Filters.md +341 -0
  36. data/docs/Setup.md +201 -0
  37. data/lib/rear.rb +13 -0
  38. data/lib/rear/actions.rb +98 -0
  39. data/lib/rear/constants.rb +61 -0
  40. data/lib/rear/controller_setup.rb +249 -0
  41. data/lib/rear/helpers.rb +17 -0
  42. data/lib/rear/helpers/class.rb +46 -0
  43. data/lib/rear/helpers/columns.rb +68 -0
  44. data/lib/rear/helpers/filters.rb +147 -0
  45. data/lib/rear/helpers/generic.rb +73 -0
  46. data/lib/rear/helpers/order.rb +47 -0
  47. data/lib/rear/helpers/pager.rb +35 -0
  48. data/lib/rear/helpers/render.rb +37 -0
  49. data/lib/rear/home_controller.rb +10 -0
  50. data/lib/rear/input.rb +341 -0
  51. data/lib/rear/orm.rb +73 -0
  52. data/lib/rear/rear.rb +74 -0
  53. data/lib/rear/setup.rb +9 -0
  54. data/lib/rear/setup/associations.rb +33 -0
  55. data/lib/rear/setup/columns.rb +109 -0
  56. data/lib/rear/setup/filters.rb +314 -0
  57. data/lib/rear/setup/generic.rb +59 -0
  58. data/lib/rear/setup/menu.rb +39 -0
  59. data/lib/rear/templates/editor/ace.slim +7 -0
  60. data/lib/rear/templates/editor/assocs.slim +10 -0
  61. data/lib/rear/templates/editor/boolean.slim +5 -0
  62. data/lib/rear/templates/editor/bulk_edit.slim +75 -0
  63. data/lib/rear/templates/editor/checkbox.slim +5 -0
  64. data/lib/rear/templates/editor/ckeditor.slim +7 -0
  65. data/lib/rear/templates/editor/date.slim +9 -0
  66. data/lib/rear/templates/editor/datetime.slim +9 -0
  67. data/lib/rear/templates/editor/layout.slim +103 -0
  68. data/lib/rear/templates/editor/password.slim +1 -0
  69. data/lib/rear/templates/editor/radio.slim +5 -0
  70. data/lib/rear/templates/editor/select.slim +3 -0
  71. data/lib/rear/templates/editor/string.slim +1 -0
  72. data/lib/rear/templates/editor/text.slim +1 -0
  73. data/lib/rear/templates/editor/time.slim +9 -0
  74. data/lib/rear/templates/error.slim +36 -0
  75. data/lib/rear/templates/filters/boolean.slim +10 -0
  76. data/lib/rear/templates/filters/checkbox.slim +15 -0
  77. data/lib/rear/templates/filters/date.slim +10 -0
  78. data/lib/rear/templates/filters/datetime.slim +10 -0
  79. data/lib/rear/templates/filters/layout.slim +26 -0
  80. data/lib/rear/templates/filters/radio.slim +14 -0
  81. data/lib/rear/templates/filters/select.slim +9 -0
  82. data/lib/rear/templates/filters/string.slim +3 -0
  83. data/lib/rear/templates/filters/text.slim +3 -0
  84. data/lib/rear/templates/filters/time.slim +10 -0
  85. data/lib/rear/templates/home.slim +0 -0
  86. data/lib/rear/templates/layout.slim +78 -0
  87. data/lib/rear/templates/pager.slim +22 -0
  88. data/lib/rear/templates/pane/ace.slim +2 -0
  89. data/lib/rear/templates/pane/assocs.slim +62 -0
  90. data/lib/rear/templates/pane/boolean.slim +2 -0
  91. data/lib/rear/templates/pane/checkbox.slim +5 -0
  92. data/lib/rear/templates/pane/ckeditor.slim +2 -0
  93. data/lib/rear/templates/pane/date.slim +2 -0
  94. data/lib/rear/templates/pane/datetime.slim +2 -0
  95. data/lib/rear/templates/pane/layout.slim +111 -0
  96. data/lib/rear/templates/pane/password.slim +2 -0
  97. data/lib/rear/templates/pane/quickview.slim +21 -0
  98. data/lib/rear/templates/pane/radio.slim +5 -0
  99. data/lib/rear/templates/pane/select.slim +5 -0
  100. data/lib/rear/templates/pane/string.slim +2 -0
  101. data/lib/rear/templates/pane/text.slim +2 -0
  102. data/lib/rear/templates/pane/time.slim +2 -0
  103. data/lib/rear/utils.rb +288 -0
  104. data/rear.gemspec +27 -0
  105. data/test/helpers.rb +33 -0
  106. data/test/models/ar.rb +52 -0
  107. data/test/models/dm.rb +53 -0
  108. data/test/models/sq.rb +58 -0
  109. data/test/setup.rb +4 -0
  110. data/test/templates/adhoc/book/editor/name.slim +1 -0
  111. data/test/templates/adhoc/book/editor/string.slim +1 -0
  112. data/test/templates/adhoc/book/pane/name.slim +1 -0
  113. data/test/templates/adhoc/book/pane/string.slim +1 -0
  114. data/test/templates/shared/shared-templates/editor/string.slim +1 -0
  115. data/test/templates/shared/shared-templates/pane/string.slim +1 -0
  116. data/test/test__assocs.rb +249 -0
  117. data/test/test__columns.rb +269 -0
  118. data/test/test__crud.rb +81 -0
  119. data/test/test__custom_templates.rb +147 -0
  120. data/test/test__filters.rb +228 -0
  121. metadata +220 -0
@@ -0,0 +1,10 @@
1
+ - attrs[:class] ||= 'input-small search-query'
2
+ - attrs[:placeholder] ||= (setup[:label] unless value)
3
+ - id = 'filters-%s-%s' % [name.hash, self.__id__]
4
+ .input-append.date id=id
5
+ input name=name value=value type="text" data-format="yyyy-MM-dd" *attrs
6
+ span.add-on
7
+ i data-time-icon="icon-time" data-date-icon="icon-calendar"
8
+
9
+ javascript:
10
+ $(function() {$('##{id}').datetimepicker({pickTime: false})});
@@ -0,0 +1,10 @@
1
+ - attrs[:class] ||= 'input-medium search-query'
2
+ - attrs[:placeholder] ||= (setup[:label] unless value)
3
+ - id = 'filters-%s-%s' % [name.hash, self.__id__]
4
+ .input-append.date id=id
5
+ input name=name value=value type="text" data-format="yyyy-MM-dd hh:mm:ss" *attrs
6
+ span.add-on
7
+ i data-time-icon="icon-time" data-date-icon="icon-calendar"
8
+
9
+ javascript:
10
+ $(function() {$('##{id}').datetimepicker()});
@@ -0,0 +1,26 @@
1
+ - dom_id_format = 'pane-filters-%s-' + dom_id
2
+
3
+ form.form-search.form-inline.pane-filters-form id=(dom_id_format % :form)
4
+ div id=(dom_id_format % :input) style="display: inline-block;"
5
+ == render_filters
6
+
7
+ - if @reverse_assoc
8
+ - jsvar = @reverse_assoc.dom_id + '__filters_instance'
9
+ - route = '%s/%s' % [@reverse_assoc.route, @reverse_assoc.attached]
10
+ javascript:
11
+ var #{jsvar} = new Rear.Filters('#{dom_id}', '#{route}');
12
+ .btn-group
13
+ button.btn.btn-small type="button" onclick="#{jsvar}.apply();"
14
+ i.icon-search
15
+ | Search
16
+ button.btn.btn-small type="button" onclick="#{jsvar}.reset();"
17
+ i.icon-remove
18
+ | Reset
19
+ - else
20
+ .btn-group
21
+ button.btn.btn-small type="submit" value="Search"
22
+ i.icon-search
23
+ | Search
24
+ a.btn.btn-small href=base_url
25
+ i.icon-remove
26
+ | Reset
@@ -0,0 +1,14 @@
1
+ - if setup[:decorative?]
2
+ - attrs[:onclick] = "new Rear.Filters('%s', '%s').update();" % \
3
+ [dom_id, route(:html_filters, true, dom_id: dom_id)]
4
+
5
+ .input-prepend.input-append
6
+ span.add-on
7
+ = setup[:label]
8
+
9
+ span.add-on
10
+ - filter_setup_to_options(setup).each_pair do |v,l|
11
+ - attrs[:checked] = value == v.to_s
12
+ label.radio
13
+ input name=name type="radio" value=v *attrs = l
14
+ |  
@@ -0,0 +1,9 @@
1
+ - attrs[:class] ||= 'selectable'
2
+ - if setup[:decorative?]
3
+ - attrs[:onchange] = "new Rear.Filters('%s', '%s').update();" % \
4
+ [dom_id, route(:html_filters, true, dom_id: dom_id)]
5
+
6
+ select name=name data-placeholder=setup[:label] *attrs
7
+ option value=""
8
+ - filter_setup_to_options(setup).each_pair do |v,l|
9
+ option value=v selected=(value == v.to_s) = l
@@ -0,0 +1,3 @@
1
+ - attrs[:class] ||= 'input-medium search-query'
2
+ - attrs[:placeholder] ||= (setup[:label] unless value)
3
+ input name=name value=value type="text" *attrs
@@ -0,0 +1,3 @@
1
+ - attrs[:class] ||= 'input-medium search-query'
2
+ - attrs[:placeholder] ||= (setup[:label] unless value)
3
+ input name=name value=value type="text" *attrs
@@ -0,0 +1,10 @@
1
+ - attrs[:class] ||= 'input-small search-query'
2
+ - attrs[:placeholder] ||= (setup[:label] unless value)
3
+ - id = 'filters-%s-%s' % [name.hash, self.__id__]
4
+ .input-append.date id=id
5
+ input name=name value=value type="text" data-format="hh:mm:ss" *attrs
6
+ span.add-on
7
+ i data-time-icon="icon-time" data-date-icon="icon-calendar"
8
+
9
+ javascript:
10
+ $(function() {$('##{id}').datetimepicker({pickDate: false})});
File without changes
@@ -0,0 +1,78 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title = 'Rear / %s' % (__rear__.label || __rear__.default_label)
5
+ meta http-equiv="Content-Type" content="text/html; charset=UTF-8"
6
+ meta name="viewport" content="width=device-width, initial-scale=1.0"
7
+ - assets_mapper route(:assets), suffix: '-rear' do
8
+ == js_tag 'jquery'
9
+
10
+ - cd 'bootstrap'
11
+ == js_tag 'js/bootstrap.min'
12
+ == css_tag 'css/bootstrap.min'
13
+ == css_tag 'css/bootstrap-responsive.min'
14
+
15
+ - cd '../select2'
16
+ == js_tag 'select2.min'
17
+ == css_tag 'select2'
18
+
19
+ - cd '../noty'
20
+ == js_tag 'jquery.noty'
21
+ == js_tag 'layouts/top'
22
+ == js_tag 'layouts/topRight'
23
+ == js_tag 'themes/default'
24
+
25
+ - cd '../bootstrap-datetimepicker'
26
+ == js_tag 'js/bootstrap-datetimepicker.min'
27
+ == css_tag 'css/bootstrap-datetimepicker.min'
28
+
29
+ - cd '/'
30
+ == css_tag 'select2-bootstrap'
31
+ == js_tag 'api'
32
+ == js_tag 'xhr'
33
+ == css_tag 'ui'
34
+
35
+ body
36
+ .container-fluid
37
+ .row-fluid
38
+ .span
39
+ .navbar.navbar-fixed-top
40
+ .navbar-inner
41
+ .container
42
+
43
+ a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"
44
+ span.icon-bar
45
+ span.icon-bar
46
+ span.icon-bar
47
+
48
+ .nav-collapse.collapse
49
+ ul.nav
50
+ li.divider-vertical
51
+ li
52
+ a href=RearHomeController.base_url
53
+ i.icon-home
54
+ |  Home
55
+
56
+ li.divider-vertical
57
+ - Rear.menu.each do |(c,children)|
58
+ - if c.is_a?(String) && children.any?
59
+ - active_children = children.find {|ch| rq.script_name =~ /\A#{ch.base_url}\//}
60
+ li.dropdown class=('active' if active_children)
61
+ a.dropdown-toggle href="#"
62
+ = c
63
+ |  
64
+ b.caret
65
+ ul.dropdown-menu
66
+ - children.each do |ch|
67
+ li class=('active' if active_children == ch)
68
+ a href=ch.base_url = ch.label
69
+ - unless ch == children.last
70
+ li.divider
71
+ - else
72
+ li class=('active' if rq.script_name =~ /\A#{c.base_url}/)
73
+ a href=c.base_url = c.label
74
+
75
+ li.divider-vertical
76
+
77
+ .content
78
+ == yield
@@ -0,0 +1,22 @@
1
+ .pagination.pagination-centered
2
+ ul
3
+ - if action_name == :quickview
4
+ li
5
+ a href=route(pager_params)
6
+ i.icon-list
7
+
8
+ - if page_min > 1
9
+ li
10
+ == pager_linker(1)
11
+ li.disabled
12
+ a ...
13
+
14
+ - page_min.upto(page_max) do |p|
15
+ li class=('active' if p == current_page)
16
+ == pager_linker(p)
17
+
18
+ - if page_max < total_pages
19
+ li.disabled
20
+ a ...
21
+ li
22
+ == pager_linker(total_pages)
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = value
@@ -0,0 +1,62 @@
1
+ - jsvar = dom_id + '_assoc_instance'
2
+ javascript:
3
+ var #{jsvar} = new Rear.Assoc('#{@reverse_assoc.route}', '##{dom_id}', #{@reverse_assoc.readonly});
4
+
5
+ == html_filters if @pager_context[:total_pages] > 2 || opted_filters.any?
6
+
7
+ - if items.size > 0
8
+ == @pager
9
+
10
+ table.table
11
+ thead
12
+ th
13
+ - pane_columns.each do |column|
14
+ th class=('hoverable-active' if order_params[column.string_name])
15
+ - if vector = sortable_column?(column)
16
+ - params = pager_params(0).merge(order: {column.string_name => vector.first})
17
+ - url = route(action, *action_params__array, params)
18
+ - onclick = "Rear.switch_page('#%s', '%s');" % [dom_id, url]
19
+ a href='javascript:void(null);' onclick=onclick
20
+ i class=('icon-chevron-%s' % vector.last)
21
+ | &nbsp;
22
+ = column.label
23
+ - else
24
+ a href='javascript:void(null);' = column.label
25
+
26
+ - items.each do |item|
27
+ - self.item, self.item_id = item, item[pkey]
28
+ tr.pane-item.hoverable
29
+ td
30
+ label.checkbox
31
+ - action = @reverse_assoc.attached ? :delete : :create
32
+ - if @reverse_assoc.source_item
33
+ - type = :checkbox
34
+ - onclick = '%s.%s(%s);' % [jsvar, action, item_id]
35
+ - if @reverse_assoc.readonly
36
+ - onclick << 'return false;'
37
+ - else
38
+ - onclick << '%s.load_attached(this);' % jsvar
39
+ - else
40
+ - type, onclick = :radio, ''
41
+ - attrs = { \
42
+ type: type, \
43
+ name: @reverse_assoc.source_key, \
44
+ value: item[@reverse_assoc.target_key || pkey], \
45
+ class: [ @reverse_assoc.dom_id + '-assoc_toggler' ], \
46
+ onclick: onclick, \
47
+ checked: @reverse_assoc.attached }
48
+ - attrs[:class] << 'pane-assoc-attacher' unless @reverse_assoc.attached
49
+ input *attrs
50
+
51
+ - pane_columns.each do |column|
52
+ - self.column = column
53
+ td class=('hoverable-active' if order_params[column.string_name])
54
+ a.pane-column_value href=route(:edit, item_id, pager_params) target="_blank"
55
+ == render_pane_column column
56
+
57
+ - if @pager_context[:total_items] > __rear__.ipp
58
+ b.pull-right
59
+ = '%s - %s of %s' % @pager_context[:counter]
60
+
61
+ - else
62
+ h4 No related #{@reverse_assoc.name} detected
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = COLUMNS__BOOLEAN_MAP[value] || value || 'Unknown'
@@ -0,0 +1,5 @@
1
+ div *attrs(column, :pane)
2
+ - options.each_pair do |k,v|
3
+ - if active_options.include?(k)
4
+ = v
5
+ br
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = value
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = value.kind_of?(Date) ? value.strftime('%Y-%m-%d') : value
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = value.kind_of?(DateTime) ? value.strftime('%Y-%m-%d %H:%M:%S') : value
@@ -0,0 +1,111 @@
1
+ == html_filters
2
+ == @pager
3
+ - quick_filters.each_pair do |column,filters|
4
+ .btn-group
5
+ - filters.each_key do |label|
6
+ - active = quick_filter?(column) == label
7
+ - if active
8
+ - params = Hash[opted_quick_filters.reject {|c,f| c == column}]
9
+ - else
10
+ - params = opted_quick_filters.merge(column => label)
11
+ a.btn.btn-small href=route(quick_filters: params) class=('active' if active)
12
+ = label
13
+
14
+ table.table
15
+ thead
16
+ th
17
+ input type="checkbox" onclick="Rear.check_multiple('.pane-selected_item', this, true);"
18
+
19
+ - pane_columns.each do |column|
20
+ th class=('hoverable-active' if order_params[column.string_name])
21
+ - if vector = sortable_column?(column)
22
+ - url = route(pager_params(0).merge(order: {column.string_name => vector.first}))
23
+ a href=url
24
+ i class=('icon-chevron-%s' % vector.last)
25
+ | &nbsp;
26
+ = column.label
27
+ - else
28
+ a href='javascript:void(null);' = column.label
29
+
30
+ th
31
+ .pull-right
32
+ a href=route(:edit, 0)
33
+ .label.label-warning
34
+ i.icon-plus
35
+
36
+ - items.each do |item|
37
+
38
+ - self.item, self.item_id = item, item[pkey]
39
+ tr.pane-item.hoverable
40
+ td
41
+ label.checkbox
42
+ input.pane-selected_item type='checkbox' value=item_id onclick="Rear.check_multiple('.pane-selected_item', this);"
43
+
44
+ - pane_columns.each do |column|
45
+ - self.column = column
46
+ - classes = []
47
+ - classes << 'hoverable-active' if order_params[column.string_name]
48
+ - classes << 'pane-pkey' if column.name == pkey
49
+ td class=classes
50
+ a.pane-column_value href=route(:edit, item_id, pager_params)
51
+ == render_pane_column column
52
+
53
+ td
54
+ .pull-right
55
+ a.label.label-info href=route(:edit, item_id.to_s, pager_params)
56
+ i.icon-edit.icon-white
57
+
58
+ - if items.any?
59
+ - unless readonly?
60
+ javascript:
61
+ var crudifier;
62
+ function launch_bulk_editor() {
63
+ var selected = [];
64
+ $('.pane-selected_item:checked').each(function(i,e) {
65
+ selected.push($(e).val());
66
+ });
67
+ if(selected.length == 0) {
68
+ Rear.warn('Please select at least one item');
69
+ return false;
70
+ }
71
+ $.get(
72
+ '#{route(:bulk_edit)}',
73
+ {items: selected.join(" ")},
74
+ function(response){
75
+ crudifier = new Rear.BulkCRUD('#{self[:bulk_edit]}');
76
+
77
+ $('#bulk_editor-modal_body').html(response);
78
+ $('#bulk_editor-modal').modal().css({
79
+ 'margin-top': -80,
80
+ 'margin-left': function() {
81
+ return -($(this).width() / 2);
82
+ }
83
+ });
84
+ }
85
+ );
86
+ }
87
+
88
+ #bulk_editor-modal.modal.hide.fade.container
89
+ .modal-header
90
+ .btn-group
91
+ button.btn data-dismiss="modal" Cancel
92
+ button.btn.btn-success.input-medium.saveButton onclick="crudifier.invoke();" Save
93
+ button.btn.input-medium onclick="location.reload();" Done
94
+ .modal-body#bulk_editor-modal_body
95
+
96
+ a href="javascript:void(null);" onclick="launch_bulk_editor();"
97
+ span.badge.badge-warning
98
+ i.icon-edit
99
+ | &nbsp;Edit Selected
100
+ | &nbsp;&nbsp;
101
+
102
+ a onclick="if(confirm('This action can not be undone! Continue?')) { new Rear.CRUD().delete_selected('#{self[:delete_selected]}', '#{{route(pager_params)}}', #{readonly? ? true : false}) } else { return false }" href="javascript:void(null);"
103
+ span.badge.badge-important
104
+ i.icon-remove
105
+ | &nbsp;Delete Selected
106
+
107
+ - if @pager_context[:total_items] > __rear__.ipp
108
+ b.pull-right
109
+ = '%s - %s of %s' % @pager_context[:counter]
110
+
111
+ == @pager
@@ -0,0 +1,2 @@
1
+ div *attrs(column, :pane)
2
+ = '*'*value.to_s.size
@@ -0,0 +1,21 @@
1
+ - selected = params[:selected].to_i
2
+
3
+ div id=dom_id
4
+ == @pager
5
+
6
+ table.table
7
+ thead
8
+ - pane_columns.each do |column|
9
+ th = column.label
10
+
11
+ - items.each do |item|
12
+ - self.item, self.item_id = item, item[pkey]
13
+ tr.pane-item.hoverable class=(:info if selected == item_id)
14
+ - pane_columns.each do |column|
15
+ - self.column = column
16
+ td
17
+ a.pane-column_value href=route(:edit, item_id, pager_params)
18
+ == render_pane_column column
19
+ - if @pager_context[:total_items] > __rear__.ipp
20
+ b.pull-right
21
+ = '%s - %s of %s' % @pager_context[:counter]
@@ -0,0 +1,5 @@
1
+ div *attrs(column, :pane)
2
+ - options.each_pair do |k,v|
3
+ - if active_options.include?(k)
4
+ = v
5
+ br