tolaria 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/README.md +8 -3
  4. data/Rakefile +1 -0
  5. data/app/assets/fonts/admin/fontawesome.woff +0 -0
  6. data/app/assets/fonts/admin/fontawesome.woff2 +0 -0
  7. data/app/assets/stylesheets/admin/components/_navigation.scss +8 -13
  8. data/app/assets/stylesheets/admin/components/_resource_form.scss +16 -5
  9. data/app/assets/stylesheets/admin/settings/_fonts.scss +2 -8
  10. data/app/assets/stylesheets/admin/settings/_icons.scss +123 -0
  11. data/app/controllers/admin/admin_controller.rb +1 -1
  12. data/app/controllers/admin/sessions_controller.rb +1 -1
  13. data/app/controllers/tolaria/resource_controller.rb +42 -19
  14. data/app/controllers/tolaria/tolaria_controller.rb +2 -2
  15. data/app/helpers/admin/table_helper.rb +7 -7
  16. data/app/helpers/admin/view_helper.rb +6 -9
  17. data/app/views/admin/tolaria_resource/_form_buttons.html.erb +1 -3
  18. data/app/views/admin/tolaria_resource/_index_table.html.erb +1 -1
  19. data/app/views/admin/tolaria_resource/edit.html.erb +12 -3
  20. data/app/views/admin/tolaria_resource/index.html.erb +1 -4
  21. data/app/views/admin/tolaria_resource/show.html.erb +1 -1
  22. data/lib/tolaria.rb +1 -1
  23. data/lib/tolaria/engine.rb +0 -1
  24. data/lib/tolaria/ransack.rb +43 -0
  25. data/lib/tolaria/version.rb +1 -1
  26. data/test/demo/app/models/application_record.rb +3 -0
  27. data/test/demo/app/models/blog_post.rb +1 -1
  28. data/test/demo/app/models/footnote.rb +1 -1
  29. data/test/demo/app/models/image.rb +1 -1
  30. data/test/demo/app/models/legal_page.rb +1 -1
  31. data/test/demo/app/models/miscellany.rb +6 -2
  32. data/test/demo/app/models/topic.rb +1 -1
  33. data/test/demo/app/models/video.rb +1 -1
  34. data/test/demo/app/views/admin/legal_pages/_form.html.erb +1 -1
  35. data/test/demo/app/views/admin/miscellany/_form.html.erb +3 -0
  36. data/test/demo/config/application.rb +0 -4
  37. data/test/demo/config/environments/test.rb +2 -2
  38. data/test/demo/db/schema.rb +8 -15
  39. data/test/integration/filter_preservation_test.rb +1 -2
  40. data/test/integration/forbidden_routes_test.rb +28 -2
  41. data/test/integration/pagination_test.rb +76 -0
  42. data/test/integration/session_test.rb +1 -2
  43. data/tolaria.gemspec +5 -5
  44. metadata +34 -31
  45. data/app/assets/fonts/admin/fontawesome.eot +0 -0
  46. data/app/assets/fonts/admin/fontawesome.svg +0 -655
  47. data/app/assets/fonts/admin/fontawesome.ttf +0 -0
  48. 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", "update"
64
- url_for(action:"show", id:@resource.id)
65
- when "new", "create"
66
- url_for(action:"index")
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:params[:q]), class:"button -cancel" do %>
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
-
@@ -70,6 +70,6 @@
70
70
  <p class="pagination-desc"><%= page_entries_info @resources %></p>
71
71
  <% end %>
72
72
 
73
- <%= paginate @resources, theme:"admin", window:2 %>
73
+ <%= paginate @resources, theme:"admin", window:2, param_name:"p" %>
74
74
 
75
75
  <% end %>
@@ -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 params[:q].present? %>
10
- <% params[:q].each do |key, value| %>
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:params[: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:params[:q]), class:"button -primary" do %>
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:params[: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"
@@ -6,7 +6,6 @@ module Tolaria
6
6
  app.config.assets.precompile += %w[
7
7
  admin/admin.css
8
8
  admin/admin.js
9
- admin/lib/no.js
10
9
  admin/favicon.ico
11
10
  ]
12
11
  end
@@ -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
@@ -2,7 +2,7 @@ module Tolaria
2
2
 
3
3
  # Returns Tolaria’s version number
4
4
  def self.version
5
- Gem::Version.new("1.2.1")
5
+ Gem::Version.new("2.0.0")
6
6
  end
7
7
 
8
8
  module VERSION
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -1,4 +1,4 @@
1
- class BlogPost < ActiveRecord::Base
1
+ class BlogPost < ApplicationRecord
2
2
 
3
3
  has_and_belongs_to_many :topics, join_table:"blog_post_topics"
4
4
  has_many :footnotes
@@ -1,4 +1,4 @@
1
- class Footnote < ActiveRecord::Base
1
+ class Footnote < ApplicationRecord
2
2
  belongs_to :blog_post
3
3
  validates_presence_of :description
4
4
  validates_presence_of :url
@@ -1,4 +1,4 @@
1
- class Image < ActiveRecord::Base
1
+ class Image < ApplicationRecord
2
2
 
3
3
  validates_presence_of :title
4
4
  validates_presence_of :attachment_address
@@ -1,4 +1,4 @@
1
- class LegalPage < ActiveRecord::Base
1
+ class LegalPage < ApplicationRecord
2
2
 
3
3
  validates_presence_of :title
4
4
  validates_presence_of :summary
@@ -1,12 +1,16 @@
1
- class Miscellany < ActiveRecord::Base
1
+ class Miscellany < ApplicationRecord
2
2
 
3
3
  manage_with_tolaria using:{
4
4
  icon: "cogs",
5
5
  category: "Settings",
6
- allowed_actions: [:index, :show, :edit],
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
@@ -1,4 +1,4 @@
1
- class Topic < ActiveRecord::Base
1
+ class Topic < ApplicationRecord
2
2
 
3
3
  has_and_belongs_to_many :blog_posts, join_table:"blog_post_topics"
4
4
 
@@ -1,4 +1,4 @@
1
- class Video < ActiveRecord::Base
1
+ class Video < ApplicationRecord
2
2
 
3
3
  validates_presence_of :title
4
4
  validates_presence_of :youtube_id
@@ -1,5 +1,5 @@
1
1
  <%= f.label :title, "Title" %>
2
- <%= f.text_field :title, placeholder:"Page title" %>
2
+ <%= f.text_field :title, placeholder:"Page title", disabled:true %>
3
3
  <%= f.hint "The title of this page." %>
4
4
 
5
5
  <%= f.label :slug, "URL Slug" %>
@@ -0,0 +1,3 @@
1
+ <%= f.label :value, "Value" %>
2
+ <%= f.text_area :value, rows:3 %>
3
+ <%= f.hint @resource.description %>
@@ -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.serve_static_files = true
18
- config.static_cache_control = "public, max-age=3600"
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
@@ -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 not retained"
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 ActionView::Template::Error do
96
- visit new_admin_card_path
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