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,81 @@
1
+
2
+ module RearTest__CRUD
3
+ class App < E
4
+ include Rear
5
+ model Book
6
+ end
7
+
8
+ Spec.new App do
9
+ orm = RearORM.new(app.model, app.pkey)
10
+
11
+ Should 'create new item' do
12
+ name, about = random_string, random_string
13
+ post :crud, name: name, about: about
14
+ is(last_response).ok?
15
+ id = last_response.body.to_i
16
+
17
+ item = orm[id]
18
+ check(item).instance_of?(app.model)
19
+ expect(item.id) == id
20
+ expect(item.name.to_s) == name
21
+ expect(item.about.to_s) == about
22
+
23
+ Then 'update it' do
24
+ new_name = random_string
25
+ put :crud, id, name: new_name
26
+ is(last_response).ok?
27
+
28
+ item.reload
29
+ refute(item.name.to_s) == name
30
+ check(item.name.to_s) == new_name
31
+ end
32
+
33
+ And 'finally delete it' do
34
+ delete :crud, id
35
+ is(last_response).ok?
36
+ item = app[id]
37
+ is(item).nil?
38
+ end
39
+ end
40
+
41
+ Testing 'multiple delete' do
42
+
43
+ ids = (1..10).inject([]) {|f,c| post(:crud); f << last_response.body.to_i}
44
+ expect(ids.size) == 10
45
+ items = orm.filter(conditions: {app.pkey => ids})
46
+ expect(items.size) == ids.size
47
+
48
+ delete :delete_selected, items: ids.join(' ')
49
+ is(last_response).ok?
50
+ items = orm.filter(conditions: {app.pkey => ids})
51
+ expect(items.size) == 0
52
+ end
53
+
54
+ end
55
+
56
+ class ReadOnlyBook < E
57
+ include Rear
58
+ model Book
59
+ readonly!
60
+ end
61
+ Spec.new ReadOnlyBook do
62
+ book, book_id = new_book
63
+
64
+ Should 'Prohibit items creation' do
65
+ name, about = random_string, random_string
66
+ post :crud, name: name, about: about
67
+ is(last_response).readonly_error?
68
+ end
69
+
70
+ Should 'prohibit updates' do
71
+ put :crud, book_id, name: random_string
72
+ is(last_response).readonly_error?
73
+ end
74
+
75
+ Should 'prohibit deletion' do
76
+ delete :crud, book_id
77
+ is(last_response).readonly_error?
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,147 @@
1
+
2
+ module RearTest__CustomTemplates
3
+
4
+ class SharedTemplates < E
5
+ include Rear
6
+ model Book
7
+ rear_templates 'templates/shared'
8
+ ipp 1000
9
+ end
10
+ Spec.new self do
11
+ app E.new {
12
+ mount SharedTemplates
13
+ root File.expand_path('..', __FILE__)
14
+ }
15
+ map SharedTemplates.base_url
16
+
17
+ item, item_id = new_book
18
+
19
+ Testing :pane do
20
+ get
21
+ is(last_response).ok?
22
+ expect( extract_elements ).any? {|e|
23
+ e.children.any? {|c|
24
+ c.to_s == '<p class="custom-shared-template">%s</p>' % item[:name]
25
+ }
26
+ }
27
+ end
28
+
29
+ Testing :editor do
30
+ get :edit, item_id
31
+ is(last_response).ok?
32
+ expect( extract_elements ).any? { |e|
33
+ e.children.any? {|c|
34
+ c.to_s == '<input class="custom-shared-template" value="%s">' % item[:name]
35
+ }
36
+ }
37
+ end
38
+ end
39
+
40
+ class AdhocDefaultTemplate < E
41
+ include Rear
42
+ model Book
43
+ rear_templates 'templates/adhoc'
44
+ ipp 1000
45
+ end
46
+ Spec.new self do
47
+ app E.new {
48
+ mount AdhocDefaultTemplate
49
+ root File.expand_path('..', __FILE__)
50
+ }
51
+ map AdhocDefaultTemplate.base_url
52
+
53
+ item, item_id = new_book
54
+
55
+ Testing :pane do
56
+ get
57
+ is(last_response).ok?
58
+ expect( extract_elements ).any? {|e|
59
+ e.children.any? {|c|
60
+ c.to_s == '<p class="adhoc-default-template">%s</p>' % item[:name]
61
+ }
62
+ }
63
+ end
64
+
65
+ Testing :editor do
66
+ get :edit, item_id
67
+ is(last_response).ok?
68
+ expect( extract_elements ).any? { |e|
69
+ e.children.any? {|c|
70
+ c.to_s == '<input class="adhoc-default-template" value="%s">' % item[:name]
71
+ }
72
+ }
73
+ end
74
+ end
75
+
76
+ class AdhocGivenTemplate < E
77
+ include Rear
78
+ model Book
79
+ rear_templates 'templates/adhoc'
80
+ input :name do
81
+ editor_template :name
82
+ pane_template :name
83
+ end
84
+ ipp 1000
85
+ end
86
+ Spec.new self do
87
+ app E.new {
88
+ mount AdhocGivenTemplate
89
+ root File.expand_path('..', __FILE__)
90
+ }
91
+ map AdhocGivenTemplate.base_url
92
+
93
+ item, item_id = new_book
94
+
95
+ Testing :pane do
96
+ get
97
+ is(last_response).ok?
98
+ expect( extract_elements ).any? {|e|
99
+ e.children.any? {|c|
100
+ c.to_s == '<p class="adhoc-given-template">%s</p>' % item[:name]
101
+ }
102
+ }
103
+ end
104
+
105
+ Testing :editor do
106
+ get :edit, item_id
107
+ is(last_response).ok?
108
+ expect( extract_elements ).any? { |e|
109
+ e.children.any? {|c|
110
+ c.to_s == '<input class="adhoc-given-template" value="%s">' % item[:name]
111
+ }
112
+ }
113
+ end
114
+ end
115
+
116
+
117
+ class ProcTemplate < E
118
+ include Rear
119
+ model Book
120
+ input :name do
121
+ pane_template { item.name }
122
+ editor_template { item.about }
123
+ end
124
+ end
125
+ Spec.new ProcTemplate do
126
+
127
+ item, item_id = new_book()
128
+
129
+ Testing :pane do
130
+ get
131
+ is(last_response).ok?
132
+ expect( extract_elements ).any? {|e|
133
+ e.children.any? {|c| c.to_s == item[:name]}
134
+ }
135
+ end
136
+
137
+ Testing :editor do
138
+ get :edit, item_id
139
+ is(last_response).ok?
140
+ expect( extract_elements ).any? do |e|
141
+ e.children.any? {|c|
142
+ c.to_s == item[:about]
143
+ }
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,228 @@
1
+
2
+ module RearTest__Filters
3
+ NAMES = %w[
4
+ Arthur Aaron Adler
5
+ Daniel Dante David
6
+ Edward Eliot Elvin
7
+ ]
8
+
9
+ class BasicQuickFilterApp < E
10
+ include Rear
11
+ model Book
12
+
13
+ quick_filter :cover, :Soft, :Solid
14
+ quick_filter :colors, 'r' => 'Red Color', 'g' => :Green
15
+ end
16
+ Spec.new BasicQuickFilterApp do
17
+
18
+ Testing 'filters provided as arguments' do
19
+ %w[Soft Solid].map do |cover|
20
+ 1.upto(rand(10)+1) { app.model.create(:cover => cover) }
21
+ end
22
+
23
+ cover = 'Soft'
24
+ items = count_books(:cover => cover)
25
+ expect(items) > 0
26
+ get quick_filters: {cover: cover}
27
+ is(last_response).ok?
28
+ expect(items) == extract_elements('.pane-item').size
29
+
30
+ cover = 'Solid'
31
+ items = count_books(cover: cover)
32
+ expect(items) > 0
33
+ get quick_filters: {cover: cover}
34
+ is(last_response).ok?
35
+ expect(items) == extract_elements('.pane-item').size
36
+ end
37
+
38
+ Testing 'filters provided as Hash' do
39
+ %w[r g].map do |color|
40
+ 1.upto(rand(10)+1) { app.model.create(:colors => color) }
41
+ end
42
+
43
+ items = count_books( :colors => 'r')
44
+ expect(items) > 0
45
+ get quick_filters: {colors: 'Red Color'}
46
+ is(last_response).ok?
47
+ expect(items) == extract_elements('.pane-item').size
48
+
49
+ items = count_books( :colors => 'g')
50
+ expect(items) > 0
51
+ get quick_filters: {colors: 'Green'}
52
+ is(last_response).ok?
53
+ expect(items) == extract_elements('.pane-item').size
54
+ end
55
+
56
+ end
57
+
58
+
59
+ class AdvancedQuickFilterApp < E
60
+ include Rear
61
+ model Book
62
+
63
+ quick_filter :name, cmp: :like_, 'A' => :StartingwithA, 'Z' => 'Starting with Z'
64
+ quick_filter :colors, 'r' => :Red, [:like_, :g] => :GreenFirst
65
+ end
66
+
67
+ Spec.new AdvancedQuickFilterApp do
68
+ query_map = RearConstants::FILTERS__QUERY_MAP.call(app.orm)
69
+
70
+ Testing 'cmp passed as global option' do
71
+ %w[A Z].map do |letter|
72
+ 1.upto(rand(10)+1) { app.model.create(:name => letter + random_string) }
73
+ end
74
+
75
+ query, value = query_map[:like_]
76
+ items = count_books( [query % :name, value % "A"])
77
+ expect(items) > 0
78
+ get quick_filters: {name: 'StartingwithA'}
79
+ is(last_response).ok?
80
+ expect(items) == extract_elements('.pane-item').size
81
+
82
+ items = count_books([query % :name, value % "Z"])
83
+ expect(items) > 0
84
+ get quick_filters: {name: 'Starting with Z'}
85
+ is(last_response).ok?
86
+ expect(items) == extract_elements('.pane-item').size
87
+ end
88
+
89
+ Testing 'cmp passed per filter' do
90
+ a = %w[
91
+ r
92
+ r,g
93
+ g
94
+ g,r
95
+ ]
96
+ 1.upto(50) do
97
+ app.model.create(:colors => a[rand(a.size)])
98
+ end
99
+
100
+ items = count_books( :colors => 'r')
101
+ expect(items) > 0
102
+ get quick_filters: {colors: 'Red'}
103
+ is(last_response).ok?
104
+ expect(items) == extract_elements('.pane-item').size
105
+
106
+ query, value = query_map[:like_]
107
+ items = count_books( [query % :colors, value % "g"])
108
+ expect(items) > 0
109
+ get quick_filters: {colors: 'GreenFirst'}
110
+ is(last_response).ok?
111
+ expect(items) == extract_elements('.pane-item').size
112
+ end
113
+ end
114
+
115
+ class LikeApp < E
116
+ include Rear
117
+ model Book
118
+ filter :name
119
+ filter :about, cmp: :unlike
120
+ end
121
+
122
+ Spec.new LikeApp do
123
+
124
+ query_map = RearConstants::FILTERS__QUERY_MAP.call(app.orm)
125
+
126
+ (items = %w[abc123 abc456 abc789 foo bar]).each do |val|
127
+ app.model.create :name => val, :about => val
128
+ end
129
+
130
+ Ensure 'all items displayed without filters' do
131
+ get
132
+ is(last_response).ok?
133
+ elements = extract_elements
134
+ items.each do |name|
135
+ expect(elements).any? {|c| c.text == name}
136
+ end
137
+ end
138
+
139
+ Testing :like do
140
+ query, value = query_map[:like]
141
+ items = count_books [query % :name, value % 'abc']
142
+ expect(items) > 0
143
+ get filters: {name: {like: 'abc'}}
144
+ is(last_response).ok?
145
+ elements = extract_elements('.pane-item')
146
+ expect(elements.size) == items
147
+ end
148
+
149
+ Testing :unlike do
150
+ query, value = query_map[:unlike]
151
+ items = count_books [query % :about, value % 'abc']
152
+ get filters: {about: {unlike: 'abc'}}
153
+ is(last_response).ok?
154
+ elements = extract_elements('.pane-item')
155
+ expect(elements.size) == items
156
+ end
157
+ end
158
+
159
+ class EqlApp < E
160
+ include Rear
161
+ model Book
162
+ filter :name, cmp: :eql
163
+ end
164
+
165
+ Spec.new EqlApp do
166
+
167
+ 1.upto(10) { app.model.create name: random_string }
168
+ item, item_id = new_book()
169
+
170
+ get
171
+ is(last_response).ok?
172
+ expect(extract_elements('.pane-item').size) > 1
173
+
174
+ get filters: {name: {eql: item.name}}
175
+ is(last_response).ok?
176
+ expect(extract_elements('.pane-item').size) == 1
177
+
178
+ end
179
+
180
+ class DecorativeFiltersApp < E
181
+ include Rear
182
+ model Book
183
+
184
+ decorative_filter :letter, :select do
185
+ ('A'..'Z').to_a
186
+ end
187
+ filter :name, :select, cmp: :like do
188
+ filter?(:letter) ?
189
+ NAMES.select {|n| n =~ /\A#{filter(:letter)}/} : {}
190
+ end
191
+ end
192
+
193
+ Spec.new DecorativeFiltersApp do
194
+
195
+ selector = '.selectable[name="filters[name][like]"]'
196
+
197
+ get :html_filters
198
+ filters = extract_elements(selector)
199
+ expect(filters.size) == 1
200
+ is(filters.first.children.select {|c| c.attr(:value).to_s.size > 0}).empty?
201
+
202
+ %w[A D E].each do |letter|
203
+ get :html_filters, filters: {letter: {decorative: letter}}
204
+ is(last_response).ok?
205
+ filters = extract_elements(selector)
206
+ expect(filters.size) == 1
207
+ children = filters.first.children.select {|c| c.attr(:value).to_s.size > 0}
208
+ check(children.map {|c| c.text }.sort) ==
209
+ NAMES.select {|n| n =~ /\A#{letter}/}.sort
210
+ end
211
+ end
212
+
213
+ class InheritedType < E
214
+ include Rear
215
+ model Book
216
+ filter :created_at
217
+ end
218
+
219
+ Spec.new InheritedType do
220
+ get
221
+ is(last_response).ok?
222
+ elements = extract_elements('.search-query')
223
+ expect(elements.select {|e|
224
+ e.attr(:name) == 'filters[created_at][eql]'
225
+ }.size) == 1
226
+ end
227
+
228
+ end