tolaria 1.2.1 → 2.0.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 (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