express_admin 1.4.8 → 1.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/ace/ace.js +18281 -0
- data/app/assets/javascripts/ace/mode-ruby.js +843 -0
- data/app/assets/javascripts/ace/theme-github.js +105 -0
- data/app/assets/javascripts/ace/worker-html.js +11527 -0
- data/app/assets/javascripts/express_admin/admin.js.coffee +36 -0
- data/app/assets/javascripts/express_admin.js +5 -0
- data/app/components/express_admin/nav_bar_actions.rb +2 -0
- data/app/components/express_admin/smart_form.rb +9 -1
- data/app/components/express_admin/smart_table.rb +36 -26
- data/lib/express_admin/engine.rb +2 -0
- data/lib/express_admin/standard_actions.rb +5 -5
- data/lib/express_admin/version.rb +1 -1
- data/test/dummy/test/components/definition_list_test.rb +13 -13
- data/test/dummy/test/components/icon_link_test.rb +52 -52
- data/test/dummy/test/components/icon_test.rb +2 -2
- data/test/dummy/test/components/mega_menu_test.rb +2 -2
- data/test/dummy/test/components/module_sidebar_test.rb +3 -3
- data/test/dummy/test/components/smart_table_test.rb +11 -12
- data/test/test_helper.rb +1 -1
- data/vendor/gems/express_templates/express_templates-0.9.8.gem +0 -0
- data/vendor/gems/express_templates/lib/express_templates/components/forms/select.rb +8 -4
- data/vendor/gems/express_templates/lib/express_templates/components/tree_for.rb +10 -1
- data/vendor/gems/express_templates/lib/express_templates/version.rb +1 -1
- data/vendor/gems/express_templates/test/dummy/log/test.log +321 -0
- metadata +23 -4
@@ -1,3 +1,37 @@
|
|
1
|
+
class AceInput
|
2
|
+
constructor: (editor)->
|
3
|
+
@editor = ace.edit(editor)
|
4
|
+
@editor.$blockScrolling = Infinity
|
5
|
+
@session = @editor.getSession()
|
6
|
+
@renderer = @editor.renderer
|
7
|
+
@textarea = $("##{$(editor).data('target')}")
|
8
|
+
@setOptions()
|
9
|
+
@updateMode()
|
10
|
+
@updateTheme()
|
11
|
+
@bindTextarea()
|
12
|
+
@editor.setFontSize "16px"
|
13
|
+
|
14
|
+
setOptions: =>
|
15
|
+
@renderer.setShowPrintMargin false
|
16
|
+
@renderer.setHScrollBarAlwaysVisible false
|
17
|
+
@session.setUseWorker false
|
18
|
+
@session.setTabSize 2
|
19
|
+
@session.setUseSoftTabs true
|
20
|
+
@session.setFoldStyle "markbeginend"
|
21
|
+
|
22
|
+
updateMode: =>
|
23
|
+
mode = require("ace/mode/ruby").Mode
|
24
|
+
@session.setMode new mode()
|
25
|
+
|
26
|
+
updateTheme: =>
|
27
|
+
@editor.setTheme require("ace/theme/github")
|
28
|
+
|
29
|
+
bindTextarea: =>
|
30
|
+
ace = @
|
31
|
+
ace.session.setValue ace.textarea.val()
|
32
|
+
ace.session.on "change", ->
|
33
|
+
ace.textarea.val ace.session.getValue()
|
34
|
+
|
1
35
|
$(document).ready ->
|
2
36
|
$('.select2').select2()
|
3
37
|
# Table Row as links
|
@@ -19,6 +53,8 @@ $(document).ready ->
|
|
19
53
|
e.preventDefault()
|
20
54
|
$('a.close-reveal-modal').trigger 'click'
|
21
55
|
return
|
56
|
+
$('.ace-input').each (index)->
|
57
|
+
editor = new AceInput(this)
|
22
58
|
|
23
59
|
String::repeat = (num) ->
|
24
60
|
new Array(num + 1).join this
|
@@ -1,6 +1,11 @@
|
|
1
1
|
//= require jquery
|
2
2
|
//= require jquery_ujs
|
3
|
+
//= require jquery-ui/sortable
|
3
4
|
//= require jquery.loadingdotdotdot
|
4
5
|
//= require tinymce-jquery
|
5
6
|
//= require select2
|
7
|
+
//= require ace/ace
|
8
|
+
//= require ace/worker-html
|
9
|
+
//= require ace/mode-ruby
|
10
|
+
//= require ace/theme-github
|
6
11
|
//= require_tree .
|
@@ -48,7 +48,15 @@ module ExpressAdmin
|
|
48
48
|
select(attrib.name.to_sym, options: config["#{relation}_collection".to_sym], select2: true)
|
49
49
|
else
|
50
50
|
if field_type == 'text_area'
|
51
|
-
|
51
|
+
if attrib.name == 'definition'
|
52
|
+
# TODO allow other fields aside from layout.definition
|
53
|
+
base_styles = "position: relative; height: 300px;"
|
54
|
+
target = [attributes[:class].to_a.last, attrib.name].join("_")
|
55
|
+
textarea attrib.name.to_sym, rows: 10, class: "hide", hidden: true
|
56
|
+
content_tag(:div, '', id: "ace_#{attrib.name}", class: "ace-input", style: base_styles, data: { target: target })
|
57
|
+
else
|
58
|
+
textarea attrib.name.to_sym, rows: 10
|
59
|
+
end
|
52
60
|
else
|
53
61
|
self.send((field_type_substitutions[field_type] || field_type), attrib.name.to_sym)
|
54
62
|
end
|
@@ -12,14 +12,24 @@ module ExpressAdmin
|
|
12
12
|
has_option :show_actions, 'Set to true if table has actions for each row'
|
13
13
|
has_option :row_class, 'Add a class to each table row'
|
14
14
|
|
15
|
-
|
16
15
|
column_defs = {}
|
17
16
|
column_defs[:array] = {description: "List of fields to include in the table as columns",
|
18
|
-
options: -> {resource.columns.map(&:name)} }
|
17
|
+
options: -> { resource.columns.map(&:name)} }
|
19
18
|
column_defs[:hash] = {description: "Hash of column names (titles) and how to calculate the cell values."}
|
20
19
|
|
21
20
|
has_option :columns, 'Specify the columns. May provide as a hash with values used to provide cell values as a proc.',
|
22
|
-
type: [:array, :hash]
|
21
|
+
type: [:array, :hash],
|
22
|
+
values: -> (*) {
|
23
|
+
options = resource_class.columns.map(&:name)
|
24
|
+
resource_class.columns.map(&:name).each do |name|
|
25
|
+
options << if name.match(/(\w+)_at$/)
|
26
|
+
"#{name}_in_words"
|
27
|
+
else
|
28
|
+
"#{name}_link"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
options = options + resource_class.instance_methods.grep(/_count$/).map(&:to_s)
|
32
|
+
}
|
23
33
|
|
24
34
|
contains -> {
|
25
35
|
thead {
|
@@ -100,29 +110,29 @@ module ExpressAdmin
|
|
100
110
|
end
|
101
111
|
|
102
112
|
def cell_value(item, accessor)
|
103
|
-
if accessor.respond_to?(:call)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
113
|
+
value = if accessor.respond_to?(:call)
|
114
|
+
begin
|
115
|
+
accessor.call(item).html_safe
|
116
|
+
rescue
|
117
|
+
'Error: '+$!.to_s
|
118
|
+
end
|
119
|
+
elsif attrib = accessor.to_s.match(/(\w+)_link$/).try(:[], 1)
|
120
|
+
# TODO: only works with non-namespaced routes
|
121
|
+
helpers.link_to item.send(attrib), resource_path(item)
|
122
|
+
elsif attrib = accessor.to_s.match(/(\w+)_in_words/).try(:[], 1)
|
123
|
+
item.send(attrib) ? (helpers.time_ago_in_words(item.send(attrib))+' ago') : 'never'
|
124
|
+
else
|
125
|
+
if relation_name = accessor.to_s.match(/(.*)_id$/).try(:[], 1)
|
126
|
+
reflection = resource_class.reflect_on_association(relation_name.to_sym)
|
127
|
+
end
|
128
|
+
|
129
|
+
if reflection
|
130
|
+
relation = item.send(relation_name)
|
131
|
+
relation.try(:name) || relation.to_s
|
132
|
+
else
|
133
|
+
item.send(accessor)
|
134
|
+
end
|
135
|
+
end
|
126
136
|
current_arbre_element.add_child value
|
127
137
|
end
|
128
138
|
|
data/lib/express_admin/engine.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'express_admin/menu'
|
2
2
|
require 'express_admin/version'
|
3
|
+
require 'express_admin/standard_actions'
|
3
4
|
require 'express_templates'
|
4
5
|
require 'jquery-rails'
|
6
|
+
require 'jquery-ui-rails'
|
5
7
|
require 'select2-rails'
|
6
8
|
require 'foundation_apps_styles'
|
7
9
|
require 'bourbon'
|
@@ -220,15 +220,15 @@ module ExpressAdmin
|
|
220
220
|
# TODO: optimize
|
221
221
|
parent_id = extract_path_info_from_routes["#{parent_name}_id".to_sym]
|
222
222
|
current_parent = "current_#{parent_name}".to_sym
|
223
|
-
unless self.
|
223
|
+
unless self.methods.include?(current_parent)
|
224
224
|
if previous_parent.nil?
|
225
225
|
self.class_eval do
|
226
226
|
define_method(current_parent) do
|
227
227
|
parent_class = parent_module_name.constantize
|
228
|
-
current_class_name = parent_name.
|
228
|
+
current_class_name = parent_name.camelize
|
229
229
|
current_class = parent_class.const_defined?(current_class_name) ?
|
230
230
|
parent_class.const_get(current_class_name) :
|
231
|
-
"::#{parent_name.
|
231
|
+
"::#{parent_name.camelize}".constantize
|
232
232
|
current_class.find(parent_id)
|
233
233
|
end
|
234
234
|
end
|
@@ -266,13 +266,13 @@ module ExpressAdmin
|
|
266
266
|
next unless engine_route
|
267
267
|
path_for_engine = request.path.gsub(%r(^#{engine_route.path.spec.to_s}), "")
|
268
268
|
begin
|
269
|
-
recognized_path = engine_instance.routes.recognize_path(path_for_engine
|
269
|
+
recognized_path = engine_instance.routes.recognize_path(path_for_engine)
|
270
270
|
rescue ActionController::RoutingError => e
|
271
271
|
end
|
272
272
|
end
|
273
273
|
if recognized_path.nil?
|
274
274
|
begin
|
275
|
-
recognized_path = Rails.application.routes.recognize_path(request.path
|
275
|
+
recognized_path = Rails.application.routes.recognize_path(request.path)
|
276
276
|
rescue ActionController::RoutingError => e
|
277
277
|
end
|
278
278
|
end
|
@@ -5,7 +5,7 @@ module ExpressAdmin
|
|
5
5
|
class DefinitionListTest < ActiveSupport::TestCase
|
6
6
|
|
7
7
|
def assigns
|
8
|
-
|
8
|
+
{list_types: list_types}
|
9
9
|
end
|
10
10
|
|
11
11
|
def helpers
|
@@ -13,21 +13,21 @@ module ExpressAdmin
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def list_types
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
16
|
+
@list_types ||= OpenStruct.new(
|
17
|
+
array: ["field1", "field2"],
|
18
|
+
hash: {term1: "def1",
|
19
|
+
term2: "def2",
|
20
|
+
term3: "def3"})
|
21
|
+
end
|
22
22
|
|
23
23
|
def deflist(*args)
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
arbre {
|
25
|
+
definition_list(:deflist, *args)
|
26
|
+
}.to_s
|
27
27
|
end
|
28
28
|
|
29
29
|
test "accepts array as input" do
|
30
|
-
|
30
|
+
assert deflist(list_types[:array])
|
31
31
|
end
|
32
32
|
|
33
33
|
test "accepts hash as input" do
|
@@ -64,8 +64,8 @@ HTML
|
|
64
64
|
|
65
65
|
test "definition_list renders correct markup with array input" do
|
66
66
|
assert_equal DEFLIST_MARKUP_ARR, deflist(list_types[:array])
|
67
|
-
end
|
67
|
+
end
|
68
68
|
|
69
69
|
end
|
70
70
|
|
71
|
-
end
|
71
|
+
end
|
@@ -4,74 +4,74 @@ module ExpressAdmin
|
|
4
4
|
|
5
5
|
class IconLinkTest < ActiveSupport::TestCase
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
7
|
+
def assigns
|
8
|
+
{resource: resource}
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource
|
12
|
+
@resource ||= OpenStruct.new(
|
13
|
+
text: 'Beer',
|
14
|
+
title: 'beer icon',
|
15
|
+
target: '_blank',
|
16
|
+
right: true,
|
17
|
+
delete: true,
|
18
|
+
confirm: true,
|
19
|
+
href: 'http://something.com'
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def helpers
|
24
24
|
mock_action_view(assigns)
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
def rendered_icon_link(*args)
|
28
|
+
arbre {
|
29
|
+
icon_link(:beer, *args)
|
30
|
+
}.to_s
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
test "renders" do
|
34
|
+
assert rendered_icon_link
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
test "icon link href default is set to #" do
|
38
|
+
assert_match /href="#"/, rendered_icon_link
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
test "icon-link target set to blank" do
|
42
|
+
# binding.pry
|
43
|
+
assert_match /target="_blank"/, rendered_icon_link(target: "#{resource[:target]}")
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
test "delete attribute is true" do
|
47
|
+
assert_match /data-delete="true"/, rendered_icon_link(delete: resource[:delete])
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
test "confirm attribute is true" do
|
51
|
+
assert_match /data-confirm="true"/, rendered_icon_link(confirm: resource[:confirm])
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
test "icon link has title set" do
|
55
|
+
assert_match /title="beer icon"/, rendered_icon_link(title: "#{resource[:title]}")
|
56
|
+
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
test "icon link has accompanying text" do
|
59
|
+
assert_match /i>\nBeer<\/a>/, rendered_icon_link(text: "#{resource[:text]}")
|
60
|
+
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
test "icon link has link set" do
|
63
|
+
assert_match /href="#{resource[:href]}"/, rendered_icon_link(href: "#{resource[:href]}")
|
64
|
+
end
|
65
65
|
|
66
|
-
|
66
|
+
MARKUP_RIGHT = <<-HTML
|
67
67
|
<a class="icon-link" href="#">
|
68
68
|
Beer <i class="icon ion-beer"></i>
|
69
69
|
</a>
|
70
70
|
HTML
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
test "if icon-link is set to right" do
|
73
|
+
assert_equal MARKUP_RIGHT, rendered_icon_link(text: "#{resource[:text]}", right: "#{resource[:right]}")
|
74
|
+
end
|
75
75
|
|
76
76
|
end
|
77
|
-
end
|
77
|
+
end
|
@@ -29,12 +29,12 @@ module ExpressAdmin
|
|
29
29
|
|
30
30
|
test "renders" do
|
31
31
|
assert rendered_mega_menu
|
32
|
-
end
|
32
|
+
end
|
33
33
|
|
34
34
|
test "links menu to eval'd path" do
|
35
35
|
assert_match /href="evaled_path"/, rendered_mega_menu
|
36
36
|
assert_match /href="some_path"/, rendered_mega_menu
|
37
|
-
end
|
37
|
+
end
|
38
38
|
|
39
39
|
test "replaces whitespace in menu title to underscore for icon class" do
|
40
40
|
assert_match /icon-express_big_menu/, rendered_mega_menu
|
@@ -3,7 +3,7 @@ require 'test_helper'
|
|
3
3
|
module Components
|
4
4
|
|
5
5
|
class ModuleSidebarTest < ActiveSupport::TestCase
|
6
|
-
|
6
|
+
|
7
7
|
class MenuItem
|
8
8
|
attr_accessor :title, :path, :items
|
9
9
|
|
@@ -21,7 +21,7 @@ module Components
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def assigns
|
24
|
-
{ current_menu: current_menu,
|
24
|
+
{ current_menu: current_menu,
|
25
25
|
current_menu_name: current_menu_name,
|
26
26
|
foo_path: 'foo',
|
27
27
|
bar_path: 'bar',
|
@@ -53,4 +53,4 @@ module Components
|
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
56
|
-
end
|
56
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
4
|
class SmartTableTest < ActiveSupport::TestCase
|
6
5
|
|
7
6
|
fixtures :widgets, :categories
|
@@ -24,17 +23,17 @@ module Components
|
|
24
23
|
}
|
25
24
|
end
|
26
25
|
|
27
|
-
test
|
26
|
+
test 'should have hidden columns' do
|
28
27
|
assert_match 'more-columns-indicator', compiled_widget_table
|
29
28
|
assert_no_match 'column7', compiled_widget_table
|
30
29
|
end
|
31
30
|
|
32
|
-
test
|
31
|
+
test 'iterates over collection setting table row id correctly' do
|
33
32
|
assert_match 'widget:298486374', compiled_widget_table
|
34
33
|
assert_match 'widget:980190962', compiled_widget_table
|
35
34
|
end
|
36
35
|
|
37
|
-
test
|
36
|
+
test 'renders cell contents' do
|
38
37
|
assert_match '<td class="column2">Hammer</td>', compiled_widget_table
|
39
38
|
end
|
40
39
|
|
@@ -43,24 +42,24 @@ module Components
|
|
43
42
|
# assert_match /\{\{\(widget.column3\).to_s.truncate\(27\)\}\}/, compiled_widget_table
|
44
43
|
# end
|
45
44
|
|
46
|
-
test
|
45
|
+
test 'table cell shows related item name or display name' do
|
47
46
|
# note this will move to a helper that will intelligently look
|
48
47
|
# for decorated methods such as name or display_name
|
49
48
|
assert_match '<td class="category_id">Toys</td>', compiled_widget_table
|
50
49
|
assert_match '<td class="category_id">Tools</td>', compiled_widget_table
|
51
50
|
end
|
52
51
|
|
53
|
-
test
|
52
|
+
test 'table displays only columns specified if columns option provided' do
|
54
53
|
compiled = compiled_widget_table(columns: [:column3, :column4])
|
55
54
|
assert_match 'column3', compiled
|
56
|
-
assert_match '
|
55
|
+
assert_match 'column4', compiled
|
57
56
|
refute_match 'column2', compiled
|
58
57
|
refute_match 'category_id', compiled
|
59
58
|
refute_match 'column5', compiled
|
60
59
|
refute_match 'column6', compiled
|
61
60
|
end
|
62
61
|
|
63
|
-
test
|
62
|
+
test 'table column titles may be customized' do
|
64
63
|
compiled = compiled_widget_table(columns: {"Column3 is the Best" => :column3})
|
65
64
|
assert_match /<th class="column3">Column3 is the Best/, compiled
|
66
65
|
end
|
@@ -78,15 +77,15 @@ module Components
|
|
78
77
|
assert_match 'Error', compiled_widget_table_with_proc_column
|
79
78
|
end
|
80
79
|
|
81
|
-
test
|
80
|
+
test 'table cell contains result of proc.call if no exception is raised' do
|
82
81
|
assert_match 'LEGO', compiled_widget_table_with_proc_column
|
83
82
|
end
|
84
83
|
|
85
|
-
test
|
84
|
+
test 'table cell class is valid when proc is used' do
|
86
85
|
assert_match 'class="this_column_will_error"', compiled_widget_table_with_proc_column
|
87
86
|
end
|
88
87
|
|
89
|
-
test
|
88
|
+
test 'attribute accessor appended with _link generates a link' do
|
90
89
|
fragment = arbre {
|
91
90
|
smart_table(:widgets, columns: {
|
92
91
|
'A link column' => :column3_link
|
@@ -95,7 +94,7 @@ module Components
|
|
95
94
|
assert_match /column3.*href="\/widgets\/(\d+)/, fragment
|
96
95
|
end
|
97
96
|
|
98
|
-
test
|
97
|
+
test 'timestamp accessor appeneded with _in_words generates code that uses time_ago_in_words' do
|
99
98
|
fragment = arbre {
|
100
99
|
smart_table(:widgets, columns: {
|
101
100
|
'Created' => :created_at_in_words
|
data/test/test_helper.rb
CHANGED
Binary file
|
@@ -60,10 +60,14 @@ module ExpressTemplates
|
|
60
60
|
|
61
61
|
def options_from_supplied_or_field_values
|
62
62
|
if select_options_supplied?
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
supplied_options = use_supplied_options
|
64
|
+
if supplied_options.respond_to?(:map)
|
65
|
+
helpers.options_for_select(
|
66
|
+
normalize_for_helper(supplied_options),
|
67
|
+
selected_value)
|
68
|
+
else
|
69
|
+
supplied_options
|
70
|
+
end
|
67
71
|
else
|
68
72
|
generate_options_from_field_values
|
69
73
|
end
|
@@ -42,16 +42,25 @@ module ExpressTemplates
|
|
42
42
|
tag :ul
|
43
43
|
|
44
44
|
has_attributes :class => 'tree'
|
45
|
+
has_option :root, "Root of the tree. Defaults to collection with the same as the id.", type: :proc
|
45
46
|
|
46
47
|
contains -> (&customize_block) {
|
47
48
|
@customize_block = customize_block
|
48
|
-
list_items(
|
49
|
+
list_items(root_node)
|
49
50
|
}
|
50
51
|
|
51
52
|
before_build -> {
|
52
53
|
add_class config[:id]
|
53
54
|
}
|
54
55
|
|
56
|
+
def root_node
|
57
|
+
if config[:root] && config[:root].respond_to?(:call)
|
58
|
+
config[:root].call
|
59
|
+
else
|
60
|
+
send(config[:id])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
55
64
|
def list_items(nodes)
|
56
65
|
nodes.each do |node|
|
57
66
|
list_item(node)
|