tolaria 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/README.md +8 -3
- data/Rakefile +1 -0
- data/app/assets/fonts/admin/fontawesome.woff +0 -0
- data/app/assets/fonts/admin/fontawesome.woff2 +0 -0
- data/app/assets/stylesheets/admin/components/_navigation.scss +8 -13
- data/app/assets/stylesheets/admin/components/_resource_form.scss +16 -5
- data/app/assets/stylesheets/admin/settings/_fonts.scss +2 -8
- data/app/assets/stylesheets/admin/settings/_icons.scss +123 -0
- data/app/controllers/admin/admin_controller.rb +1 -1
- data/app/controllers/admin/sessions_controller.rb +1 -1
- data/app/controllers/tolaria/resource_controller.rb +42 -19
- data/app/controllers/tolaria/tolaria_controller.rb +2 -2
- data/app/helpers/admin/table_helper.rb +7 -7
- data/app/helpers/admin/view_helper.rb +6 -9
- data/app/views/admin/tolaria_resource/_form_buttons.html.erb +1 -3
- data/app/views/admin/tolaria_resource/_index_table.html.erb +1 -1
- data/app/views/admin/tolaria_resource/edit.html.erb +12 -3
- data/app/views/admin/tolaria_resource/index.html.erb +1 -4
- data/app/views/admin/tolaria_resource/show.html.erb +1 -1
- data/lib/tolaria.rb +1 -1
- data/lib/tolaria/engine.rb +0 -1
- data/lib/tolaria/ransack.rb +43 -0
- data/lib/tolaria/version.rb +1 -1
- data/test/demo/app/models/application_record.rb +3 -0
- data/test/demo/app/models/blog_post.rb +1 -1
- data/test/demo/app/models/footnote.rb +1 -1
- data/test/demo/app/models/image.rb +1 -1
- data/test/demo/app/models/legal_page.rb +1 -1
- data/test/demo/app/models/miscellany.rb +6 -2
- data/test/demo/app/models/topic.rb +1 -1
- data/test/demo/app/models/video.rb +1 -1
- data/test/demo/app/views/admin/legal_pages/_form.html.erb +1 -1
- data/test/demo/app/views/admin/miscellany/_form.html.erb +3 -0
- data/test/demo/config/application.rb +0 -4
- data/test/demo/config/environments/test.rb +2 -2
- data/test/demo/db/schema.rb +8 -15
- data/test/integration/filter_preservation_test.rb +1 -2
- data/test/integration/forbidden_routes_test.rb +28 -2
- data/test/integration/pagination_test.rb +76 -0
- data/test/integration/session_test.rb +1 -2
- data/tolaria.gemspec +5 -5
- metadata +34 -31
- data/app/assets/fonts/admin/fontawesome.eot +0 -0
- data/app/assets/fonts/admin/fontawesome.svg +0 -655
- data/app/assets/fonts/admin/fontawesome.ttf +0 -0
- data/test/demo/app/models/.keep +0 -0
@@ -38,11 +38,6 @@ module Admin::ViewHelper
|
|
38
38
|
lookup_context.template_exists?("admin/#{template_path}", [], true)
|
39
39
|
end
|
40
40
|
|
41
|
-
# True if Ransack filtering parameters are present
|
42
|
-
def currently_filtering?
|
43
|
-
params[:q].present? && params[:q].is_a?(Hash) && params[:q].keys.reject{|key| key == "s"}.any?
|
44
|
-
end
|
45
|
-
|
46
41
|
# Attempt to automatically construct a default text search
|
47
42
|
# field name for Ransack from a given model's table settings
|
48
43
|
def ransack_text_search_chain(model)
|
@@ -58,12 +53,14 @@ module Admin::ViewHelper
|
|
58
53
|
return %{Are you sure you want to delete the #{resource.model_name.human.downcase} “#{Tolaria.display_name(resource)}”? This action is not reversible.}
|
59
54
|
end
|
60
55
|
|
56
|
+
# Returns the correct value to pass to the `url:` of `form_for`,
|
57
|
+
# based on the current controller.action_name
|
61
58
|
def contextual_form_url
|
62
59
|
case controller.action_name
|
63
|
-
when "edit"
|
64
|
-
url_for(action:"
|
65
|
-
when "new"
|
66
|
-
url_for(action:"
|
60
|
+
when "edit"
|
61
|
+
url_for(action:"update", id:@resource.id)
|
62
|
+
when "new"
|
63
|
+
url_for(action:"create")
|
67
64
|
else
|
68
65
|
nil
|
69
66
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% if @managed_class.allows? :index %>
|
2
|
-
<%= link_to url_for(action:"index", q:
|
2
|
+
<%= link_to url_for(action:"index", q:ransack_params, p:page_param), class:"button -cancel" do %>
|
3
3
|
<%= fontawesome_icon :close %>
|
4
4
|
Cancel
|
5
5
|
<% end %>
|
@@ -17,5 +17,3 @@
|
|
17
17
|
<%= fontawesome_icon :check %>
|
18
18
|
<%= @resource.persisted?? "Save" : "Create" %>
|
19
19
|
<% end %>
|
20
|
-
|
21
|
-
|
@@ -6,19 +6,28 @@
|
|
6
6
|
|
7
7
|
<%= form_for [:admin, @resource], url:contextual_form_url, builder:Admin::FormBuilder, html:{class:"resource-form"} do |form_builder| %>
|
8
8
|
|
9
|
-
<% if
|
10
|
-
|
9
|
+
<% if page_param.present? %>
|
10
|
+
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if ransack_params.present? %>
|
14
|
+
<% ransack_params.each do |key, value| %>
|
15
|
+
<% next if value.respond_to?(:keys) %>
|
11
16
|
<%= hidden_field_tag "q[#{key}]", value %>
|
12
17
|
<% end %>
|
13
18
|
<% end %>
|
14
19
|
|
20
|
+
<% if page_param.present? %>
|
21
|
+
<%= hidden_field_tag :p, page_param %>
|
22
|
+
<% end %>
|
23
|
+
|
15
24
|
<div class="main-controls">
|
16
25
|
<div class="main-controls-left">
|
17
26
|
<h1>
|
18
27
|
<span class="crumb">
|
19
28
|
<%= fontawesome_icon @managed_class.icon %>
|
20
29
|
<% if @managed_class.allows? :index %>
|
21
|
-
<%= link_to @managed_class.model_name.human.pluralize.titleize, url_for(action:"index", controller:@managed_class.plural, q:
|
30
|
+
<%= link_to @managed_class.model_name.human.pluralize.titleize, url_for(action:"index", controller:@managed_class.plural, q:ransack_params, p:page_param) %>
|
22
31
|
<% else %>
|
23
32
|
<%= @managed_class.model_name.human.pluralize.titleize %>
|
24
33
|
<% end %>
|
@@ -26,7 +26,7 @@
|
|
26
26
|
<% end %>
|
27
27
|
|
28
28
|
<% if @managed_class.allows?(:new) %>
|
29
|
-
<%= link_to url_for(action:"new", q:
|
29
|
+
<%= link_to url_for(action:"new", q:ransack_params, p:page_param), class:"button -primary" do %>
|
30
30
|
<%= fontawesome_icon :plus %>
|
31
31
|
New <%= @managed_class.model_name.human.titleize %>
|
32
32
|
<% end %>
|
@@ -38,6 +38,3 @@
|
|
38
38
|
|
39
39
|
<%= render "admin/tolaria_resource/search_form" %>
|
40
40
|
<%= render "admin/tolaria_resource/index_table" %>
|
41
|
-
|
42
|
-
|
43
|
-
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<span class="crumb">
|
8
8
|
<%= fontawesome_icon @managed_class.icon %>
|
9
9
|
<% if @managed_class.allows? :index %>
|
10
|
-
<%= link_to @managed_class.model_name.human.pluralize.titleize, url_for(action:"index", controller:@managed_class.plural, q:
|
10
|
+
<%= link_to @managed_class.model_name.human.pluralize.titleize, url_for(action:"index", controller:@managed_class.plural, q:ransack_params, p:page_param) %>
|
11
11
|
<% else %>
|
12
12
|
<%= @managed_class.model_name.human.pluralize.titleize %>
|
13
13
|
<% end %>
|
data/lib/tolaria.rb
CHANGED
@@ -9,12 +9,12 @@ require "kaminari"
|
|
9
9
|
require "ransack"
|
10
10
|
|
11
11
|
require "tolaria/version"
|
12
|
+
require "tolaria/ransack"
|
12
13
|
require "tolaria/engine"
|
13
14
|
require "tolaria/config"
|
14
15
|
require "tolaria/default_config"
|
15
16
|
require "tolaria/random_tokens"
|
16
17
|
require "tolaria/admin"
|
17
|
-
|
18
18
|
require "tolaria/reload"
|
19
19
|
require "tolaria/managed_class"
|
20
20
|
require "tolaria/manage"
|
data/lib/tolaria/engine.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Until Ransack releases a new gem version, we have to patch in this
|
2
|
+
# Rails 5 support from their master branch
|
3
|
+
|
4
|
+
module Ransack
|
5
|
+
module Helpers
|
6
|
+
module FormHelper
|
7
|
+
|
8
|
+
def sort_link(search_object, attribute, *args, &block)
|
9
|
+
search, routing_proxy = extract_search_and_routing_proxy(search_object)
|
10
|
+
unless Search === search
|
11
|
+
raise TypeError, 'First argument must be a Ransack::Search!'
|
12
|
+
end
|
13
|
+
args.unshift(capture(&block)) if block_given?
|
14
|
+
s = SortLink.new(search, attribute, args, params, &block)
|
15
|
+
link_to(s.name, url(routing_proxy, s.url_options), s.html_options(args))
|
16
|
+
end
|
17
|
+
|
18
|
+
class SortLink
|
19
|
+
|
20
|
+
def initialize(search, attribute, args, params)
|
21
|
+
@search = search
|
22
|
+
@params = parameters_hash(params)
|
23
|
+
@field = attribute.to_s
|
24
|
+
@sort_fields = extract_sort_fields_and_mutate_args!(args).compact
|
25
|
+
@current_dir = existing_sort_direction
|
26
|
+
@label_text = extract_label_and_mutate_args!(args)
|
27
|
+
@options = extract_options_and_mutate_args!(args)
|
28
|
+
@hide_indicator = @options.delete(:hide_indicator) || Ransack.options[:hide_sort_order_indicators]
|
29
|
+
@default_order = @options.delete :default_order
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parameters_hash(params)
|
35
|
+
return params unless params.respond_to?(:to_unsafe_h)
|
36
|
+
params.to_unsafe_h
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/tolaria/version.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
class Miscellany <
|
1
|
+
class Miscellany < ApplicationRecord
|
2
2
|
|
3
3
|
manage_with_tolaria using:{
|
4
4
|
icon: "cogs",
|
5
5
|
category: "Settings",
|
6
|
-
allowed_actions: [:index, :
|
6
|
+
allowed_actions: [:index, :edit, :update],
|
7
7
|
permit_params: [
|
8
8
|
:value
|
9
9
|
],
|
10
10
|
}
|
11
11
|
|
12
|
+
def to_s
|
13
|
+
key
|
14
|
+
end
|
15
|
+
|
12
16
|
end
|
@@ -18,9 +18,5 @@ module Demo
|
|
18
18
|
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
19
19
|
# config.i18n.default_locale = :de
|
20
20
|
|
21
|
-
# Do not swallow errors in after_commit/after_rollback callbacks.
|
22
|
-
config.active_record.raise_in_transactional_callbacks = true
|
23
|
-
|
24
21
|
end
|
25
22
|
end
|
26
|
-
|
@@ -14,8 +14,8 @@ Rails.application.configure do
|
|
14
14
|
config.eager_load = false
|
15
15
|
|
16
16
|
# Configure static file server for tests with Cache-Control for performance.
|
17
|
-
config.
|
18
|
-
config.
|
17
|
+
config.public_file_server.enabled = true
|
18
|
+
config.public_file_server.headers = {"Cache-Control" => "public, max-age=3600"}
|
19
19
|
|
20
20
|
# Show full error reports and disable caching.
|
21
21
|
config.consider_all_requests_local = true
|
data/test/demo/db/schema.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# This file is auto-generated from the current state of the database. Instead
|
3
2
|
# of editing this file, please use the migrations feature of Active Record to
|
4
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
@@ -26,19 +25,17 @@ ActiveRecord::Schema.define(version: 20150610135235) do
|
|
26
25
|
t.integer "lockout_strikes", default: 0, null: false
|
27
26
|
t.integer "total_strikes", default: 0, null: false
|
28
27
|
t.integer "sessions_created", default: 0, null: false
|
28
|
+
t.index ["auth_token"], name: "index_administrators_on_auth_token"
|
29
|
+
t.index ["email"], name: "index_administrators_on_email"
|
29
30
|
end
|
30
31
|
|
31
|
-
add_index "administrators", ["auth_token"], name: "index_administrators_on_auth_token"
|
32
|
-
add_index "administrators", ["email"], name: "index_administrators_on_email"
|
33
|
-
|
34
32
|
create_table "blog_post_topics", force: :cascade do |t|
|
35
33
|
t.integer "blog_post_id", null: false
|
36
34
|
t.integer "topic_id", null: false
|
35
|
+
t.index ["blog_post_id"], name: "index_blog_post_topics_on_blog_post_id"
|
36
|
+
t.index ["topic_id"], name: "index_blog_post_topics_on_topic_id"
|
37
37
|
end
|
38
38
|
|
39
|
-
add_index "blog_post_topics", ["blog_post_id"], name: "index_blog_post_topics_on_blog_post_id"
|
40
|
-
add_index "blog_post_topics", ["topic_id"], name: "index_blog_post_topics_on_topic_id"
|
41
|
-
|
42
39
|
create_table "blog_posts", force: :cascade do |t|
|
43
40
|
t.datetime "created_at", null: false
|
44
41
|
t.datetime "updated_at", null: false
|
@@ -57,10 +54,9 @@ ActiveRecord::Schema.define(version: 20150610135235) do
|
|
57
54
|
t.integer "blog_post_id", null: false
|
58
55
|
t.text "description"
|
59
56
|
t.text "url"
|
57
|
+
t.index ["blog_post_id"], name: "index_footnotes_on_blog_post_id"
|
60
58
|
end
|
61
59
|
|
62
|
-
add_index "footnotes", ["blog_post_id"], name: "index_footnotes_on_blog_post_id"
|
63
|
-
|
64
60
|
create_table "images", force: :cascade do |t|
|
65
61
|
t.datetime "created_at", null: false
|
66
62
|
t.datetime "updated_at", null: false
|
@@ -78,29 +74,26 @@ ActiveRecord::Schema.define(version: 20150610135235) do
|
|
78
74
|
t.string "slug", null: false
|
79
75
|
t.text "summary", null: false
|
80
76
|
t.text "body"
|
77
|
+
t.index ["slug"], name: "index_legal_pages_on_slug"
|
81
78
|
end
|
82
79
|
|
83
|
-
add_index "legal_pages", ["slug"], name: "index_legal_pages_on_slug"
|
84
|
-
|
85
80
|
create_table "miscellany", force: :cascade do |t|
|
86
81
|
t.datetime "created_at", null: false
|
87
82
|
t.datetime "updated_at", null: false
|
88
83
|
t.string "key", null: false
|
89
84
|
t.text "value", null: false
|
90
85
|
t.text "description", null: false
|
86
|
+
t.index ["key"], name: "index_miscellany_on_key"
|
91
87
|
end
|
92
88
|
|
93
|
-
add_index "miscellany", ["key"], name: "index_miscellany_on_key"
|
94
|
-
|
95
89
|
create_table "topics", force: :cascade do |t|
|
96
90
|
t.datetime "created_at"
|
97
91
|
t.datetime "updated_at"
|
98
92
|
t.string "label", null: false
|
99
93
|
t.string "slug", null: false
|
94
|
+
t.index ["slug"], name: "index_topics_on_slug"
|
100
95
|
end
|
101
96
|
|
102
|
-
add_index "topics", ["slug"], name: "index_topics_on_slug"
|
103
|
-
|
104
97
|
create_table "videos", force: :cascade do |t|
|
105
98
|
t.datetime "created_at", null: false
|
106
99
|
t.datetime "updated_at", null: false
|
@@ -25,7 +25,7 @@ class FilterPreservationTest < ActionDispatch::IntegrationTest
|
|
25
25
|
find_link("Organization").click
|
26
26
|
find_link("Nintendo").click
|
27
27
|
first(".button.-cancel").click
|
28
|
-
assert page.current_url.include?("q[s]=organization+asc"), "filter
|
28
|
+
assert page.current_url.include?("q[s]=organization+asc"), "filter should be retained"
|
29
29
|
end
|
30
30
|
|
31
31
|
test "after filtering index, should retain filter on edit and save" do
|
@@ -58,4 +58,3 @@ class FilterPreservationTest < ActionDispatch::IntegrationTest
|
|
58
58
|
end
|
59
59
|
|
60
60
|
end
|
61
|
-
|
@@ -92,8 +92,8 @@ class ForbiddenRoutesTest < ActionDispatch::IntegrationTest
|
|
92
92
|
assert page.has_content?("Delete")
|
93
93
|
assert page.has_content?("Edit")
|
94
94
|
|
95
|
-
assert_raises
|
96
|
-
visit
|
95
|
+
assert_raises ActionController::RoutingError do
|
96
|
+
visit "/admin/cards"
|
97
97
|
end
|
98
98
|
|
99
99
|
# Unseat the Card class so that it doesn't leak out of this test
|
@@ -102,4 +102,30 @@ class ForbiddenRoutesTest < ActionDispatch::IntegrationTest
|
|
102
102
|
|
103
103
|
end
|
104
104
|
|
105
|
+
test "handles only allowed_actions index, edit, update" do
|
106
|
+
|
107
|
+
# Miscellany only allows index, edit, update
|
108
|
+
Miscellany.create(value:"Tchotchke", key:"tchotchke", description:"Test Tchotchke")
|
109
|
+
|
110
|
+
sign_in_dummy_administrator!
|
111
|
+
visit admin_miscellany_index_path
|
112
|
+
assert page.has_content?("tchotchke"), "should see the Miscellany on the index"
|
113
|
+
|
114
|
+
visit edit_admin_miscellany_path(Miscellany.first.id)
|
115
|
+
assert page.has_content?("Tchotchke"), "can see the Miscellany form"
|
116
|
+
assert page.has_content?("Save"), "can see the Miscellany form"
|
117
|
+
|
118
|
+
assert_raises ActionController::RoutingError do
|
119
|
+
visit "/admin/miscellany/new" # This route shouldn’t exist
|
120
|
+
end
|
121
|
+
|
122
|
+
assert_raises ActionController::RoutingError do
|
123
|
+
visit "/admin/miscellany/1" # This route shouldn’t exist
|
124
|
+
end
|
125
|
+
|
126
|
+
# Remove the test object
|
127
|
+
Miscellany.first.destroy
|
128
|
+
|
129
|
+
end
|
130
|
+
|
105
131
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PaginationTest < ActionDispatch::IntegrationTest
|
4
|
+
|
5
|
+
def setup
|
6
|
+
BlogPost.destroy_all
|
7
|
+
50.times do
|
8
|
+
BlogPost.create!({
|
9
|
+
title: SecureRandom.uuid,
|
10
|
+
body: "X",
|
11
|
+
summary: "X",
|
12
|
+
published_at: Time.current,
|
13
|
+
})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
BlogPost.destroy_all
|
19
|
+
end
|
20
|
+
|
21
|
+
test "shows pagination and its usable" do
|
22
|
+
sign_in_dummy_administrator!
|
23
|
+
visit("/admin/blog_posts")
|
24
|
+
assert page.has_content?("Next")
|
25
|
+
assert page.has_content?("Last")
|
26
|
+
assert page.has_content?("3")
|
27
|
+
visit("/admin/blog_posts?p=3")
|
28
|
+
assert page.has_content?("Next")
|
29
|
+
assert page.has_content?("Last")
|
30
|
+
assert page.has_content?("First")
|
31
|
+
assert page.has_content?("Prev")
|
32
|
+
end
|
33
|
+
|
34
|
+
test "after paginating index, should retain page on edit and back with crumb" do
|
35
|
+
sign_in_dummy_administrator!
|
36
|
+
visit("/admin/blog_posts?p=3")
|
37
|
+
first(".button.-edit").click # Open the editor form for a random blog post
|
38
|
+
first(".crumb a").click
|
39
|
+
assert page.current_url.include?("p=3"), "page should be retained"
|
40
|
+
end
|
41
|
+
|
42
|
+
test "after paginating index, should retain page on edit and back with button" do
|
43
|
+
sign_in_dummy_administrator!
|
44
|
+
visit("/admin/blog_posts?p=3")
|
45
|
+
first(".button.-edit").click # Open the editor form for a random blog post
|
46
|
+
first(".button.-cancel").click
|
47
|
+
assert page.current_url.include?("p=3"), "page should be retained"
|
48
|
+
end
|
49
|
+
|
50
|
+
test "after paginating index, should retain page on edit and save" do
|
51
|
+
sign_in_dummy_administrator!
|
52
|
+
visit("/admin/blog_posts?p=3")
|
53
|
+
first(".button.-edit").click # Open the editor form for a random blog post
|
54
|
+
first(".button.-save").click
|
55
|
+
assert page.current_url.include?("p=3"), "page should be retained"
|
56
|
+
end
|
57
|
+
|
58
|
+
test "after paginating index, should retain page on edit and failed validation" do
|
59
|
+
sign_in_dummy_administrator!
|
60
|
+
visit("/admin/blog_posts?p=3")
|
61
|
+
first(".button.-edit").click # Open the editor form for a random blog post
|
62
|
+
fill_in("blog_post[title]", with:"")
|
63
|
+
first(".button.-save").click
|
64
|
+
first(".button.-cancel").click
|
65
|
+
assert page.current_url.include?("p=3"), "page should be retained"
|
66
|
+
end
|
67
|
+
|
68
|
+
test "after paginating index, should NOT retain page on save and review" do
|
69
|
+
sign_in_dummy_administrator!
|
70
|
+
visit("/admin/blog_posts?p=3")
|
71
|
+
first(".button.-edit").click # Open the editor form for a random blog post
|
72
|
+
first(".button.-save-and-review").click
|
73
|
+
assert page.current_url.exclude?("p=3"), "page should not have been retained"
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|