├── tmp
└── pids
│ └── .keep
├── .ruby-version
├── app
├── assets
│ ├── builds
│ │ └── .keep
│ ├── images
│ │ └── administrate
│ │ │ └── .keep
│ ├── config
│ │ └── administrate_manifest.js
│ ├── stylesheets
│ │ └── administrate
│ │ │ ├── components
│ │ │ ├── _form-actions.scss
│ │ │ ├── _app-container.scss
│ │ │ ├── _pagination.scss
│ │ │ ├── _flashes.scss
│ │ │ ├── _attributes.scss
│ │ │ ├── _main-content.scss
│ │ │ ├── _navigation.scss
│ │ │ └── _cells.scss
│ │ │ ├── library
│ │ │ ├── _clearfix.scss
│ │ │ └── _data-label.scss
│ │ │ ├── base
│ │ │ ├── _lists.scss
│ │ │ └── _layout.scss
│ │ │ └── application.scss
│ └── javascripts
│ │ └── administrate
│ │ ├── add_jquery.js
│ │ ├── application.js
│ │ └── controllers
│ │ ├── index.js
│ │ ├── application.js
│ │ ├── table_controller.js
│ │ └── tooltip_controller.js
└── views
│ ├── administrate
│ └── application
│ │ ├── _pagination.html.erb
│ │ ├── _collection_header_actions.html.erb
│ │ ├── _stylesheet.html.erb
│ │ ├── _javascript.html.erb
│ │ ├── _flashes.html.erb
│ │ ├── _collection_item_actions.html.erb
│ │ ├── _navigation.html.erb
│ │ └── _search.html.erb
│ ├── fields
│ ├── select
│ │ ├── _show.html.erb
│ │ ├── _index.html.erb
│ │ └── _form.html.erb
│ ├── time
│ │ ├── _show.html.erb
│ │ ├── _index.html.erb
│ │ └── _form.html.erb
│ ├── password
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── text
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── email
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── rich_text
│ │ ├── _show.html.erb
│ │ ├── _index.html.erb
│ │ └── _form.html.erb
│ ├── string
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── boolean
│ │ ├── _show.html.erb
│ │ ├── _index.html.erb
│ │ └── _form.html.erb
│ ├── date
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── number
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── url
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── date_time
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ ├── has_one
│ │ └── _index.html.erb
│ ├── has_many
│ │ └── _index.html.erb
│ ├── polymorphic
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ └── belongs_to
│ │ ├── _index.html.erb
│ │ ├── _show.html.erb
│ │ └── _form.html.erb
│ └── layouts
│ └── administrate
│ └── application.html.erb
├── .node-version
├── spec
├── support
│ ├── matchers
│ │ ├── .keep
│ │ └── to_sql.rb
│ ├── mixins
│ │ └── .keep
│ ├── shared_examples
│ │ └── .keep
│ ├── factory_girl.rb
│ ├── shoulda_matchers.rb
│ ├── have_flash_matcher.rb
│ ├── dashboard_helpers.rb
│ ├── webmock.rb
│ ├── features
│ │ └── page_elements.rb
│ ├── constant_helpers.rb
│ ├── mock_relation.rb
│ ├── controller_helpers.rb
│ ├── i18n.rb
│ ├── field_matchers.rb
│ ├── table.rb
│ └── webdrivers.rb
├── example_app
│ ├── app
│ │ ├── views
│ │ │ ├── pages
│ │ │ │ └── .keep
│ │ │ ├── fields
│ │ │ │ └── receipt_link
│ │ │ │ │ ├── _index.html.erb
│ │ │ │ │ └── _show.html.erb
│ │ │ ├── application
│ │ │ │ ├── _javascript.html.erb
│ │ │ │ └── _flashes.html.erb
│ │ │ ├── admin
│ │ │ │ ├── stats
│ │ │ │ │ ├── index.html+admin.erb
│ │ │ │ │ └── index.html.erb
│ │ │ │ └── customers
│ │ │ │ │ ├── _index_header.html+admin.erb
│ │ │ │ │ ├── _collection_header_actions.html.erb
│ │ │ │ │ ├── _collection_item_actions.html.erb
│ │ │ │ │ └── _index_header.html.erb
│ │ │ └── layouts
│ │ │ │ └── application.html.erb
│ │ ├── assets
│ │ │ ├── builds
│ │ │ │ └── .keep
│ │ │ ├── javascripts
│ │ │ │ ├── admin.js
│ │ │ │ └── admin
│ │ │ │ │ └── identity.js
│ │ │ ├── stylesheets
│ │ │ │ └── admin.css
│ │ │ └── config
│ │ │ │ └── manifest.js
│ │ ├── models
│ │ │ ├── host.rb
│ │ │ ├── page.rb
│ │ │ ├── payment.rb
│ │ │ ├── blog.rb
│ │ │ ├── series.rb
│ │ │ ├── country.rb
│ │ │ ├── application_record.rb
│ │ │ ├── blog
│ │ │ │ ├── tag.rb
│ │ │ │ └── post.rb
│ │ │ ├── product_meta_tag.rb
│ │ │ ├── log_entry.rb
│ │ │ ├── line_item.rb
│ │ │ ├── order.rb
│ │ │ └── customer.rb
│ │ ├── policies
│ │ │ ├── host_policy.rb
│ │ │ ├── page_policy.rb
│ │ │ ├── series_policy.rb
│ │ │ ├── stats_policy.rb
│ │ │ ├── customer_policy.rb
│ │ │ ├── line_item_policy.rb
│ │ │ ├── log_entry_policy.rb
│ │ │ ├── product_policy.rb
│ │ │ ├── product_meta_tag_policy.rb
│ │ │ ├── blog
│ │ │ │ ├── post_policy.rb
│ │ │ │ └── tag_policy.rb
│ │ │ ├── own
│ │ │ │ └── order_policy.rb
│ │ │ ├── payment_policy.rb
│ │ │ ├── order_policy.rb
│ │ │ └── application_policy.rb
│ │ ├── javascript
│ │ │ └── application.js
│ │ ├── controllers
│ │ │ ├── admin
│ │ │ │ ├── hosts_controller.rb
│ │ │ │ ├── pages_controller.rb
│ │ │ │ ├── orders_controller.rb
│ │ │ │ ├── payments_controller.rb
│ │ │ │ ├── series_controller.rb
│ │ │ │ ├── line_items_controller.rb
│ │ │ │ ├── product_meta_tags_controller.rb
│ │ │ │ ├── blog
│ │ │ │ │ ├── tags_controller.rb
│ │ │ │ │ └── posts_controller.rb
│ │ │ │ ├── products_controller.rb
│ │ │ │ ├── stats_controller.rb
│ │ │ │ ├── customers_controller.rb
│ │ │ │ ├── log_entries_controller.rb
│ │ │ │ └── application_controller.rb
│ │ │ ├── application_controller.rb
│ │ │ └── files_controller.rb
│ │ ├── fields
│ │ │ └── has_many_variant_field.rb
│ │ └── dashboards
│ │ │ ├── stat_dashboard.rb
│ │ │ ├── series_dashboard.rb
│ │ │ ├── payment_dashboard.rb
│ │ │ ├── country_dashboard.rb
│ │ │ ├── product_meta_tag_dashboard.rb
│ │ │ ├── host_dashboard.rb
│ │ │ ├── blog
│ │ │ ├── tag_dashboard.rb
│ │ │ └── post_dashboard.rb
│ │ │ ├── page_dashboard.rb
│ │ │ ├── log_entry_dashboard.rb
│ │ │ ├── line_item_dashboard.rb
│ │ │ └── product_dashboard.rb
│ ├── public
│ │ ├── favicon.ico
│ │ ├── icon.png
│ │ ├── robots.txt
│ │ └── icon.svg
│ ├── .rspec
│ ├── config
│ │ ├── environments
│ │ │ └── staging.rb
│ │ ├── initializers
│ │ │ ├── json_encoding.rb
│ │ │ ├── administrate.rb
│ │ │ ├── cookies_serializer.rb
│ │ │ ├── mime_types.rb
│ │ │ ├── session_store.rb
│ │ │ ├── errors.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ └── inflections.rb
│ │ ├── boot.rb
│ │ ├── environment.rb
│ │ ├── database.yml.sample
│ │ ├── secrets.yml
│ │ ├── locales
│ │ │ ├── en.yml
│ │ │ └── simple_form.en.yml
│ │ ├── routes.rb
│ │ └── newrelic.yml
│ ├── README.md
│ ├── db
│ │ └── migrate
│ │ │ ├── 20150903215027_add_shipped_at_to_orders.rb
│ │ │ ├── 20170702033920_add_timestamp_to_payments.rb
│ │ │ ├── 20171006144755_add_password_to_customers.rb
│ │ │ ├── 20170526151453_add_example_time_to_customer.rb
│ │ │ ├── 20160117011028_create_series.rb
│ │ │ ├── 20160119024340_add_kind_to_customer.rb
│ │ │ ├── 20200326202615_add_release_year_to_products.rb
│ │ │ ├── 20150916011117_add_email_subscriber_to_customers.rb
│ │ │ ├── 20180525115059_add_hidden_to_customers.rb
│ │ │ ├── 20250705215532_create_hosts.rb
│ │ │ ├── 20220804132651_create_blog_tags.rb
│ │ │ ├── 20160815100728_create_payments.rb
│ │ │ ├── 20220804133503_create_blog_posts_tags.rb
│ │ │ ├── 20150220194224_create_customers.rb
│ │ │ ├── 20200714081950_create_pages.rb
│ │ │ ├── 20170507115814_create_blog_posts.rb
│ │ │ ├── 20230802181655_change_products_price_to_decimal.rb
│ │ │ ├── 20171031155447_create_log_entries.rb
│ │ │ ├── 20150403065618_create_products.rb
│ │ │ ├── 20230802180848_change_line_items_unit_price_to_decimal.rb
│ │ │ ├── 20170508183744_create_product_meta_tags.rb
│ │ │ ├── 20170510122301_create_countries.rb
│ │ │ ├── 20241113130741_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│ │ │ ├── 20150417044505_create_line_items.rb
│ │ │ ├── 20150411204433_create_orders.rb
│ │ │ ├── 20150914175022_add_slug_to_products.rb
│ │ │ └── 20241113130739_add_service_name_to_active_storage_blobs.active_storage.rb
│ ├── config.ru
│ ├── lib
│ │ ├── administrate
│ │ │ └── field
│ │ │ │ └── receipt_link.rb
│ │ ├── tasks
│ │ │ └── development_seeds.rake
│ │ └── templates
│ │ │ └── erb
│ │ │ └── scaffold
│ │ │ └── _form.html.erb
│ └── spec
│ │ ├── models
│ │ └── product_spec.rb
│ │ └── features
│ │ └── log_search_spec.rb
├── app_templates
│ └── config
│ │ └── routes.rb
├── features
│ ├── hosts_index_page_spec.rb
│ ├── associations_spec.rb
│ ├── payments_show_spec.rb
│ ├── new_page_spec.rb
│ ├── log_entries_show_spec.rb
│ ├── form_errors_spec.rb
│ └── payments_index_spec.rb
├── spec_helper.rb
├── lib
│ ├── fields
│ │ └── email_spec.rb
│ └── pages
│ │ └── show_spec.rb
├── administrate
│ └── views
│ │ └── fields
│ │ ├── has_many
│ │ ├── _show_spec.rb
│ │ └── _form_spec.rb
│ │ ├── date_time
│ │ └── _index_spec.rb
│ │ ├── url
│ │ └── _form_spec.rb
│ │ ├── polymorphic
│ │ └── _form_spec.rb
│ │ ├── rich_text
│ │ └── _index_spec.rb
│ │ ├── time
│ │ └── _index_spec.rb
│ │ └── text
│ │ └── _form_spec.rb
├── models
│ └── line_item_spec.rb
├── dashboards
│ └── order_dashboard_spec.rb
├── i18n_spec.rb
├── generators
│ └── views
│ │ ├── navigation_generator_spec.rb
│ │ └── form_generator_spec.rb
└── rails_helper.rb
├── gemfiles
└── .gitignore
├── .rspec
├── Procfile
├── .stylelintignore
├── .tool-versions
├── config
├── routes.rb
├── i18n-tasks.yml
└── locales
│ ├── administrate.zh-CN.yml
│ ├── administrate.zh-TW.yml
│ ├── administrate.ja.yml
│ ├── administrate.ko.yml
│ ├── administrate.sl.yml
│ ├── administrate.da.yml
│ ├── administrate.sv.yml
│ ├── administrate.ar.yml
│ ├── administrate.en.yml
│ ├── administrate.nl.yml
│ ├── administrate.ru.yml
│ ├── administrate.uk.yml
│ ├── administrate.vi.yml
│ ├── administrate.bs.yml
│ ├── administrate.ca.yml
│ ├── administrate.fi.yml
│ ├── administrate.sq.yml
│ ├── administrate.id.yml
│ ├── administrate.es.yml
│ ├── administrate.tr.yml
│ ├── administrate.it.yml
│ ├── administrate.pt.yml
│ ├── administrate.fr.yml
│ ├── administrate.pt-BR.yml
│ ├── administrate.de.yml
│ └── administrate.pl.yml
├── bin
├── dev-assets
├── rake
├── bundle
├── rspec
├── rails
├── dev
├── build-gem
├── deploy
├── appraisal
├── spring
├── setup
├── release
├── merge
└── generate-database-schema
├── lib
├── generators
│ └── administrate
│ │ ├── field
│ │ ├── templates
│ │ │ ├── _index.html.erb
│ │ │ ├── _show.html.erb
│ │ │ ├── field_object.rb.erb
│ │ │ └── _form.html.erb
│ │ └── field_generator.rb
│ │ ├── routes
│ │ └── templates
│ │ │ └── routes.rb.erb
│ │ ├── dashboard
│ │ └── USAGE
│ │ ├── views
│ │ ├── form_generator.rb
│ │ ├── show_generator.rb
│ │ ├── navigation_generator.rb
│ │ ├── new_generator.rb
│ │ ├── edit_generator.rb
│ │ ├── views_generator.rb
│ │ ├── index_generator.rb
│ │ └── layout_generator.rb
│ │ ├── install
│ │ └── templates
│ │ │ └── application_controller.rb.erb
│ │ └── test_record.rb
├── administrate
│ ├── page.rb
│ ├── version.rb
│ ├── field
│ │ ├── email.rb
│ │ ├── boolean.rb
│ │ ├── time.rb
│ │ ├── string.rb
│ │ ├── text.rb
│ │ ├── rich_text.rb
│ │ ├── date.rb
│ │ ├── url.rb
│ │ ├── password.rb
│ │ └── date_time.rb
│ ├── generator_helpers.rb
│ ├── custom_dashboard.rb
│ ├── namespace
│ │ └── resource.rb
│ ├── not_authorized_error.rb
│ ├── page
│ │ ├── show.rb
│ │ ├── form.rb
│ │ ├── collection.rb
│ │ └── base.rb
│ └── namespace.rb
├── tasks
│ └── administrate_tasks.rake
└── administrate.rb
├── Procfile.dev
├── config.ru
├── .sample.env
├── docs
├── using_administrate.md
├── guides.md
├── guides
│ ├── hiding_dashboards_from_sidebar.md
│ └── scoping_has_many_relations.md
└── using_administrate
│ └── stable_sorting.md
├── CODE_OF_CONDUCT.md
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── bundle-audit.yml
│ ├── dynamic-readme.yml
│ ├── dynamic-security.yml
│ ├── diff-check.yml
│ ├── lint.yml
│ └── codeql-analysis.yml
├── .yardopts
├── .stylelintrc.json
├── Appraisals
├── SECURITY.md
├── .gitignore
├── Rakefile
├── app.json
└── package.json
/tmp/pids/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.4.6
2 |
--------------------------------------------------------------------------------
/app/assets/builds/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 20.11.0
2 |
--------------------------------------------------------------------------------
/spec/support/matchers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/support/mixins/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/images/administrate/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gemfiles/.gitignore:
--------------------------------------------------------------------------------
1 | *.gemfile.lock
2 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/pages/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/example_app/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/support/shared_examples/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/example_app/app/assets/builds/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C config/puma.rb
2 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | app/assets/stylesheets/administrate/reset/**
2 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | ruby 3.4.6
2 | nodejs 20.11.0
3 | yarn 1.22.22
4 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Administrate::Engine.routes.draw do
2 | end
3 |
--------------------------------------------------------------------------------
/bin/dev-assets:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | "$(dirname "$0")"/dev -m js=1,css=1
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/assets/javascripts/admin.js:
--------------------------------------------------------------------------------
1 | //= require_tree ./admin
2 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/host.rb:
--------------------------------------------------------------------------------
1 | class Host < ApplicationRecord
2 | end
3 |
--------------------------------------------------------------------------------
/lib/generators/administrate/field/templates/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%= field.to_s %>
2 |
--------------------------------------------------------------------------------
/lib/generators/administrate/field/templates/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%= field.to_s %>
2 |
--------------------------------------------------------------------------------
/spec/example_app/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 | --format Fuubar
4 |
--------------------------------------------------------------------------------
/spec/example_app/config/environments/staging.rb:
--------------------------------------------------------------------------------
1 | require_relative "production"
2 |
--------------------------------------------------------------------------------
/lib/administrate/page.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | module Page
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/lib/administrate/version.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | VERSION = "1.0.0".freeze
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/assets/stylesheets/admin.css:
--------------------------------------------------------------------------------
1 | /*
2 | *= require_tree ./admin
3 | */
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/host_policy.rb:
--------------------------------------------------------------------------------
1 | class HostPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/page_policy.rb:
--------------------------------------------------------------------------------
1 | class PagePolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/app/assets/config/administrate_manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_tree ../builds
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/series_policy.rb:
--------------------------------------------------------------------------------
1 | class SeriesPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/stats_policy.rb:
--------------------------------------------------------------------------------
1 | class StatsPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/README.md:
--------------------------------------------------------------------------------
1 | This file is here only for the sake of a spec. It's not really a README.
2 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/customer_policy.rb:
--------------------------------------------------------------------------------
1 | class CustomerPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/line_item_policy.rb:
--------------------------------------------------------------------------------
1 | class LineItemPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/log_entry_policy.rb:
--------------------------------------------------------------------------------
1 | class LogEntryPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/product_policy.rb:
--------------------------------------------------------------------------------
1 | class ProductPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/javascript/application.js:
--------------------------------------------------------------------------------
1 | // Entry point for the build script in your package.json
2 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/page.rb:
--------------------------------------------------------------------------------
1 | class Page < ApplicationRecord
2 | belongs_to :product
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/payment.rb:
--------------------------------------------------------------------------------
1 | class Payment < ApplicationRecord
2 | belongs_to :order
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/fields/receipt_link/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to field.filename, field.data %>
2 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/fields/receipt_link/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to field.filename, field.data %>
2 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/json_encoding.rb:
--------------------------------------------------------------------------------
1 | ActiveSupport::JSON::Encoding.time_precision = 0
2 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C config/puma.rb
2 | js: yarn build --watch
3 | css: yarn build:css --watch
4 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require ::File.expand_path("../spec/example_app/config/environment", __FILE__)
2 | run Rails.application
3 |
--------------------------------------------------------------------------------
/spec/app_templates/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | root to: "application#show"
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/blog.rb:
--------------------------------------------------------------------------------
1 | module Blog
2 | def self.table_name_prefix
3 | "blog_"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/product_meta_tag_policy.rb:
--------------------------------------------------------------------------------
1 | class ProductMetaTagPolicy < ApplicationPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/spec/support/factory_girl.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.include FactoryBot::Syntax::Methods
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/series.rb:
--------------------------------------------------------------------------------
1 | class Series < ApplicationRecord
2 | validates :name, presence: true
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/administrate/HEAD/spec/example_app/public/icon.png
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_form-actions.scss:
--------------------------------------------------------------------------------
1 | .form-actions {
2 | margin-left: calc(15% + 2rem);
3 | }
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/blog/post_policy.rb:
--------------------------------------------------------------------------------
1 | module Blog
2 | class PostPolicy < ApplicationPolicy
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/blog/tag_policy.rb:
--------------------------------------------------------------------------------
1 | module Blog
2 | class TagPolicy < ApplicationPolicy
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/.sample.env:
--------------------------------------------------------------------------------
1 | # http://ddollar.github.com/foreman/
2 | RACK_ENV=development
3 | SECRET_KEY_BASE=development_secret
4 | PORT=3000
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/country.rb:
--------------------------------------------------------------------------------
1 | class Country < ApplicationRecord
2 | validates :code, :name, presence: true
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/add_jquery.js:
--------------------------------------------------------------------------------
1 | import jquery from "jquery";
2 |
3 | window.jQuery = jquery;
4 | window.$ = jquery;
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative '../spec/example_app/config/boot'
4 | require 'rake'
5 | Rake.application.run
6 |
--------------------------------------------------------------------------------
/spec/example_app/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_pagination.html.erb:
--------------------------------------------------------------------------------
1 | <%= paginate resources, param_name: local_assigns.fetch(:param_name, "_page") %>
2 |
--------------------------------------------------------------------------------
/docs/using_administrate.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using Administrate
3 | ---
4 |
5 | - [Stable sorting](./using_administrate/stable_sorting.md)
6 |
--------------------------------------------------------------------------------
/lib/tasks/administrate_tasks.rake:
--------------------------------------------------------------------------------
1 | # desc "Explaining what the task does"
2 | # task :administrate do
3 | # # Task goes here
4 | # end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/spec/support/matchers/to_sql.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :to_sql do |sql|
2 | match do |actual|
3 | actual.to_sql == sql
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/hosts_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class HostsController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/pages_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class PagesController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/administrate.rb:
--------------------------------------------------------------------------------
1 | Administrate::Engine.add_stylesheet("admin")
2 | Administrate::Engine.add_javascript("admin")
3 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/orders_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class OrdersController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/payments_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class PaymentsController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/series_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class SeriesController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/line_items_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class LineItemsController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/public/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/product_meta_tags_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class ProductMetaTagsController < Admin::ApplicationController
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/example_app/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/fields/has_many_variant_field.rb:
--------------------------------------------------------------------------------
1 | class HasManyVariantField < Administrate::Field::HasMany
2 | # Only here to test that this works out of the box
3 | end
4 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/blog/tags_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | module Blog
3 | class TagsController < Admin::ApplicationController
4 | end
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/stat_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/custom_dashboard"
2 |
3 | class StatDashboard < Administrate::CustomDashboard
4 | resource "Stats"
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/library/_clearfix.scss:
--------------------------------------------------------------------------------
1 | @mixin administrate-clearfix {
2 | &::after {
3 | clear: both;
4 | content: "";
5 | display: block;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/blog/posts_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | module Blog
3 | class PostsController < Admin::ApplicationController
4 | end
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/blog/tag.rb:
--------------------------------------------------------------------------------
1 | module Blog
2 | class Tag < ApplicationRecord
3 | has_and_belongs_to_many :posts
4 |
5 | validates :name, presence: true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/shoulda_matchers.rb:
--------------------------------------------------------------------------------
1 | Shoulda::Matchers.configure do |config|
2 | config.integrate do |with|
3 | with.test_framework :rspec
4 | with.library :rails
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path("../spring", __FILE__)
4 | rescue LoadError
5 | end
6 | require 'bundler/setup'
7 | load Gem.bin_path('rspec-core', 'rspec')
8 |
--------------------------------------------------------------------------------
/spec/example_app/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link administrate-internal/docs.css
2 |
3 | //= link_tree ../images
4 | //= link_tree ../builds
5 | //= link admin.css
6 | //= link admin.js
7 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/product_meta_tag.rb:
--------------------------------------------------------------------------------
1 | class ProductMetaTag < ApplicationRecord
2 | belongs_to :product
3 |
4 | validates :meta_title, :meta_description, presence: true
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/blog/post.rb:
--------------------------------------------------------------------------------
1 | module Blog
2 | class Post < ApplicationRecord
3 | has_and_belongs_to_many :tags
4 |
5 | validates :title, :body, presence: true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.action_dispatch.cookies_serializer = :marshal
4 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of conduct
2 |
3 | By participating in this project, you agree to abide by the
4 | [thoughtbot code of conduct][1].
5 |
6 | [1]: https://thoughtbot.com/open-source-code-of-conduct
7 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | APP_PATH = File.expand_path('../../spec/example_app/config/application', __FILE__)
4 | require_relative '../spec/example_app/config/boot'
5 | require 'rails/commands'
6 |
--------------------------------------------------------------------------------
/lib/generators/administrate/field/templates/field_object.rb.erb:
--------------------------------------------------------------------------------
1 | require "administrate/field/base"
2 |
3 | class <%= class_name %>Field < Administrate::Field::Base
4 | def to_s
5 | data
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/application/_javascript.html.erb:
--------------------------------------------------------------------------------
1 | <% Administrate::Engine.javascripts.each do |js_path| %>
2 | <%= javascript_include_tag js_path %>
3 | <% end %>
4 |
5 | <%= yield :javascript %>
6 |
--------------------------------------------------------------------------------
/bin/dev:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | if ! gem list foreman -i --silent; then
6 | echo "Installing foreman..."
7 | gem install foreman
8 | fi
9 |
10 | exec foreman start -f Procfile.dev "$@"
11 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150903215027_add_shipped_at_to_orders.rb:
--------------------------------------------------------------------------------
1 | class AddShippedAtToOrders < ActiveRecord::Migration[4.2]
2 | def change
3 | add_column :orders, :shipped_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20170702033920_add_timestamp_to_payments.rb:
--------------------------------------------------------------------------------
1 | class AddTimestampToPayments < ActiveRecord::Migration[5.1]
2 | def change
3 | change_table(:payments, &:timestamps)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/support/have_flash_matcher.rb:
--------------------------------------------------------------------------------
1 | module Features
2 | def have_flash(text, options = {})
3 | options.reverse_merge!(type: :notice)
4 | have_css(".flash-#{options[:type]}", text: text)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: "_administrate-prototype_session"
4 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20171006144755_add_password_to_customers.rb:
--------------------------------------------------------------------------------
1 | class AddPasswordToCustomers < ActiveRecord::Migration[4.2]
2 | def change
3 | add_column :customers, :password, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20170526151453_add_example_time_to_customer.rb:
--------------------------------------------------------------------------------
1 | class AddExampleTimeToCustomer < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :customers, :example_time, :time
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/administrate/field/templates/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= f.label field.attribute %>
3 |
4 |
5 | <%= f.text_field field.attribute %>
6 |
7 |
--------------------------------------------------------------------------------
/spec/support/dashboard_helpers.rb:
--------------------------------------------------------------------------------
1 | module DashboardHelpers
2 | def displayed(resource)
3 | (resource.class.to_s + "Dashboard")
4 | .constantize
5 | .new
6 | .display_resource(resource)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20160117011028_create_series.rb:
--------------------------------------------------------------------------------
1 | class CreateSeries < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :series do |t|
4 | t.string :name, null: false
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/administrate.rb:
--------------------------------------------------------------------------------
1 | require "administrate/engine"
2 | require "administrate/version"
3 |
4 | module Administrate
5 | def self.deprecator
6 | @deprecator ||= ActiveSupport::Deprecation.new(VERSION, "Administrate")
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20160119024340_add_kind_to_customer.rb:
--------------------------------------------------------------------------------
1 | class AddKindToCustomer < ActiveRecord::Migration[4.2]
2 | def change
3 | add_column :customers, :kind, :string, null: false, default: "standard"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20200326202615_add_release_year_to_products.rb:
--------------------------------------------------------------------------------
1 | class AddReleaseYearToProducts < ActiveRecord::Migration[6.0]
2 | def change
3 | add_column :products, :release_year, :integer, limit: 2
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/administrate/field/email.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Email < Field::Base
6 | def self.searchable?
7 | true
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/own/order_policy.rb:
--------------------------------------------------------------------------------
1 | module Own
2 | class OrderPolicy < ::OrderPolicy
3 | class Scope < Scope
4 | def resolve
5 | scope.where(customer: user)
6 | end
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150916011117_add_email_subscriber_to_customers.rb:
--------------------------------------------------------------------------------
1 | class AddEmailSubscriberToCustomers < ActiveRecord::Migration[4.2]
2 | def change
3 | add_column :customers, :email_subscriber, :boolean
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20180525115059_add_hidden_to_customers.rb:
--------------------------------------------------------------------------------
1 | class AddHiddenToCustomers < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :customers, :hidden, :boolean, default: false, null: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/administrate/routes/templates/routes.rb.erb:
--------------------------------------------------------------------------------
1 | namespace :<%= namespace %> do
2 | <% dashboard_resources.each do |resource| %> resources :<%= resource %>
3 | <%end%>
4 | root to: "<%= dashboard_resources.first %>#index"
5 | end
6 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/library/_data-label.scss:
--------------------------------------------------------------------------------
1 | @mixin data-label {
2 | color: $hint-grey;
3 | font-size: 0.8em;
4 | font-weight: 400;
5 | letter-spacing: 0.0357em;
6 | position: relative;
7 | text-transform: uppercase;
8 | }
9 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20250705215532_create_hosts.rb:
--------------------------------------------------------------------------------
1 | class CreateHosts < ActiveRecord::Migration[8.0]
2 | def change
3 | create_table :hosts do |t|
4 | t.string :name
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/application/_flashes.html.erb:
--------------------------------------------------------------------------------
1 | <% if flash.any? %>
2 |
3 | <% flash.each do |key, value| -%>
4 |
<%= value.html_safe %>
5 | <% end -%>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/bin/build-gem:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | VERSION=$(ruby -r ./lib/administrate/version.rb -e "puts Administrate::VERSION")
6 | yarn run build
7 | yarn run build:css
8 | gem build administrate.gemspec
9 | gem install "administrate-${VERSION}.gem"
10 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20220804132651_create_blog_tags.rb:
--------------------------------------------------------------------------------
1 | class CreateBlogTags < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :blog_tags do |t|
4 | t.string :name
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/products_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class ProductsController < Admin::ApplicationController
3 | private
4 |
5 | def find_resource(param)
6 | Product.find_by!(slug: param)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/stats/index.html+admin.erb:
--------------------------------------------------------------------------------
1 |
2 |
Stats
3 |
4 |
Total Customers: <%= @stats[:customer_count] %>
5 |
6 |
Total Orders: <%= @stats[:order_count] %>
7 |
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_app-container.scss:
--------------------------------------------------------------------------------
1 | .app-container {
2 | align-items: stretch;
3 | display: flex;
4 | margin-left: auto;
5 | margin-right: auto;
6 | max-width: 100rem;
7 | min-height: 100vh;
8 | padding: $base-spacing;
9 | }
10 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 | end
6 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/stats/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
Stats
3 |
4 |
Stats are only visible to Admin. <%= link_to("Become the Admin", become_admin_customer_path("admin"), class: "identity__become-action") %>
5 |
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20160815100728_create_payments.rb:
--------------------------------------------------------------------------------
1 | class CreatePayments < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :payments do |t|
4 | t.references :order, index: true
5 | end
6 | add_foreign_key :payments, :orders
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/example_app/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 |
7 | if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1")
8 | Rails.application.load_server
9 | end
10 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/payment_policy.rb:
--------------------------------------------------------------------------------
1 | class PaymentPolicy < ApplicationPolicy
2 | def index?
3 | user.admin?
4 | end
5 |
6 | def create?
7 | false
8 | end
9 |
10 | def update?
11 | false
12 | end
13 |
14 | def destroy?
15 | false
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_collection_header_actions.html.erb:
--------------------------------------------------------------------------------
1 | <% [existing_action?(collection_presenter.resource_name, :edit),
2 | existing_action?(collection_presenter.resource_name, :destroy)].count(true).times do %>
3 |
4 | <% end %>
5 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20220804133503_create_blog_posts_tags.rb:
--------------------------------------------------------------------------------
1 | class CreateBlogPostsTags < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :blog_posts_tags do |t|
4 | t.belongs_to :post
5 | t.belongs_to :tag
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/application.js:
--------------------------------------------------------------------------------
1 | import "./add_jquery";
2 | import "trix";
3 | import "@rails/actiontext";
4 |
5 | import "@hotwired/turbo-rails";
6 | import "@selectize/selectize/dist/js/selectize.min.js";
7 |
8 | import "./controllers";
9 | import "./vendor/css-anchor-positioning";
10 |
--------------------------------------------------------------------------------
/lib/administrate/field/boolean.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Boolean < Base
6 | def to_s
7 | if data.nil?
8 | "-"
9 | else
10 | data.to_s
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/support/webmock.rb:
--------------------------------------------------------------------------------
1 | require "webmock/rspec"
2 |
3 | # Downloading the Firefox driver involves a redirect
4 | driver_hosts = ["github-releases.githubusercontent.com"]
5 |
6 | # Additionally, avoid conflict with Selenium (localhost)
7 | WebMock.disable_net_connect!(allow_localhost: true, allow: driver_hosts)
8 |
--------------------------------------------------------------------------------
/bin/deploy:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Run this script to deploy the app to Heroku.
4 |
5 | set -e
6 |
7 | branch="$(git symbolic-ref HEAD --short)"
8 | target="${1:-staging}"
9 |
10 | git push "$target" "$branch:master"
11 | heroku run rake db:migrate --remote "$target"
12 | heroku restart --remote "$target"
13 |
--------------------------------------------------------------------------------
/spec/example_app/config/database.yml.sample:
--------------------------------------------------------------------------------
1 | development: &default
2 | adapter: postgresql
3 | database: administrate-prototype_development
4 | encoding: utf8
5 | min_messages: warning
6 | pool: 2
7 | timeout: 5000
8 |
9 | test:
10 | <<: *default
11 | database: administrate-prototype_test
12 |
--------------------------------------------------------------------------------
/spec/support/features/page_elements.rb:
--------------------------------------------------------------------------------
1 | module Features
2 | def have_header(title)
3 | have_css("h1", text: title)
4 | end
5 |
6 | def have_label(title)
7 | have_css("label", text: title)
8 | end
9 |
10 | def have_table_header(title)
11 | have_css("th", text: title)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150220194224_create_customers.rb:
--------------------------------------------------------------------------------
1 | class CreateCustomers < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :customers do |t|
4 | t.string :name, null: false
5 | t.string :email, null: false
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20200714081950_create_pages.rb:
--------------------------------------------------------------------------------
1 | class CreatePages < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :pages do |t|
4 | t.string :title
5 | t.text :body
6 | t.belongs_to :product, foreign_key: true
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/features/hosts_index_page_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | feature "hosts index page" do
4 | scenario "user views hosts" do
5 | host = create(:host)
6 |
7 | visit admin_hosts_path
8 |
9 | expect(page).to have_header("Hosts")
10 | expect(page).to have_content(host.name)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Did something not work as expected?
4 | labels: 'bug'
5 | ---
6 |
7 | * What were you trying to do?
8 | * What did you end up with (logs, or, even better, example apps are great!)?
9 | * What versions are you running?
10 | - Rails
11 | - administrate
12 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/customers/_index_header.html+admin.erb:
--------------------------------------------------------------------------------
1 | <% content_for(:header_middle) do %>
2 | You are logged in as <%= pundit_user.name %> .
3 | <% end %>
4 |
5 | <%= render template: 'administrate/application/_index_header', locals: local_assigns %>
6 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20170507115814_create_blog_posts.rb:
--------------------------------------------------------------------------------
1 | class CreateBlogPosts < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :blog_posts do |t|
4 | t.string :title
5 | t.datetime :published_at
6 | t.text :body
7 |
8 | t.timestamps null: false
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20230802181655_change_products_price_to_decimal.rb:
--------------------------------------------------------------------------------
1 | class ChangeProductsPriceToDecimal < ActiveRecord::Migration[7.0]
2 | def up
3 | change_column :products, :price, :decimal, precision: 15, scale: 2
4 | end
5 |
6 | def down
7 | change_column :products, :price, :float
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/generators/administrate/dashboard/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Generates a Dashboard object for a model,
3 | pulling the attributes from database columns.
4 |
5 | Example:
6 | rails generate administrate:dashboard FooBar [--namespace admin]
7 |
8 | This will create:
9 | app/dashboards/foo_bar_dashboard.rb
10 |
--------------------------------------------------------------------------------
/lib/administrate/generator_helpers.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | module GeneratorHelpers
3 | def call_generator(generator, *args)
4 | Rails::Generators.invoke(generator, args, generator_options)
5 | end
6 |
7 | private
8 |
9 | def generator_options
10 | {behavior: behavior}
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20171031155447_create_log_entries.rb:
--------------------------------------------------------------------------------
1 | class CreateLogEntries < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :log_entries do |t|
4 | t.string :action
5 | t.references :logeable, polymorphic: true, index: true
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/log_entry.rb:
--------------------------------------------------------------------------------
1 | class LogEntry < ApplicationRecord
2 | belongs_to :logeable, polymorphic: true
3 |
4 | validate do |log_entry|
5 | if log_entry.action == "cancel" && log_entry.logeable.is_a?(Customer)
6 | log_entry.errors.add(:logeable, "A customer cannot be cancelled")
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
2 | RSpec.configure do |config|
3 | config.expect_with :rspec do |expectations|
4 | expectations.syntax = :expect
5 | end
6 |
7 | config.mock_with :rspec do |mocks|
8 | mocks.syntax = :expect
9 | end
10 |
11 | config.order = :random
12 | end
13 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/base/_lists.scss:
--------------------------------------------------------------------------------
1 | ul,
2 | ol {
3 | list-style-type: none;
4 | margin: 0;
5 | padding: 0;
6 | }
7 |
8 | dl {
9 | margin-bottom: $small-spacing;
10 |
11 | dt {
12 | font-weight: $bold-font-weight;
13 | margin-top: $small-spacing;
14 | }
15 |
16 | dd {
17 | margin: 0;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150403065618_create_products.rb:
--------------------------------------------------------------------------------
1 | class CreateProducts < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :products do |t|
4 | t.string :name
5 | t.float :price
6 | t.text :description
7 | t.string :image_url
8 |
9 | t.timestamps null: false
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20230802180848_change_line_items_unit_price_to_decimal.rb:
--------------------------------------------------------------------------------
1 | class ChangeLineItemsUnitPriceToDecimal < ActiveRecord::Migration[7.0]
2 | def up
3 | change_column :line_items, :unit_price, :decimal, precision: 15, scale: 2
4 | end
5 |
6 | def down
7 | change_column :line_items, :unit_price, :float
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/docs/guides.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Guides
3 | ---
4 |
5 | - [Hiding Dashboards from the Sidebar](./guides/hiding_dashboards_from_sidebar)
6 | - [Customising the search](./guides/customising_search)
7 | - [Scoping HasMany Relations](./guides/scoping_has_many_relations.md)
8 | - [Switching templates with view variants](./guides/switching_templates_with_view_variants.md)
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Is there something new you'd like to see in administrate?
4 | labels: 'feature-request'
5 | ---
6 |
7 | * What would you like to be able to do? Can you provide some examples?
8 | * How could we go about implementing that?
9 | * Can you think of other approaches to the problem?
10 |
--------------------------------------------------------------------------------
/spec/example_app/lib/administrate/field/receipt_link.rb:
--------------------------------------------------------------------------------
1 | require "administrate/field/base"
2 |
3 | module Administrate
4 | module Field
5 | class ReceiptLink < Base
6 | def data
7 | "/files/receipts/#{filename}"
8 | end
9 |
10 | def filename
11 | "receipt-#{resource.id}.txt"
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/support/constant_helpers.rb:
--------------------------------------------------------------------------------
1 | module ConstantHelpers
2 | def remove_constants(*constants)
3 | constants.each { |const| Object.send(:remove_const, const) }
4 | rescue NameError => e
5 | warn "Warning from ConstantHelpers::remove_constants:\n\t#{e}"
6 | end
7 | end
8 |
9 | RSpec.configure do |config|
10 | config.include ConstantHelpers
11 | end
12 |
--------------------------------------------------------------------------------
/config/i18n-tasks.yml:
--------------------------------------------------------------------------------
1 | search:
2 | paths:
3 | - "app/controllers"
4 | - "app/helpers"
5 | - "app/pages"
6 | - "app/views"
7 |
8 | data:
9 | read:
10 | - config/locales/administrate.%{locale}.yml
11 |
12 | ignore_unused:
13 | - activerecord.*
14 | - administrate.*
15 | - date.*
16 | - simple_form.*
17 | - time.*
18 | - titles.*
19 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 2
3 |
4 | updates:
5 | - package-ecosystem: bundler
6 | directory: "/"
7 | schedule:
8 | interval: daily
9 | open-pull-requests-limit: 10
10 |
11 | - package-ecosystem: github-actions
12 | directory: "/"
13 | schedule:
14 | interval: weekly
15 | time: '02:00'
16 | timezone: 'Etc/UTC'
17 |
--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | -
2 | CHANGELOG.md
3 | docs/getting_started.md
4 | docs/rails_api.md
5 | docs/customizing_dashboards.md
6 | docs/customizing_page_views.md
7 | docs/customizing_attribute_partials.md
8 | docs/adding_controllers_without_related_model.md
9 | docs/adding_custom_field_types.md
10 | docs/authentication.md
11 | docs/authorization.md
12 | CODE_OF_CONDUCT.md
13 | LICENSE.md
14 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/line_item.rb:
--------------------------------------------------------------------------------
1 | class LineItem < ApplicationRecord
2 | belongs_to :order
3 | belongs_to :product
4 |
5 | validates :product, presence: true
6 | validates :order, presence: true
7 | validates :unit_price, presence: true
8 | validates :quantity, presence: true
9 |
10 | def total_price
11 | unit_price * quantity
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/example_app/app/assets/javascripts/admin/identity.js:
--------------------------------------------------------------------------------
1 | let mainContent = document.querySelector(".main-content");
2 | if (mainContent) {
3 | mainContent.addEventListener("click", evt => {
4 | if (evt.target.classList.contains("identity__become-action")) {
5 | if (!confirm("Change identity?")) {
6 | evt.preventDefault();
7 | }
8 | }
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/order_policy.rb:
--------------------------------------------------------------------------------
1 | class OrderPolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | scope.all
5 | end
6 | end
7 |
8 | def create?
9 | user.admin?
10 | end
11 |
12 | def update?
13 | user.admin? || user.id == record.customer_id
14 | end
15 |
16 | def destroy?
17 | user.admin?
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/customers/_collection_header_actions.html.erb:
--------------------------------------------------------------------------------
1 | <%
2 | [
3 | existing_action?(collection_presenter.resource_name, :edit),
4 | existing_action?(collection_presenter.resource_name, :destroy),
5 | resources.klass == Customer, # "Become" action
6 | ].count(true).times do %>
7 |
8 | <% end %>
9 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/customers/_collection_item_actions.html.erb:
--------------------------------------------------------------------------------
1 | <%= render template: 'administrate/application/_collection_item_actions', locals: local_assigns %>
2 |
3 | <% if resource.class == Customer %>
4 |
5 | <%= link_to("Become", become_admin_customer_path(resource), class: "identity__become-action") %>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/spec/support/mock_relation.rb:
--------------------------------------------------------------------------------
1 | class MockRelation
2 | def initialize(data)
3 | @data = data
4 | end
5 |
6 | delegate :==, :empty?, :map, to: :@data
7 |
8 | def page(n)
9 | self
10 | end
11 |
12 | def per(n)
13 | @data.first(n)
14 | end
15 |
16 | def limit(n)
17 | @data.first(n)
18 | end
19 |
20 | def size
21 | @data.size
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20170508183744_create_product_meta_tags.rb:
--------------------------------------------------------------------------------
1 | class CreateProductMetaTags < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :product_meta_tags do |t|
4 | t.belongs_to :product
5 | t.string :meta_title, null: false
6 | t.string :meta_description, null: false
7 |
8 | t.timestamps null: false
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/administrate/custom_dashboard.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | class CustomDashboard
3 | include Administrate
4 |
5 | class << self
6 | def resource_name(_opts)
7 | named_resource.pluralize.titleize
8 | end
9 |
10 | def resource(resource_name)
11 | define_singleton_method(:named_resource) { resource_name }
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/example_app/config/secrets.yml:
--------------------------------------------------------------------------------
1 | development:
2 | secret_key_base: 1a022e4f335d24af3d6bd622b9daef5a44808afbe16d4f1c8bed03675ab91ecd9c76ddc1a30f3fbb668b02c00abcba2ecc3714c95943b8f4af86289a2a46d5e1
3 |
4 | test:
5 | secret_key_base: test_secret
6 |
7 | staging:
8 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
9 |
10 | production:
11 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
12 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20170510122301_create_countries.rb:
--------------------------------------------------------------------------------
1 | class CreateCountries < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :countries do |t|
4 | t.string :code, null: false, index: {unique: true}
5 | t.string :name
6 |
7 | t.timestamps null: false
8 | end
9 | add_column :customers, :country_code, :string, index: true
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/example_app/lib/tasks/development_seeds.rake:
--------------------------------------------------------------------------------
1 | if Rails.env.development? || Rails.env.test?
2 | require "factory_bot"
3 |
4 | namespace :dev do
5 | desc "Seed data for development environment"
6 | task prime: "db:setup" do
7 | include FactoryBot::Syntax::Methods
8 |
9 | # create(:user, email: "user@example.com", password: "password")
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_pagination.scss:
--------------------------------------------------------------------------------
1 | .pagination {
2 | margin-top: $base-spacing;
3 | padding-left: $base-spacing;
4 | padding-right: $base-spacing;
5 | text-align: center;
6 |
7 | .first,
8 | .prev,
9 | .page,
10 | .next,
11 | .last {
12 | margin: $small-spacing;
13 | }
14 |
15 | .current {
16 | font-weight: $bold-font-weight;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/administrate/field/time.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Time < Base
6 | def time
7 | I18n.localize(
8 | data,
9 | format: format
10 | )
11 | end
12 |
13 | private
14 |
15 | def format
16 | options.fetch(:format, "%I:%M%p")
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/admin/customers/_index_header.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for(:header_middle) do %>
2 | You are logged in as <%= pundit_user.name %> . <%= link_to("Become the Admin", become_admin_customer_path("admin"), class: "identity__become-action")%>
3 | <% end %>
4 |
5 | <%= render template: 'administrate/application/_index_header', locals: local_assigns %>
6 |
--------------------------------------------------------------------------------
/spec/features/associations_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "Associations" do
4 | it "can associate to namespaced models on dashboards" do
5 | post = create(:blog_post)
6 | tag = create(:blog_tag, name: "foobarisms")
7 | post.tags << tag
8 |
9 | visit admin_blog_post_url(post)
10 |
11 | expect(page).to have_css(".cell-data", text: "foobarisms")
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/support/controller_helpers.rb:
--------------------------------------------------------------------------------
1 | module ControllerHelpers
2 | def capture_view_locals
3 | allow(@controller).to receive(:render)
4 | yield
5 |
6 | locals = nil
7 | expect(@controller).to have_received(:render).at_least(1).times do |*args|
8 | args.each do |arg|
9 | locals ||= arg.try(:fetch, :locals, nil)
10 | end
11 | end
12 | locals
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/form_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class FormGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_form
10 | copy_resource_template("_form")
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/show_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class ShowGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_template
10 | copy_resource_template("show")
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/errors.rb:
--------------------------------------------------------------------------------
1 | require "net/http"
2 |
3 | # Example:
4 | # begin
5 | # some http call
6 | # rescue *HTTP_ERRORS => error
7 | # notify_hoptoad error
8 | # end
9 |
10 | HTTP_ERRORS = [
11 | EOFError,
12 | Errno::ECONNRESET,
13 | Errno::EINVAL,
14 | Net::HTTPBadResponse,
15 | Net::HTTPHeaderSyntaxError,
16 | Net::ProtocolError,
17 | Timeout::Error
18 | ]
19 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20241113130741_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20211119233751)
2 | class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[8.0]
3 | def change
4 | return unless table_exists?(:active_storage_blobs)
5 |
6 | change_column_null(:active_storage_blobs, :checksum, true)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/series_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class SeriesDashboard < Administrate::BaseDashboard
4 | ATTRIBUTE_TYPES = {
5 | id: Field::Number,
6 | name: Field::String
7 | }.freeze
8 |
9 | COLLECTION_ATTRIBUTES = %i[id name].freeze
10 |
11 | SHOW_PAGE_ATTRIBUTES = %i[id name].freeze
12 |
13 | FORM_ATTRIBUTES = [
14 | :name
15 | ].freeze
16 | end
17 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/navigation_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class NavigationGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_navigation
10 | copy_resource_template("_navigation")
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/controllers/index.js:
--------------------------------------------------------------------------------
1 | import { application } from "./application";
2 |
3 | import SelectController from "./select_controller";
4 | import TableController from "./table_controller";
5 | import TooltipController from "./tooltip_controller";
6 |
7 | application.register("select", SelectController);
8 | application.register("table", TableController);
9 | application.register("tooltip", TooltipController);
10 |
--------------------------------------------------------------------------------
/lib/administrate/field/string.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class String < Field::Base
6 | def self.searchable?
7 | true
8 | end
9 |
10 | def truncate
11 | data.to_s[0...truncation_length]
12 | end
13 |
14 | private
15 |
16 | def truncation_length
17 | options.fetch(:truncate, 50)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/new_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class NewGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_new
10 | copy_resource_template("new")
11 | copy_resource_template("_form")
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/administrate/field/text.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Text < Administrate::Field::Base
6 | def self.searchable?
7 | false
8 | end
9 |
10 | def truncate
11 | data.to_s[0...truncation_length]
12 | end
13 |
14 | private
15 |
16 | def truncation_length
17 | options.fetch(:truncate, 50)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/edit_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class EditGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_edit
10 | copy_resource_template("edit")
11 | copy_resource_template("_form")
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@thoughtbot/stylelint-config",
3 | "rules": {
4 | "function-no-unknown": null,
5 | "scss/no-global-function-names": null,
6 | "selector-class-pattern": [
7 | "^[a-z]([-]?[a-z0-9]+)*(__[a-z0-9]([-]?[a-z0-9]+)*)?(--[a-z0-9]([-]?[a-z0-9]+)*)?$",
8 | {
9 | "message": "Expected class selector to match BEM CSS pattern: https://en.bem.info/methodology/css"
10 | }
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/views/fields/select/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Select Show Partial
3 |
4 | This partial renders a selectable text attribute,
5 | to be displayed on a resource's show page.
6 |
7 | ## Local variables:
8 |
9 | - `field`:
10 | An instance of [Administrate::Field::Select][1].
11 | A wrapper around the attribute pulled from the database.
12 |
13 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Select
14 | %>
15 |
16 | <%= field.data %>
17 |
--------------------------------------------------------------------------------
/spec/example_app/lib/templates/erb/scaffold/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
2 | <%%= f.error_notification %>
3 |
4 |
5 | <%- attributes.each do |attribute| -%>
6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
7 | <%- end -%>
8 |
9 |
10 |
11 | <%%= f.button :submit %>
12 |
13 | <%% end %>
14 |
--------------------------------------------------------------------------------
/.github/workflows/bundle-audit.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bundler Audit
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | types: [opened, synchronize, reopened]
9 |
10 | jobs:
11 | audit:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v6
15 | - name: 'Bundler Audit'
16 | uses: thoughtbot/bundler-audit-action@main
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 |
--------------------------------------------------------------------------------
/app/views/fields/select/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Select Index Partial
3 |
4 | This partial renders a selectable text attribute,
5 | to be displayed on a resource's index page.
6 |
7 | ## Local variables:
8 |
9 | - `field`:
10 | An instance of [Administrate::Field::Select][1].
11 | A wrapper around the attribute pulled from the database.
12 |
13 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Select
14 | %>
15 |
16 | <%= field.data %>
17 |
--------------------------------------------------------------------------------
/.github/workflows/dynamic-readme.yml:
--------------------------------------------------------------------------------
1 | name: update-templates
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - README.md
9 | workflow_dispatch:
10 |
11 | jobs:
12 | update-templates:
13 | permissions:
14 | contents: write
15 | pull-requests: write
16 | pages: write
17 | uses: thoughtbot/templates/.github/workflows/dynamic-readme.yaml@main
18 | secrets:
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/dynamic-security.yml:
--------------------------------------------------------------------------------
1 | name: update-security
2 |
3 | on:
4 | push:
5 | paths:
6 | - SECURITY.md
7 | branches:
8 | - main
9 | workflow_dispatch:
10 |
11 | jobs:
12 | update-security:
13 | permissions:
14 | contents: write
15 | pull-requests: write
16 | pages: write
17 | uses: thoughtbot/templates/.github/workflows/dynamic-security.yaml@main
18 | secrets:
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_stylesheet.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Stylesheet Partial
3 |
4 | This partial imports the necessary stylesheets on each page.
5 | By default, it includes the application CSS,
6 | but each page can define additional CSS sources
7 | by providing a `content_for(:stylesheet)` block.
8 | %>
9 |
10 | <% Administrate::Engine.stylesheets.each do |css_path| %>
11 | <%= stylesheet_link_tag css_path %>
12 | <% end %>
13 |
14 | <%= yield :stylesheet %>
15 |
--------------------------------------------------------------------------------
/app/views/fields/time/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Time Show Partial
3 |
4 | This partial renders an time attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered as a text tag.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Time][1].
13 | A wrapper around the time attributes pulled from the model.
14 |
15 | %>
16 |
17 | <% if field.data %>
18 | <%= field.time %>
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/lib/administrate/field/rich_text.rb:
--------------------------------------------------------------------------------
1 | require "administrate/field/base"
2 |
3 | module Administrate
4 | module Field
5 | class RichText < Administrate::Field::Base
6 | def to_s
7 | data&.to_plain_text
8 | end
9 |
10 | def truncate
11 | to_s.truncate(truncation_length)
12 | end
13 |
14 | private
15 |
16 | def truncation_length
17 | options.fetch(:truncate, 50)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/lib/fields/email_spec.rb:
--------------------------------------------------------------------------------
1 | require "administrate/field/email"
2 |
3 | describe Administrate::Field::Email do
4 | describe "#partial_prefixes" do
5 | it "returns a partial based on the page being rendered" do
6 | page = :show
7 | field = Administrate::Field::Email.new(:email, "foo@example.com", page)
8 |
9 | prefixes = field.partial_prefixes
10 |
11 | expect(prefixes).to eq(["fields/email", "fields/base"])
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/fields/time/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Time Index Partial
3 |
4 | This partial renders an time attribute
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered as a text tag.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Time][1].
13 | A wrapper around the time attributes pulled from the model.
14 |
15 | %>
16 |
17 | <% if field.data %>
18 | <%= field.time %>
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/files_controller.rb:
--------------------------------------------------------------------------------
1 | class FilesController < ApplicationController
2 | def download
3 | filename = params[:filename]
4 | match = %r{receipt-(\d+)}.match(filename)
5 | if match
6 | payment_id = match[1]
7 | send_data("This is the receipt for payment ##{payment_id}", filename: "#{filename}.txt")
8 | else
9 | render status: 404, layout: false, file: Rails.root.join("public/404.html")
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150417044505_create_line_items.rb:
--------------------------------------------------------------------------------
1 | class CreateLineItems < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :line_items do |t|
4 | t.references :order, index: true
5 | t.references :product, index: true
6 | t.float :unit_price
7 | t.integer :quantity
8 |
9 | t.timestamps null: false
10 | end
11 | add_foreign_key :line_items, :orders
12 | add_foreign_key :line_items, :products
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/bin/appraisal:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 | #
4 | # This file was generated by Bundler.
5 | #
6 | # The application 'appraisal' is installed as part of a gem, and
7 | # this file is here to facilitate running it.
8 | #
9 |
10 | require "pathname"
11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12 | Pathname.new(__FILE__).realpath)
13 |
14 | require "rubygems"
15 | require "bundler/setup"
16 |
17 | load Gem.bin_path("appraisal", "appraisal")
18 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise "rails60" do
2 | gem "drb"
3 | gem "mutex_m"
4 | gem "psych", "< 4"
5 | gem "rails", "~> 6.0.3.4"
6 | gem "sprockets-rails", "~> 3.4"
7 | end
8 |
9 | appraise "rails61" do
10 | gem "drb"
11 | gem "mutex_m"
12 | gem "rails", "~> 6.1"
13 | end
14 |
15 | appraise "rails70" do
16 | gem "rails", "~> 7.0"
17 | end
18 |
19 | appraise "rails80" do
20 | gem "rails", "~> 8.0"
21 | end
22 |
23 | appraise "pundit21" do
24 | gem "pundit", "~> 2.1.0"
25 | end
26 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/has_many/_show_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "fields/has_many/_show", type: :view do
4 | context "without any associated records" do
5 | it "displays 'None'" do
6 | has_many = double(resources: [])
7 |
8 | render(
9 | partial: "fields/has_many/show",
10 | locals: {field: has_many}
11 | )
12 |
13 | expect(rendered.strip).to eq(t("administrate.fields.has_many.none"))
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150411204433_create_orders.rb:
--------------------------------------------------------------------------------
1 | class CreateOrders < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :orders do |t|
4 | t.references :customer, index: true
5 | t.string :address_line_one
6 | t.string :address_line_two
7 | t.string :address_city
8 | t.string :address_state
9 | t.string :address_zip
10 |
11 | t.timestamps null: false
12 | end
13 | add_foreign_key :orders, :customers
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/support/i18n.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.include AbstractController::Translation
3 |
4 | def with_translations(locale, translations)
5 | original_backend = I18n.backend
6 |
7 | new_backend = I18n::Backend::KeyValue.new({}, true)
8 | new_backend.store_translations(locale, translations)
9 |
10 | I18n.backend = I18n::Backend::Chain.new(new_backend, original_backend)
11 |
12 | yield
13 | ensure
14 | I18n.backend = original_backend
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/stats_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class StatsController < Admin::ApplicationController
3 | before_action :with_variant, only: %i[index]
4 |
5 | def index
6 | @stats = {
7 | customer_count: Customer.count,
8 | order_count: Order.count
9 | }
10 | end
11 |
12 | private
13 |
14 | def with_variant
15 | if @current_user.admin?
16 | request.variant = :admin
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_javascript.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Javascript Partial
3 |
4 | This partial imports the necessary javascript on each page.
5 | By default, it includes the application JS,
6 | but each page can define additional JS sources
7 | by providing a `content_for(:javascript)` block.
8 | %>
9 |
10 | <% Administrate::Engine.javascripts.each do |js_path| %>
11 | <%= javascript_include_tag js_path, "data-turbo-track": "reload", defer: true %>
12 | <% end %>
13 |
14 | <%= yield :javascript %>
15 |
--------------------------------------------------------------------------------
/app/views/fields/password/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Password Index Partial
3 |
4 | This partial renders a password attribute
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered as a truncated string.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Password][1].
13 | A wrapper around the Password.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Password
16 | %>
17 |
18 | <%= field.truncate %>
19 |
--------------------------------------------------------------------------------
/app/views/fields/password/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Password Show Partial
3 |
4 | This partial renders a password attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered as an truncate string.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Password][1].
13 | A wrapper around the Password.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Password
16 | %>
17 |
18 | <%= field.truncate %>
19 |
--------------------------------------------------------------------------------
/app/views/fields/text/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Text Index Partial
3 |
4 | This partial renders a text attribute
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered as a truncated string.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Text][1].
13 | A wrapper around the Text pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Text
16 | %>
17 |
18 | <%= field.truncate %>
19 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require "rubygems"
8 | require "bundler"
9 |
10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
11 | Gem.paths = { "GEM_PATH" => Bundler.bundle_path.to_s }
12 | gem "spring", match[1]
13 | require "spring/binstub"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/example_app/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | date:
3 | formats:
4 | default:
5 | "%m/%d/%Y"
6 | with_weekday:
7 | "%a %m/%d/%y"
8 |
9 | helpers:
10 | label:
11 | order:
12 | address: Address
13 | details: Details
14 |
15 | time:
16 | formats:
17 | default:
18 | "%a, %b %-d, %Y at %r"
19 | date:
20 | "%b %-d, %Y"
21 | short:
22 | "%B %d"
23 |
24 | titles:
25 | application: Administrate-prototype
26 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/base/_layout.scss:
--------------------------------------------------------------------------------
1 | html {
2 | background-color: $base-background-color;
3 | box-sizing: border-box;
4 | }
5 |
6 | *,
7 | *::before,
8 | *::after {
9 | box-sizing: inherit;
10 | }
11 |
12 | figure {
13 | margin: 0;
14 | }
15 |
16 | /* stylelint-disable selector-no-qualifying-type, selector-class-pattern */
17 | form.button_to { // we don't control this class name
18 | display: contents;
19 | }
20 |
21 | img,
22 | picture {
23 | margin: 0;
24 | max-width: 100%;
25 | }
26 |
--------------------------------------------------------------------------------
/app/views/fields/email/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Email Index Partial
3 |
4 | This partial renders an email address,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the relationship is rendered as a `mailto` link.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Email][1].
13 | A wrapper around the email pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Email
16 | %>
17 |
18 | <%= mail_to field.data %>
19 |
--------------------------------------------------------------------------------
/app/views/fields/email/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Email Show Partial
3 |
4 | This partial renders an email address,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the relationship is rendered as a `mailto` link.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Email][1].
13 | A wrapper around the email pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Email
16 | %>
17 |
18 | <%= mail_to field.data %>
19 |
--------------------------------------------------------------------------------
/lib/administrate/field/date.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Date < Base
6 | def date
7 | I18n.localize(
8 | data.in_time_zone(timezone).to_date,
9 | format: format
10 | )
11 | end
12 |
13 | private
14 |
15 | def format
16 | options.fetch(:format, :default)
17 | end
18 |
19 | def timezone
20 | options.fetch(:timezone, ::Time.zone)
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/fields/rich_text/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # RichText Show Partial
3 |
4 | This partial renders a rich_text attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered as HTML.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::RichText][1].
13 | A wrapper around the RichText object pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/RichText
16 | %>
17 |
18 | <%= field.data %>
19 |
--------------------------------------------------------------------------------
/app/views/fields/string/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # String Index Partial
3 |
4 | This partial renders a string attribute
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered as a truncated string.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::String][1].
13 | A wrapper around the String pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/String
16 | %>
17 |
18 | <%= field.truncate %>
19 |
--------------------------------------------------------------------------------
/lib/administrate/field/url.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Url < Field::Base
6 | def self.searchable?
7 | true
8 | end
9 |
10 | def truncate
11 | data.to_s[0...truncation_length]
12 | end
13 |
14 | def html_options
15 | @options[:html_options] || {}
16 | end
17 |
18 | private
19 |
20 | def truncation_length
21 | options.fetch(:truncate, 50)
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/models/line_item_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe LineItem do
4 | it { should validate_presence_of :product }
5 | it { should validate_presence_of :order }
6 | it { should validate_presence_of :unit_price }
7 | it { should validate_presence_of :quantity }
8 |
9 | describe "#total_price" do
10 | it "is the product of the unit price and the quantity" do
11 | item = build(:line_item, unit_price: 20, quantity: 2)
12 |
13 | expect(item.total_price).to eq 40
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure parameters to be partially matched (e.g. passw matches password) and
4 | # filtered from the log file Use this to limit dissemination of sensitive
5 | # information. See the ActiveSupport::ParameterFilter documentation for
6 | # supported notations and behaviors.
7 | Rails.application.config.filter_parameters += %i[
8 | passw email secret token _key crypt salt certificate otp ssn cvv cvc
9 | ]
10 |
--------------------------------------------------------------------------------
/spec/support/field_matchers.rb:
--------------------------------------------------------------------------------
1 | module FieldMatchers
2 | def should_permit_param(expected_param, for_attribute:, on_model:)
3 | permitted_param = described_class.permitted_attribute(
4 | for_attribute,
5 | resource_class: on_model
6 | )
7 |
8 | permitted_param =
9 | if permitted_param.respond_to?(:transform_keys)
10 | permitted_param.deep_transform_keys(&:to_s)
11 | else
12 | permitted_param.to_s
13 | end
14 |
15 | expect(permitted_param).to eq(expected_param)
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/payment_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/field/receipt_link"
2 | require "administrate/base_dashboard"
3 |
4 | class PaymentDashboard < Administrate::BaseDashboard
5 | ATTRIBUTE_TYPES = {
6 | id: Field::Number,
7 | receipt: Field::ReceiptLink,
8 | created_at: Field::DateTime,
9 | updated_at: Field::DateTime,
10 | order: Field::BelongsTo
11 | }
12 |
13 | COLLECTION_ATTRIBUTES = [
14 | :id,
15 | :receipt
16 | ]
17 |
18 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTE_TYPES.keys
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/fields/rich_text/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # RichText Index Partial
3 |
4 | This partial renders a rich_text attribute
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered as a plain text truncated string.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::RichText][1].
13 | A wrapper around the RichText pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/RichText
16 | %>
17 |
18 | <%= field.truncate %>
19 |
--------------------------------------------------------------------------------
/spec/features/payments_show_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | feature "payment show page" do
4 | scenario "user cannot click through to the edit page" do
5 | payment = create(:payment)
6 |
7 | visit admin_payment_path(payment)
8 | expect(page).not_to have_button t("administrate.actions.edit")
9 | end
10 |
11 | scenario "user cannot delete record" do
12 | payment = create(:payment)
13 |
14 | visit admin_payment_path(payment)
15 | expect(page).not_to have_button t("administrate.actions.destroy")
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/fields/text/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Text Show Partial
3 |
4 | This partial renders a text attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered as text with whitespace preserved.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Text][1].
13 | A wrapper around the Text pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Text
16 | %>
17 |
18 | <%= field.data %>
19 |
--------------------------------------------------------------------------------
/app/views/fields/boolean/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Boolean Show Partial
3 |
4 | This partial renders a boolean attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered
8 | as a string representation of the boolean value.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Boolean][1].
14 | A wrapper around the boolean value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Boolean
17 | %>
18 |
19 | <%= field.to_s %>
20 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_flashes.scss:
--------------------------------------------------------------------------------
1 | @each $flash-type, $color in $flashes {
2 | .flash-#{$flash-type} {
3 | background-color: $color;
4 | color: mix($black, $color, 60%);
5 | display: block;
6 | margin-bottom: $base-spacing * 0.5;
7 | padding: $base-spacing * 0.5;
8 | text-align: center;
9 |
10 | a {
11 | color: mix($black, $color, 70%);
12 | text-decoration: underline;
13 |
14 | &:focus,
15 | &:hover {
16 | color: mix($black, $color, 90%);
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/views/fields/boolean/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Boolean Index Partial
3 |
4 | This partial renders a boolean attribute,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered
8 | as a string representation of the boolean value.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Boolean][1].
14 | A wrapper around the boolean value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Boolean
17 | %>
18 |
19 | <%= field.to_s %>
20 |
--------------------------------------------------------------------------------
/app/views/fields/date/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Date Index Partial
3 |
4 | This partial renders a date attribute,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered
8 | as a localized date string.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Date][1].
14 | A wrapper around the Date value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Date
17 | %>
18 |
19 | <% if field.data %>
20 | <%= field.date %>
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/app/views/fields/date/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Date Show Partial
3 |
4 | This partial renders a date attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered
8 | as a localized date string.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Date][1].
14 | A wrapper around the Date value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Date
17 | %>
18 |
19 | <% if field.data %>
20 | <%= field.date %>
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/app/views/fields/number/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Number Index Partial
3 |
4 | This partial renders a number attribute,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered
8 | using the formatting options given in the resource's dashboard.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Number][1].
14 | A wrapper around the number pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Number
17 | %>
18 |
19 | <%= field.to_s %>
20 |
--------------------------------------------------------------------------------
/app/views/fields/number/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Number Show Partial
3 |
4 | This partial renders a number attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered
8 | using the formatting options given in the resource's dashboard.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::Number][1].
14 | A wrapper around the number pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Number
17 | %>
18 |
19 | <%= field.to_s %>
20 |
--------------------------------------------------------------------------------
/spec/dashboards/order_dashboard_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe OrderDashboard do
4 | describe "#permitted_attributes" do
5 | it "returns the attribute name by default" do
6 | dashboard = OrderDashboard.new
7 |
8 | expect(dashboard.permitted_attributes).to include(:address_line_one)
9 | end
10 |
11 | it "returns the attribute_id name for belongs_to relationships" do
12 | dashboard = OrderDashboard.new
13 |
14 | expect(dashboard.permitted_attributes).to include("customer_id")
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/fields/string/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # String Show Partial
3 |
4 | This partial renders a string attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered as text with whitespace preserved.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::String][1].
13 | A wrapper around the String pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/String
16 | %>
17 |
18 | <%= field.data %>
19 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/country_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class CountryDashboard < Administrate::BaseDashboard
4 | ATTRIBUTES = %i[code name].freeze
5 |
6 | ATTRIBUTE_TYPES = {
7 | created_at: Field::DateTime,
8 | updated_at: Field::DateTime,
9 | code: Field::String,
10 | name: Field::String
11 | }.freeze
12 |
13 | COLLECTION_ATTRIBUTES = ATTRIBUTES
14 | FORM_ATTRIBUTES = ATTRIBUTES
15 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTES
16 |
17 | def display_resource(resource)
18 | resource.name
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/views/fields/url/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Url Index Partial
3 |
4 | This partial renders a URL address,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the value is rendered as an `a` element.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Url][1].
13 | A wrapper around the email pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Url
16 | %>
17 |
18 | <%= content_tag :a, href: field.data, **field.html_options do %>
19 | <%= field.data %>
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/views/fields/url/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Url Show Partial
3 |
4 | This partial renders a URL address,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the value is rendered as an `a` element.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Url][1].
13 | A wrapper around the email pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Url
16 | %>
17 |
18 | <%= content_tag :a, href: field.data, **field.html_options do %>
19 | <%= field.data %>
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/date_time/_index_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "fields/date_time/_index", type: :view do
4 | it "renders date_time" do
5 | product = create(:product)
6 | date_time = instance_double(
7 | "Administrate::Field::DateTime.with_options(
8 | format: #{Time::DATE_FORMATS[:default]}
9 | )",
10 | data: product,
11 | datetime: product.created_at
12 | )
13 |
14 | render(
15 | partial: "fields/date_time/index",
16 | locals: {field: date_time, namespace: :admin}
17 | )
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/app/views/fields/date_time/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # DateTime Index Partial
3 |
4 | This partial renders a datetime attribute,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the attribute is rendered
8 | as a localized date & time string.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::DateTime][1].
14 | A wrapper around the DateTime value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/DateTime
17 | %>
18 |
19 | <% if field.data %>
20 | <%= field.datetime %>
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/app/views/fields/date_time/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # DateTime Show Partial
3 |
4 | This partial renders a datetime attribute,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the attribute is rendered
8 | as a localized date & time string.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::DateTime][1].
14 | A wrapper around the DateTime value pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/DateTime
17 | %>
18 |
19 | <% if field.data %>
20 | <%= field.datetime %>
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20150914175022_add_slug_to_products.rb:
--------------------------------------------------------------------------------
1 | class AddSlugToProducts < ActiveRecord::Migration[4.2]
2 | def change
3 | add_column :products, :slug, :string, null: true
4 |
5 | products = select_all("select id, name from products")
6 | products.each do |product|
7 | update(<<-SQL)
8 | UPDATE products
9 | SET slug='#{product["name"].parameterize}'
10 | WHERE id=#{product["id"]}
11 | SQL
12 | end
13 |
14 | change_column_null :products, :slug, false
15 | add_index :products, :slug, unique: true
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/administrate/namespace/resource.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | class Namespace
3 | class Resource
4 | attr_reader :namespace, :resource
5 |
6 | def initialize(namespace, resource)
7 | @namespace = namespace
8 | @resource = resource
9 | end
10 |
11 | def to_s
12 | name.to_s
13 | end
14 |
15 | def to_sym
16 | name
17 | end
18 |
19 | def name
20 | resource.to_s.gsub(/^#{namespace}\//, "").to_sym
21 | end
22 |
23 | def path
24 | name.to_s.tr("/", "_")
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_flashes.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Flash Partial
3 |
4 | This partial renders flash messages on every page.
5 |
6 | ## Relevant Helpers:
7 |
8 | - `flash`:
9 | Returns a hash,
10 | where the keys are the type of flash (alert, error, notice, etc)
11 | and the values are the message to be displayed.
12 | %>
13 |
14 | <% if flash.any? %>
15 |
16 | <% flash.each do |key, value| -%>
17 | <% next unless value.respond_to?(:html_safe) %>
18 |
<%= value.html_safe %>
19 | <% end -%>
20 |
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/docs/guides/hiding_dashboards_from_sidebar.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hiding Dashboards from the Sidebar
3 | ---
4 |
5 | Resources can be removed from the sidebar by removing their `index` action
6 | from the routes. For example:
7 |
8 | ```ruby
9 | # config/routes.rb
10 | Rails.application.routes.draw do
11 | namespace :admin do
12 | resources :line_items, except: :index
13 | resources :orders
14 | resources :products
15 | root to: "customers#index"
16 | end
17 | end
18 | ```
19 |
20 | In this case, only Orders and Products will appear in the sidebar, while
21 | Line Items can still appear as an association.
22 |
--------------------------------------------------------------------------------
/lib/administrate/field/password.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class Password < Field::Base
6 | def self.searchable?
7 | false
8 | end
9 |
10 | def self.sortable?
11 | false
12 | end
13 |
14 | def truncate
15 | data.to_s.gsub(/./, character)[0...truncation_length]
16 | end
17 |
18 | private
19 |
20 | def truncation_length
21 | options.fetch(:truncate, 50)
22 | end
23 |
24 | def character
25 | options.fetch(:character, "•")
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/i18n_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "i18n/tasks"
3 |
4 | describe "I18n" do
5 | let(:i18n) { I18n::Tasks::BaseTask.new }
6 | let(:missing_keys) { i18n.missing_keys }
7 | let(:unused_keys) { i18n.unused_keys }
8 |
9 | it "does not have missing keys" do
10 | expect(missing_keys).to be_empty,
11 | "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
12 | end
13 |
14 | it "does not have unused keys" do
15 | expect(unused_keys).to be_empty,
16 | "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/order.rb:
--------------------------------------------------------------------------------
1 | class Order < ApplicationRecord
2 | belongs_to :customer
3 |
4 | validates :customer, presence: true
5 | has_many :line_items, dependent: :destroy
6 | has_many :payments, dependent: :restrict_with_error
7 | has_many :log_entries, as: :logeable
8 |
9 | validates :address_line_one, presence: true
10 | validates :address_line_two, presence: true
11 | validates :address_city, presence: true
12 | validates :address_state, presence: true
13 | validates :address_zip, presence: true
14 |
15 | def total_price
16 | line_items.map(&:total_price).reduce(0, :+)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/views_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | class ViewsGenerator < Administrate::ViewGenerator
6 | def copy_templates
7 | view = "administrate:views:"
8 | call_generator("#{view}index", resource_path, "--namespace", namespace)
9 | call_generator("#{view}show", resource_path, "--namespace", namespace)
10 | call_generator("#{view}new", resource_path, "--namespace", namespace)
11 | call_generator("#{view}edit", resource_path, "--namespace", namespace)
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/product_meta_tag_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class ProductMetaTagDashboard < Administrate::BaseDashboard
4 | ATTRIBUTES = [
5 | :meta_title,
6 | :meta_description
7 | ].freeze
8 |
9 | ATTRIBUTE_TYPES = {
10 | created_at: Field::DateTime,
11 | updated_at: Field::DateTime,
12 | meta_title: Field::String,
13 | meta_description: Field::String
14 | }.freeze
15 |
16 | COLLECTION_ATTRIBUTES = ATTRIBUTES
17 | FORM_ATTRIBUTES = ATTRIBUTES
18 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTES
19 |
20 | def display_resource(tag)
21 | tag.meta_title
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/new_page_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Admin creates a new tag", type: :feature, js: true do
4 | it "allows selecting a resource and submitting the form" do
5 | _blog_post = create(:blog_post, title: "How to Bake Bread")
6 | _blog_post2 = create(:blog_post, title: "How to Play Guitar")
7 |
8 | visit "/admin/blog/tags/new"
9 | find(".selectize-input").click
10 | find(".selectize-input").fill_in(with: "Bake Bread")
11 | find(".option", text: "How to Bake Bread").click
12 | click_button "Create Tag"
13 |
14 | expect(page).to have_content("How to Bake Bread")
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_attributes.scss:
--------------------------------------------------------------------------------
1 | .attribute-label {
2 | @include data-label;
3 |
4 | clear: left;
5 | float: left;
6 | margin-bottom: $base-spacing;
7 | margin-top: 0.25em;
8 | text-align: right;
9 | width: calc(20% - 1rem);
10 | }
11 |
12 | .preserve-whitespace {
13 | white-space: pre-wrap;
14 | word-wrap: break-word;
15 | }
16 |
17 | .attribute-data {
18 | float: left;
19 | margin-bottom: $base-spacing;
20 | margin-left: 2rem;
21 | width: calc(80% - 1rem);
22 | word-break: break-word;
23 | }
24 |
25 | .attribute--nested {
26 | border: $base-border;
27 | padding: $small-spacing;
28 | }
29 |
--------------------------------------------------------------------------------
/app/views/fields/date/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Date Form Partial
3 |
4 | This partial renders an input element for a date attribute.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate input fields.
10 | - `field`:
11 | An instance of [Administrate::Field::Date][1].
12 | A wrapper around the Date value pulled from the database.
13 |
14 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Date
15 | %>
16 |
17 |
18 | <%= f.label field.attribute %>
19 |
20 |
21 | <%= f.date_field field.attribute %>
22 |
23 |
--------------------------------------------------------------------------------
/lib/administrate/not_authorized_error.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | class NotAuthorizedError < StandardError
3 | def initialize(action:, resource:)
4 | @action = action
5 | @resource = resource
6 |
7 | case resource
8 | when String, Symbol
9 | super("Not allowed to perform #{action.inspect} on #{resource.inspect}")
10 | when Module
11 | super("Not allowed to perform #{action.inspect} on #{resource.name}")
12 | else
13 | super(
14 | "Not allowed to perform #{action.inspect} on the given " +
15 | resource.class.name
16 | )
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/features/log_entries_show_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | feature "log entry show page" do
4 | scenario "displays logeable information" do
5 | log_entry = create(:log_entry)
6 |
7 | visit admin_log_entry_path(log_entry)
8 |
9 | expect(page).to have_content(log_entry.action)
10 | expect(page).to have_content(displayed(log_entry.logeable))
11 | end
12 |
13 | scenario "links to logeable", :js do
14 | log_entry = create(:log_entry)
15 |
16 | visit admin_log_entry_path(log_entry)
17 | click_on(displayed(log_entry.logeable))
18 |
19 | expect(page).to have_header(displayed(log_entry.logeable))
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/fields/time/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Time Form Partial
3 |
4 | This partial renders an input element for time attributes.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate input fields.
10 | - `field`:
11 | An instance of [Administrate::Field::Time][1].
12 | A wrapper around the tmie attributes pulled from the model.
13 |
14 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Time
15 | %>
16 |
17 |
18 | <%= f.label field.attribute %>
19 |
20 |
21 | <%= f.time_field field.attribute, step: 1 %>
22 |
23 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/host_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class HostDashboard < Administrate::BaseDashboard
4 | ATTRIBUTE_TYPES = {
5 | id: Field::Number,
6 | name: Field::String,
7 | created_at: Field::DateTime,
8 | updated_at: Field::DateTime
9 | }.freeze
10 |
11 | COLLECTION_ATTRIBUTES = %i[
12 | id
13 | name
14 | created_at
15 | updated_at
16 | ].freeze
17 |
18 | SHOW_PAGE_ATTRIBUTES = %i[
19 | id
20 | name
21 | created_at
22 | updated_at
23 | ].freeze
24 |
25 | FORM_ATTRIBUTES = %i[
26 | name
27 | ].freeze
28 |
29 | COLLECTION_FILTERS = {}.freeze
30 | end
31 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/url/_form_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 | require "administrate/field/url"
3 |
4 | describe "fields/url/_form", type: :view do
5 | it "provides the correct name for the field" do
6 | product = build(:product, image_url: nil)
7 | url = instance_double(
8 | "Administrate::Field::Url",
9 | attribute: :image_url,
10 | data: nil
11 | )
12 |
13 | fields model: product do |f|
14 | render(
15 | partial: "fields/url/form",
16 | locals: {field: url, f: f}
17 | )
18 | end
19 |
20 | expect(rendered).to have_css(%(input[type="url"][name="product[image_url]"]))
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/fields/url/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Url Form Partial
3 |
4 | This partial renders an input element for URL.
5 | By default, the input is a text box.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Url][1].
13 | A wrapper around the URL pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Url
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.url_field field.attribute %>
23 |
24 |
--------------------------------------------------------------------------------
/lib/administrate/field/date_time.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Field
5 | class DateTime < Base
6 | def date
7 | I18n.localize(
8 | data.in_time_zone(timezone).to_date,
9 | format: format
10 | )
11 | end
12 |
13 | def datetime
14 | I18n.localize(
15 | data.in_time_zone(timezone),
16 | format: format
17 | )
18 | end
19 |
20 | private
21 |
22 | def format
23 | options.fetch(:format, :default)
24 | end
25 |
26 | def timezone
27 | options.fetch(:timezone, ::Time.zone)
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/views/fields/text/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Text Form Partial
3 |
4 | This partial renders a textarea element for a text attribute.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate input fields.
10 | - `field`:
11 | An instance of [Administrate::Field::Text][1].
12 | A wrapper around the Text pulled from the database.
13 |
14 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Text
15 | %>
16 |
17 |
18 | <%= f.label field.attribute %>
19 |
20 |
21 | <%= f.text_area field.attribute, field.options.fetch(:input_options, {}) %>
22 |
23 |
--------------------------------------------------------------------------------
/app/views/fields/email/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Email Form Partial
3 |
4 | This partial renders an input element for email addresses.
5 | By default, the input is a text box.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Email][1].
13 | A wrapper around the email pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Email
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.email_field field.attribute %>
23 |
24 |
--------------------------------------------------------------------------------
/spec/example_app/spec/models/product_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Product do
4 | describe "validations" do
5 | it { should allow_value("http://example.com/foo/bar").for(:image_url) }
6 | it { should allow_value("https://example.com/foo/bar").for(:image_url) }
7 | it { should_not allow_value("ftp://example.com/foo/bar").for(:image_url) }
8 | end
9 |
10 | describe "#image_url" do
11 | it "is trimmed on save" do
12 | product = FactoryBot.create(
13 | :product,
14 | image_url: "\n https://example.com/foo/bar \n"
15 | )
16 | expect(product.image_url).to eq("https://example.com/foo/bar")
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/fields/date_time/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # DateTime Form Partial
3 |
4 | This partial renders an input element for a datetime attribute.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate input fields.
10 | - `field`:
11 | An instance of [Administrate::Field::DateTime][1].
12 | A wrapper around the DateTime value pulled from the database.
13 |
14 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/DateTime
15 | %>
16 |
17 |
18 | <%= f.label field.attribute %>
19 |
20 |
21 | <%= f.datetime_local_field field.attribute, step: 1 %>
22 |
23 |
--------------------------------------------------------------------------------
/spec/generators/views/navigation_generator_spec.rb:
--------------------------------------------------------------------------------
1 | require "support/generator_spec_helpers"
2 | require "generators/administrate/views/navigation_generator"
3 |
4 | describe Administrate::Generators::Views::NavigationGenerator, :generator do
5 | describe "administrate:views:navigation" do
6 | it "copies the navigation partial into the `admin/application` namespace" do
7 | expected_contents = contents_for_application_template("_navigation")
8 | generated_file = file("app/views/admin/application/_navigation.html.erb")
9 |
10 | run_generator []
11 |
12 | contents = File.read(generated_file)
13 | expect(contents).to eq(expected_contents)
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/fields/rich_text/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Rich Text Form Partial
3 |
4 | This partial renders a trix-editor element for ActionText::RichText attributes.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate trix-editor fields.
10 | - `field`:
11 | An instance of [Administrate::Field::RichText][1].
12 | A wrapper around the tmie attributes pulled from the model.
13 |
14 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/RichText
15 | %>
16 |
17 |
18 | <%= f.label field.attribute %>
19 |
20 |
21 | <%= f.rich_text_area(field.attribute) %>
22 |
23 |
--------------------------------------------------------------------------------
/app/views/fields/string/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # String Form Partial
3 |
4 | This partial renders an input element for a string attribute.
5 | By default, the input is a text field.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::String][1].
13 | A wrapper around the String pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/String
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.text_field field.attribute %>
23 |
24 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/customers_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class CustomersController < Admin::ApplicationController
3 | before_action :with_variant, only: %i[index]
4 |
5 | def become
6 | user_id = params[:id]
7 | if user_id == "admin"
8 | session.delete(:user_id)
9 | else
10 | session[:user_id] = user_id
11 | end
12 | redirect_back fallback_location: admin_root_url
13 | end
14 |
15 | private
16 |
17 | def scoped_resource
18 | Customer.where(hidden: false)
19 | end
20 |
21 | def with_variant
22 | if @current_user.admin?
23 | request.variant = :admin
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/features/form_errors_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | feature "form errors" do
4 | scenario "error messages for new resource" do
5 | visit new_admin_customer_path
6 | fill_in("Name", with: "")
7 | click_on "Create Customer"
8 |
9 | expect(page).to have_content "Name can't be blank"
10 | expect(page).to have_content "prohibited this Customer from being saved"
11 | end
12 |
13 | scenario "error messages for editing resource" do
14 | customer = create(:customer)
15 |
16 | visit edit_admin_customer_path(customer)
17 | fill_in("Name", with: "")
18 | click_on "Update Customer"
19 |
20 | expect(page).to have_content "Name can't be blank"
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/controllers/application.js:
--------------------------------------------------------------------------------
1 | import { Application } from "@hotwired/stimulus";
2 |
3 | const application = Application.start();
4 |
5 | // Configure Stimulus development experience
6 | application.debug = false;
7 | window.Stimulus = application;
8 |
9 | // Workaround for Stimulus controllers not being properly disconnected
10 | // https://github.com/hotwired/stimulus/issues/104#issuecomment-365393601
11 | document.addEventListener('turbo:before-cache', function() {
12 | application.controllers.forEach(function(controller){
13 | if(typeof controller.teardown === 'function') {
14 | controller.teardown();
15 | }
16 | });
17 | });
18 |
19 | export { application };
20 |
--------------------------------------------------------------------------------
/app/views/fields/boolean/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Boolean Form Partial
3 |
4 | This partial renders an input element for a boolean attribute.
5 | By default, the input is a checkbox.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Boolean][1].
13 | A wrapper around the boolean value pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Boolean
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.check_box field.attribute %>
23 |
24 |
--------------------------------------------------------------------------------
/app/views/fields/number/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Number Form Partial
3 |
4 | This partial renders an input element for number attributes.
5 | By default, the input is a text field.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Number][1].
13 | A wrapper around the number pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Number
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.number_field field.attribute, step: "any" %>
23 |
24 |
--------------------------------------------------------------------------------
/app/views/fields/password/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Password Form Partial
3 |
4 | This partial renders an input element for a password attribute.
5 | By default, the input is a password field.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Password][1].
13 | A wrapper around the Password.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Password
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%= f.password_field field.attribute, value: field.data %>
23 |
24 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/controllers/table_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus";
2 | import $ from "jquery";
3 |
4 | var keycodes = { space: 32, enter: 13 };
5 |
6 | export default class extends Controller {
7 | visitDataUrl(event) {
8 | if (event.type == "click" ||
9 | event.keyCode == keycodes.space ||
10 | event.keyCode == keycodes.enter) {
11 |
12 | if (event.target.href) {
13 | return;
14 | }
15 |
16 | var dataUrl = $(event.target).closest("tr").data("url");
17 | var selection = window.getSelection().toString();
18 | if (selection.length === 0 && dataUrl) {
19 | Turbo.visit(dataUrl);
20 | }
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | EXAMPLE_APP_PATH="spec/example_app"
6 | SAMPLE_DB_CONFIG_PATH="$EXAMPLE_APP_PATH/config/database.yml.sample"
7 | DB_CONFIG_PATH="$EXAMPLE_APP_PATH/config/database.yml"
8 | if [ ! -e "$DB_CONFIG_PATH" ]; then
9 | cp "$SAMPLE_DB_CONFIG_PATH" "$DB_CONFIG_PATH"
10 | echo "A new database config file was created at $DB_CONFIG_PATH."
11 | echo "Please edit it to match your DB settings and then run this script again."
12 | exit 0
13 | fi
14 |
15 | gem install bundler --conservative
16 | bundle install
17 | bundle exec appraisal install
18 | yarn install
19 |
20 | if [ ! -f .env ]; then
21 | cp .sample.env .env
22 | fi
23 |
24 | bin/rails log:clear tmp:clear
25 | bin/rails dev:prime
26 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_main-content.scss:
--------------------------------------------------------------------------------
1 | .main-content {
2 | background-color: $white;
3 | border-radius: $base-border-radius;
4 | box-shadow: 0 0 6px 0 rgba($black, 0.12),
5 | 0 2px 2px rgba($black, 0.2);
6 | flex: 1 1 100%;
7 | min-width: 800px;
8 | padding-bottom: 10vh;
9 | }
10 |
11 | .main-content__header,
12 | .main-content__body {
13 | padding: 1rem 2rem;
14 | }
15 |
16 | .main-content__body--flush {
17 | padding-left: 0;
18 | padding-right: 0;
19 | }
20 |
21 | .main-content__header {
22 | align-items: center;
23 | border-bottom: $base-border;
24 | display: flex;
25 | }
26 |
27 | .main-content__page-title {
28 | font-size: 1.6em;
29 | margin-right: auto;
30 | }
31 |
--------------------------------------------------------------------------------
/app/views/fields/has_one/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # HasOne Index Partial
3 |
4 | This partial renders a has_one relationship,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the relationship is rendered as a link to the associated object.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::HasOne][1].
13 | A wrapper around the has_one relationship pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/HasOne
16 | %>
17 |
18 | <% if field.linkable? %>
19 | <%= link_to_if(
20 | accessible_action?(field.data, :show),
21 | field.display_associated_resource,
22 | [namespace, field.data],
23 | ) %>
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 | # Security Policy
3 |
4 | ## Supported Versions
5 |
6 | Only the the latest version of this project is supported at a given time. If
7 | you find a security issue with an older version, please try updating to the
8 | latest version first.
9 |
10 | If for some reason you can't update to the latest version, please let us know
11 | your reasons so that we can have a better understanding of your situation.
12 |
13 | ## Reporting a Vulnerability
14 |
15 | For security inquiries or vulnerability reports, visit
16 | .
17 |
18 | If you have any suggestions to improve this policy, visit .
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spec/example_app/app/models/customer.rb:
--------------------------------------------------------------------------------
1 | class Customer < ApplicationRecord
2 | has_many :orders, dependent: :destroy
3 | belongs_to(
4 | :territory,
5 | class_name: "Country",
6 | foreign_key: :country_code,
7 | primary_key: :code
8 | )
9 | has_many :log_entries, as: :logeable
10 |
11 | validates :name, presence: true
12 | validates :email, presence: true
13 |
14 | if Rails.gem_version >= Gem::Version.new("7.0")
15 | enum :kind, {"standard" => "kind:std", "vip" => "kind:vip"}
16 | else
17 | enum kind: {"standard" => "kind:std", "vip" => "kind:vip"}
18 | end
19 |
20 | def admin?
21 | false
22 | end
23 |
24 | def lifetime_value
25 | orders.map(&:total_price).reduce(0, :+)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/index_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class IndexGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_template
10 | copy_resource_template("index")
11 | copy_resource_template("_collection")
12 | copy_resource_template("_collection_header_actions")
13 | copy_resource_template("_collection_item_actions")
14 | copy_resource_template("_index_header")
15 | copy_resource_template("_pagination")
16 | copy_resource_template("_search")
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/example_app/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, "\\1en"
8 | # inflect.singular /^(ox)en/i, "\\1"
9 | # inflect.irregular "person", "people"
10 | inflect.uncountable %w[series]
11 | end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym "RESTful"
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/blog/tag_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | module Blog
4 | class TagDashboard < Administrate::BaseDashboard
5 | ATTRIBUTE_TYPES = {
6 | id: Field::Number,
7 | name: Field::String,
8 | created_at: Field::DateTime,
9 | updated_at: Field::DateTime,
10 | posts: Field::HasMany
11 | }.freeze
12 |
13 | COLLECTION_ATTRIBUTES = %i[
14 | id
15 | name
16 | posts
17 | created_at
18 | ].freeze
19 |
20 | FORM_ATTRIBUTES = %i[
21 | name
22 | posts
23 | ].freeze
24 |
25 | SHOW_PAGE_ATTRIBUTES = COLLECTION_ATTRIBUTES
26 |
27 | def display_resource(resource)
28 | resource.name
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/page_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class PageDashboard < Administrate::BaseDashboard
4 | ATTRIBUTE_TYPES = {
5 | product: Field::BelongsTo,
6 | id: Field::Number,
7 | title: Field::String,
8 | body: Field::Text,
9 | created_at: Field::DateTime,
10 | updated_at: Field::DateTime
11 | }.freeze
12 |
13 | COLLECTION_ATTRIBUTES = %i[
14 | id
15 | title
16 | ].freeze
17 |
18 | SHOW_PAGE_ATTRIBUTES = %i[
19 | product
20 | id
21 | title
22 | body
23 | created_at
24 | updated_at
25 | ].freeze
26 |
27 | FORM_ATTRIBUTES = %i[
28 | product
29 | title
30 | body
31 | ].freeze
32 |
33 | COLLECTION_FILTERS = {}.freeze
34 | end
35 |
--------------------------------------------------------------------------------
/app/views/fields/has_many/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # HasMany Index Partial
3 |
4 | This partial renders a has_many relationship,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the relationship is rendered
8 | as a count of how many objects are associated through the relationship.
9 |
10 | ## Local variables:
11 |
12 | - `field`:
13 | An instance of [Administrate::Field::HasMany][1].
14 | A wrapper around the has_many relationship pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/HasMany
17 | %>
18 |
19 | <%= pluralize(field.data.size, t("activerecord.models.#{field.attribute.to_s.singularize}", default: field.attribute.to_s.humanize.downcase.singularize, count: field.data.size)) %>
20 |
--------------------------------------------------------------------------------
/app/views/fields/polymorphic/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Polymorphic Index Partial
3 |
4 | This partial renders a polymorphic relationship,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the relationship is rendered as a link to the associated object.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Polymorphic][1].
13 | A wrapper around the polymorphic belongs_to relationship
14 | pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
17 | %>
18 |
19 | <% if field.data %>
20 | <%= link_to_if(
21 | accessible_action?(field.data, :show),
22 | field.display_associated_resource,
23 | [namespace, field.data]
24 | ) %>
25 | <% end %>
26 |
--------------------------------------------------------------------------------
/app/assets/javascripts/administrate/controllers/tooltip_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus";
2 |
3 | export default class extends Controller {
4 | static targets = ["popover", "tooltip"];
5 |
6 | connect() {
7 | this.tooltipTarget.addEventListener("mouseenter", this.showPopover);
8 | this.tooltipTarget.addEventListener("mouseleave", this.hidePopover);
9 | }
10 |
11 | disconnect() {
12 | this.tooltipTarget.removeEventListener("mouseenter", this.showPopover);
13 | this.tooltipTarget.removeEventListener("mouseleave", this.hidePopover);
14 | }
15 |
16 | showPopover = () => {
17 | this.popoverTarget.showPopover();
18 | }
19 |
20 | hidePopover = () => {
21 | this.popoverTarget.hidePopover();
22 | }
23 | };
24 |
25 |
--------------------------------------------------------------------------------
/spec/lib/pages/show_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe Administrate::Page::Show do
4 | describe "#page_title" do
5 | it "is the stringified resource" do
6 | customer = double(name: "Worf")
7 | page = Administrate::Page::Show.new(CustomerDashboard.new, customer)
8 |
9 | expect(page.page_title).to eq("Worf")
10 | end
11 | end
12 |
13 | describe "#attributes" do
14 | it "passes the resource to the field object" do
15 | customer = double(name: "Worf").as_null_object
16 | page = Administrate::Page::Show.new(CustomerDashboard.new, customer)
17 |
18 | expect(page.attributes[""].first.resource).to eq(customer)
19 | expect(page.attributes[""].first.resource.name).to eq("Worf")
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | !.keep
2 | *.DS_Store
3 | *.swo
4 | *.swp
5 | /.bundle
6 | /.env
7 | /.foreman
8 | /coverage/*
9 | /db/*.sqlite3
10 |
11 | /log/*
12 | /tmp/*
13 | !/log/.keep
14 | !/tmp/.keep
15 |
16 | /spec/example_app/log/*
17 | /spec/example_app/tmp/*
18 | !/spec/example_app/log/.keep
19 | !/spec/example_app/tmp/.keep
20 | /spec/example_app/public/system
21 | /spec/example_app/public/assets
22 |
23 | /spec/example_app/app/assets/builds/*
24 | !/spec/example_app/app/assets/builds/.keep
25 |
26 | /spec/example_app/node_modules
27 | /spec/example_app/config/database.yml
28 | /spec/example_app/storage
29 |
30 | /node_modules
31 | /public
32 |
33 | /tags
34 | pkg
35 | *.ipr
36 | *.iws
37 | *.iml
38 | /.idea
39 | .byebug_history
40 | gemfiles/.bundle/
41 | .yardoc
42 | /doc
43 | *.gem
44 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_collection_item_actions.html.erb:
--------------------------------------------------------------------------------
1 | <% if existing_action?(collection_presenter.resource_name, :edit) %>
2 | <%= link_to(
3 | t("administrate.actions.edit"),
4 | [:edit, namespace, resource],
5 | class: "action-edit",
6 | ) if accessible_action?(resource, :edit) %>
7 | <% end %>
8 |
9 | <% if existing_action?(collection_presenter.resource_name, :destroy) %>
10 | <%= button_to(
11 | t("administrate.actions.destroy"),
12 | [namespace, resource],
13 | class: "link link--danger",
14 | method: :delete,
15 | data: { turbo_confirm: t("administrate.actions.confirm") }
16 | ) if accessible_action?(resource, :destroy) %>
17 | <% end %>
18 |
--------------------------------------------------------------------------------
/spec/support/table.rb:
--------------------------------------------------------------------------------
1 | module Features
2 | def click_row_for(model)
3 | within(row_css_for(model)) do
4 | all(clickable_table_elements).first.click
5 | end
6 | end
7 |
8 | def click_show_link_for(model)
9 | within(row_css_for(model)) do
10 | all(show_link_elements).first.click
11 | end
12 | end
13 |
14 | private
15 |
16 | def row_css_for(model)
17 | "tr[data-url='#{url_for(model)}']"
18 | end
19 |
20 | def clickable_table_elements
21 | ".cell-data--string, .cell-data--number"
22 | end
23 |
24 | def show_link_elements
25 | ".action-show"
26 | end
27 |
28 | def url_for(model)
29 | "/" + [
30 | :admin,
31 | model.class.to_s.underscore.pluralize,
32 | model.to_param
33 | ].join("/")
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require "bundler/setup"
3 | rescue LoadError
4 | puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5 | end
6 |
7 | require "rdoc/task"
8 |
9 | require File.expand_path("../spec/example_app/config/application", __FILE__)
10 |
11 | RDoc::Task.new(:rdoc) do |rdoc|
12 | rdoc.rdoc_dir = "rdoc"
13 | rdoc.title = "Administrate"
14 | rdoc.options << "--line-numbers"
15 | rdoc.rdoc_files.include("README.rdoc")
16 | rdoc.rdoc_files.include("lib/**/*.rb")
17 | end
18 |
19 | Bundler::GemHelper.install_tasks
20 |
21 | Rails.application.load_tasks
22 | task(:default).clear
23 | task default: [:spec]
24 |
25 | if defined? RSpec
26 | task(:spec).clear
27 | RSpec::Core::RakeTask.new(:spec) do |t|
28 | t.verbose = false
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "administrate",
3 | "scripts": {
4 | "postdeploy": "bundle exec rake db:schema:load db:seed"
5 | },
6 | "env": {
7 | "LANG": {
8 | "required": true
9 | },
10 | "RACK_ENV": {
11 | "required": true
12 | },
13 | "RAILS_ENV": {
14 | "required": true
15 | },
16 | "RAILS_LOG_TO_STDOUT": {
17 | "required": true
18 | },
19 | "RAILS_SERVE_STATIC_FILES": {
20 | "required": true
21 | },
22 | "SECRET_KEY_BASE": {
23 | "required": true
24 | }
25 | },
26 | "formation": {
27 | "web": {
28 | "quantity": 1
29 | }
30 | },
31 | "addons": [
32 | "heroku-postgresql",
33 | "scheduler"
34 | ],
35 | "buildpacks": [
36 | {
37 | "url": "heroku/ruby"
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/config/locales/administrate.zh-CN.yml:
--------------------------------------------------------------------------------
1 | ---
2 | zh-CN:
3 | administrate:
4 | actions:
5 | confirm: 确定?
6 | destroy: 删除
7 | edit: 编辑
8 | edit_resource: 编辑 %{name}
9 | show_resource: 查看 %{name}
10 | new_resource: 新建 %{name}
11 | back: 返回
12 | controller:
13 | create:
14 | success: "%{resource} 创建成功."
15 | destroy:
16 | success: "%{resource} 删除成功."
17 | update:
18 | success: "%{resource} 更新成功."
19 | fields:
20 | has_many:
21 | more: 显示所有 %{total_count} 中 %{count} 条
22 | none: 无
23 | form:
24 | error: 错误
25 | errors: "%{resource_name} 保存前出现了 %{pluralized_errors} 个错误:"
26 | navigation:
27 | back_to_app: 返回应用
28 | search:
29 | clear: 清除搜索
30 | label: 搜索 %{resource}
31 |
--------------------------------------------------------------------------------
/lib/generators/administrate/views/layout_generator.rb:
--------------------------------------------------------------------------------
1 | require "administrate/view_generator"
2 |
3 | module Administrate
4 | module Generators
5 | module Views
6 | class LayoutGenerator < Administrate::ViewGenerator
7 | source_root template_source_path
8 |
9 | def copy_template
10 | copy_file(
11 | "../../layouts/administrate/application.html.erb",
12 | "app/views/layouts/#{namespace}/application.html.erb"
13 | )
14 |
15 | call_generator("administrate:views:navigation")
16 | copy_resource_template("_stylesheet")
17 | copy_resource_template("_javascript")
18 | copy_resource_template("_flashes")
19 | copy_resource_template("_icons")
20 | end
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/config/locales/administrate.zh-TW.yml:
--------------------------------------------------------------------------------
1 | ---
2 | zh-TW:
3 | administrate:
4 | actions:
5 | confirm: 確定?
6 | destroy: 刪除
7 | edit: 編輯
8 | edit_resource: 編輯 %{name}
9 | show_resource: 檢視 %{name}
10 | new_resource: 新增 %{name}
11 | back: 返回
12 | controller:
13 | create:
14 | success: "已成功新增 %{resource}。"
15 | destroy:
16 | success: "已成功刪除 %{resource}。"
17 | update:
18 | success: "已成功更新 %{resource}。"
19 | fields:
20 | has_many:
21 | more: 顯示 %{total_count} 筆中的 %{count} 筆資料
22 | none: 無
23 | form:
24 | error: 錯誤
25 | errors: "%{pluralized_errors} 導致此 %{resource_name} 不能被儲存:"
26 | navigation:
27 | back_to_app: 返回首頁
28 | search:
29 | clear: 清除搜尋
30 | label: 搜尋 %{resource}
31 |
--------------------------------------------------------------------------------
/lib/administrate/page/show.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Page
5 | class Show < Page::Base
6 | def initialize(dashboard, resource)
7 | super(dashboard)
8 | @resource = resource
9 | end
10 |
11 | attr_reader :resource
12 |
13 | def page_title
14 | dashboard.display_resource(resource)
15 | end
16 |
17 | def attributes
18 | attributes = dashboard.show_page_attributes
19 |
20 | if attributes.is_a? Array
21 | attributes = {"" => attributes}
22 | end
23 |
24 | attributes.transform_values do |attrs|
25 | attrs.map do |attr_name|
26 | attribute_field(dashboard, resource, attr_name, :show)
27 | end
28 | end
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/.github/workflows/diff-check.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: diff-check
3 | on: [push]
4 |
5 | jobs:
6 | appraisal:
7 | runs-on: ubuntu-latest
8 | env:
9 | DIFF_CHECK_APPRAISAL: "true"
10 | steps:
11 | - uses: actions/checkout@v6
12 | - uses: ruby/setup-ruby@v1
13 | - run: bundle install
14 | - uses: nickcharlton/diff-check@main
15 | with:
16 | command: bundle exec appraisal
17 |
18 | builds:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v6
22 | - name: Set up JS
23 | uses: actions/setup-node@v6
24 | with:
25 | cache: yarn
26 | - run: yarn install --frozen-lockfile
27 | - uses: nickcharlton/diff-check@main
28 | with:
29 | command: |
30 | yarn build
31 | yarn build:css
32 |
--------------------------------------------------------------------------------
/app/views/fields/belongs_to/_index.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # BelongsTo Index Partial
3 |
4 | This partial renders a belongs_to relationship,
5 | to be displayed on a resource's index page.
6 |
7 | By default, the relationship is rendered as a link to the associated object.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::BelongsTo][1].
13 | A wrapper around the belongs_to relationship pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
16 | %>
17 |
18 | <% if field.data %>
19 | <% if accessible_action?(field.data, :show) %>
20 | <%= link_to(
21 | field.display_associated_resource,
22 | [namespace, field.data],
23 | ) %>
24 | <% else %>
25 | <%= field.display_associated_resource %>
26 | <% end %>
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/app/views/fields/belongs_to/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # BelongsTo Show Partial
3 |
4 | This partial renders a belongs_to relationship,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the relationship is rendered as a link to the associated object.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::BelongsTo][1].
13 | A wrapper around the belongs_to relationship pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
16 | %>
17 |
18 | <% if field.data %>
19 | <% if accessible_action?(field.data, :show) %>
20 | <%= link_to(
21 | field.display_associated_resource,
22 | [namespace, field.data],
23 | ) %>
24 | <% else %>
25 | <%= field.display_associated_resource %>
26 | <% end %>
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/lib/generators/administrate/field/field_generator.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | module Generators
3 | class FieldGenerator < Rails::Generators::NamedBase
4 | source_root File.expand_path("../templates", __FILE__)
5 |
6 | def template_field_object
7 | template(
8 | "field_object.rb.erb",
9 | "app/fields/#{file_name}_field.rb"
10 | )
11 | end
12 |
13 | def copy_partials
14 | copy_partial(:show)
15 | copy_partial(:index)
16 | copy_partial(:form)
17 | end
18 |
19 | private
20 |
21 | def copy_partial(partial_name)
22 | partial = "_#{partial_name}.html.erb"
23 |
24 | copy_file(
25 | partial,
26 | "app/views/fields/#{file_name}_field/#{partial}"
27 | )
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/config/locales/administrate.ja.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ja:
3 | administrate:
4 | actions:
5 | confirm: 本当によろしいですか?
6 | destroy: 削除
7 | edit: 編集
8 | edit_resource: "%{name}を編集"
9 | show_resource: "%{name}を参照"
10 | new_resource: "%{name}を作成"
11 | back: 戻る
12 | controller:
13 | create:
14 | success: "%{resource}を作成しました。"
15 | destroy:
16 | success: "%{resource}を削除しました。"
17 | update:
18 | success: "%{resource}を更新しました。"
19 | fields:
20 | has_many:
21 | more: "%{total_count} 件中 %{count} 件表示"
22 | none: データがありません
23 | form:
24 | error: エラー
25 | errors: "%{pluralized_errors}のため%{resource_name}を保存できませんでした。"
26 | navigation:
27 | back_to_app: アプリに戻る
28 | search:
29 | clear: 検索をクリアする
30 | label: "%{resource}を検索"
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.ko.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ko:
3 | administrate:
4 | actions:
5 | confirm: 괜찮습니까?
6 | destroy: 삭제
7 | edit: 수정
8 | edit_resource: 수정 %{name}
9 | show_resource: "%{name}"
10 | new_resource: 새 %{name}
11 | back: 뒤로
12 | controller:
13 | create:
14 | success: "%{resource}가 성공적으로 생성되었습니다."
15 | destroy:
16 | success: "%{resource}가 성공적으로 삭제되었습니다."
17 | update:
18 | success: "%{resource}가 성공적으로 수정되었습니다."
19 | fields:
20 | has_many:
21 | more: "%{total_count}개 중에서 %{count}개"
22 | none: 없음
23 | form:
24 | error: 에러
25 | errors: "%{pluralized_errors}로 인하여 %{resource_name}을(를) 저장하는데 실패하였습니다."
26 | navigation:
27 | back_to_app: 앱으로 돌아 가기
28 | search:
29 | clear: 검색 초기화
30 | label: "%{resource} 검색"
31 |
--------------------------------------------------------------------------------
/app/views/fields/polymorphic/_show.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Polymorphic Show Partial
3 |
4 | This partial renders a polymorphic relationship,
5 | to be displayed on a resource's show page.
6 |
7 | By default, the relationship is rendered as a link to the associated object.
8 |
9 | ## Local variables:
10 |
11 | - `field`:
12 | An instance of [Administrate::Field::Polymorphic][1].
13 | A wrapper around the polymorphic belongs_to relationship
14 | pulled from the database.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
17 | %>
18 |
19 | <% if field.data %>
20 | <% if accessible_action?(field.data, :show) %>
21 | <%= link_to(
22 | field.display_associated_resource,
23 | [namespace, field.data],
24 | ) %>
25 | <% else %>
26 | <%= field.display_associated_resource %>
27 | <% end %>
28 | <% end %>
29 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/has_many/_form_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "fields/has_many/_form", type: :view do
4 | describe "the field label" do
5 | it "displays the association name" do
6 | has_many = double(
7 | attribute_key: :associated_object_ids,
8 | attribute: :associated_objects,
9 | html_controller: "select"
10 | )
11 |
12 | render(
13 | partial: "fields/has_many/form",
14 | locals: {f: fake_form_builder, field: has_many}
15 | )
16 |
17 | expect(rendered).to include("Associated objects")
18 | end
19 | end
20 |
21 | def fake_form_builder
22 | double("Form Builder").as_null_object.tap do |form_builder|
23 | allow(form_builder).to receive(:label) do |*args|
24 | args.first.to_s.humanize
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/example_app/app/policies/application_policy.rb:
--------------------------------------------------------------------------------
1 | class ApplicationPolicy
2 | attr_reader :user, :record
3 |
4 | def initialize(user, record)
5 | @user = user
6 | @record = record
7 | end
8 |
9 | def index?
10 | true
11 | end
12 |
13 | def show?
14 | true
15 | end
16 |
17 | def create?
18 | true
19 | end
20 |
21 | def new?
22 | create?
23 | end
24 |
25 | def update?
26 | true
27 | end
28 |
29 | def edit?
30 | update?
31 | end
32 |
33 | def destroy?
34 | true
35 | end
36 |
37 | def scope
38 | Pundit.policy_scope!(user, record.class)
39 | end
40 |
41 | class Scope
42 | attr_reader :user, :scope
43 |
44 | def initialize(user, scope)
45 | @user = user
46 | @scope = scope
47 | end
48 |
49 | def resolve
50 | scope
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_navigation.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Navigation
3 |
4 | This partial is used to display the navigation in Administrate.
5 | By default, the navigation contains navigation links
6 | for all resources in the admin dashboard,
7 | as defined by the routes in the `admin/` namespace
8 | %>
9 |
10 |
11 | <%= link_to(t("administrate.navigation.back_to_app"), root_url, class: "button button--alt button--nav") if defined?(root_url) %>
12 |
13 | <% Administrate::Namespace.new(namespace).resources_with_index_route.each do |resource| %>
14 | <%= link_to(
15 | display_resource_name(resource),
16 | resource_index_route(resource),
17 | class: "navigation__link navigation__link--#{nav_link_state(resource)}"
18 | ) if accessible_action?(model_from_resource(resource), :index) %>
19 | <% end %>
20 |
21 |
--------------------------------------------------------------------------------
/lib/generators/administrate/install/templates/application_controller.rb.erb:
--------------------------------------------------------------------------------
1 | # All Administrate controllers inherit from this
2 | # `Administrate::ApplicationController`, making it the ideal place to put
3 | # authentication logic or other before_actions.
4 | #
5 | # If you want to add pagination or other controller-level concerns,
6 | # you're free to overwrite the RESTful controller actions.
7 | module <%= namespace.camelize %>
8 | class ApplicationController < Administrate::ApplicationController
9 | before_action :authenticate_admin
10 |
11 | def authenticate_admin
12 | # TODO Add authentication logic here.
13 | end
14 |
15 | # Override this value to specify the number of elements to display at a time
16 | # on index pages. Defaults to 20.
17 | # def records_per_page
18 | # params[:per_page] || 20
19 | # end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/polymorphic/_form_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "fields/polymorphic/_form", type: :view do
4 | it "displays the association name" do
5 | polymorphic = double(name: "parent").as_null_object
6 |
7 | render(
8 | partial: "fields/polymorphic/form",
9 | locals: {field: polymorphic, f: fake_form_builder}
10 | )
11 |
12 | expect(rendered).to include("Parent")
13 | end
14 |
15 | def fake_form_builder
16 | fields_builder = double("Fields Builder").as_null_object.tap do |fb|
17 | allow(fb).to receive(:label) do |*args|
18 | args.last
19 | end
20 | end
21 |
22 | double("Form Builder").as_null_object.tap do |form_builder|
23 | allow(form_builder).to receive(:fields_for) do |*_, &block|
24 | block.call(fields_builder)
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Lint
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | types: [opened, synchronize, reopened]
9 |
10 | jobs:
11 | standardrb:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | contents: read
15 | checks: write
16 | steps:
17 | - uses: actions/checkout@v6
18 | - name: StandardRB Linter
19 | uses: testdouble/standard-ruby-action@main
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 |
23 | stylelint:
24 | runs-on: ubuntu-latest
25 | steps:
26 | - uses: actions/checkout@v6
27 | - name: Set up JS
28 | uses: actions/setup-node@v6
29 | with:
30 | cache: yarn
31 | - name: Install JS dependencies
32 | run: yarn install
33 | - name: Run Stylelint
34 | run: yarn run lint:css
35 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_navigation.scss:
--------------------------------------------------------------------------------
1 | $navigation-link-padding: 0.6em;
2 |
3 | .navigation {
4 | flex: 1 0 10rem;
5 | padding: $base-spacing;
6 | padding-left: 0;
7 | }
8 |
9 | .navigation__link {
10 | background-color: transparent;
11 | color: $base-font-color;
12 | display: block;
13 | line-height: 1;
14 | margin-left: -($navigation-link-padding);
15 | padding: $navigation-link-padding;
16 | transition: background-color $base-duration $base-timing,
17 | color $base-duration $base-timing;
18 |
19 | &:not(:last-of-type) {
20 | margin-bottom: $small-spacing;
21 | }
22 |
23 | &:hover {
24 | background-color: mix($black, $base-background-color, 5%);
25 | border-radius: $base-border-radius;
26 | color: $base-font-color;
27 | }
28 | }
29 |
30 | .navigation__link--active {
31 | font-weight: $bold-font-weight;
32 | }
33 |
--------------------------------------------------------------------------------
/config/locales/administrate.sl.yml:
--------------------------------------------------------------------------------
1 | ---
2 | sl:
3 | administrate:
4 | actions:
5 | confirm: Ali ste preričani?
6 | destroy: Izbriši
7 | edit: Uredi
8 | edit_resource: Uredi %{name}
9 | show_resource: Prikaži %{name}
10 | new_resource: Dodaj %{name}
11 | back: Nazaj
12 | controller:
13 | create:
14 | success: "%{resource} je dodan."
15 | destroy:
16 | success: "%{resource} je izbrisan."
17 | update:
18 | success: "%{resource} je posodobljen."
19 | fields:
20 | has_many:
21 | more: Prikazanih %{count} od %{total_count}
22 | none: Nobene
23 | form:
24 | error: napaka
25 | errors: "%{resource_name} ni mogoče shraniti zaradi:"
26 | navigation:
27 | back_to_app: Nazaj v aplikacijo
28 | search:
29 | clear: Počisti iskanje
30 | label: Išči %{resource}
31 |
--------------------------------------------------------------------------------
/bin/release:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | VERSION=$(ruby -r ./lib/administrate/version.rb -e "puts Administrate::VERSION")
6 | echo "Version $VERSION"
7 |
8 | $(dirname "$0")/build-gem
9 |
10 | sed -i.bak "s/^gemspec$/gem \"administrate\", \"`echo $VERSION`\"/g" Gemfile
11 | rm Gemfile.bak
12 | bundle
13 |
14 | bundle exec rake
15 | bundle exec appraisal rake
16 |
17 | gem uninstall administrate -v $VERSION
18 | echo "Gem is verified and ready to go."
19 | gem push administrate-$VERSION.gem
20 |
21 | bundle
22 | bundle exec rake
23 | bundle exec appraisal rake
24 |
25 | echo "Administrate version $VERSION was released successfully."
26 |
27 | git checkout -- Gemfile*
28 |
29 | echo
30 | echo "Finish up by merging the branch and tagging the commit:"
31 | echo "bin/merge"
32 | echo "git tag v$VERSION"
33 | echo "git push --tags"
34 | echo "https://github.com/thoughtbot/administrate/releases/new"
35 |
--------------------------------------------------------------------------------
/spec/example_app/db/migrate/20241113130739_add_service_name_to_active_storage_blobs.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20190112182829)
2 | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[8.0]
3 | def up
4 | return unless table_exists?(:active_storage_blobs)
5 |
6 | unless column_exists?(:active_storage_blobs, :service_name)
7 | add_column :active_storage_blobs, :service_name, :string
8 |
9 | if (configured_service = ActiveStorage::Blob.service.name)
10 | ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
11 | end
12 |
13 | change_column :active_storage_blobs, :service_name, :string, null: false
14 | end
15 | end
16 |
17 | def down
18 | return unless table_exists?(:active_storage_blobs)
19 |
20 | remove_column :active_storage_blobs, :service_name
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/fields/select/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Select Form Partial
3 |
4 | This partial renders a selectable text attribute,
5 | to be displayed on a resource's edit form page.
6 |
7 | ## Local variables:
8 |
9 | - `f`:
10 | A Rails form generator, used to help create the appropriate input fields.
11 | - `field`:
12 | An instance of [Administrate::Field::Select][1].
13 | A wrapper around the attribute pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Select
16 | %>
17 |
18 |
19 | <%= f.label field.attribute %>
20 |
21 |
22 | <%=
23 | f.select(
24 | field.attribute,
25 | options_for_select(
26 | field.selectable_options,
27 | field.data,
28 | ),
29 | field.tag_options,
30 | field.html_options
31 | )
32 | %>
33 |
34 |
--------------------------------------------------------------------------------
/bin/merge:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 |
3 | CURRENT_BRANCH="$(git symbolic-ref HEAD --short)"
4 | BRANCH_NAME=${1-$CURRENT_BRANCH}
5 | echo "Merging branch: $BRANCH_NAME"
6 |
7 | git fetch &&\
8 | echo 'git checkout $BRANCH_NAME' &&\
9 | git checkout $BRANCH_NAME &&\
10 | echo 'git rebase -i origin/master' &&\
11 | git rebase -i origin/master &&\
12 | echo 'git push -f' &&\
13 | git push --force-with-lease &&\
14 | echo 'bundle exec rake' &&\
15 | bundle exec rake &&\
16 | appraisal rake &&\
17 | echo 'git checkout master' &&\
18 | git checkout master &&\
19 | echo 'git pull' &&\
20 | git pull &&\
21 | echo 'git merge --ff-only $BRANCH_NAME' &&\
22 | git merge --ff-only $BRANCH_NAME &&\
23 | echo 'git push' &&\
24 | git push &&\
25 | echo 'git branch -d $BRANCH_NAME ' &&\
26 | git branch -d $BRANCH_NAME && \
27 | echo 'git push -f origin :$BRANCH_NAME' &&\
28 | git push --force-with-lease origin :$BRANCH_NAME; \
29 |
--------------------------------------------------------------------------------
/lib/administrate/page/form.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Page
5 | class Form < Page::Base
6 | def initialize(dashboard, resource)
7 | super(dashboard)
8 | @resource = resource
9 | end
10 |
11 | attr_reader :resource
12 |
13 | def attributes(action = nil)
14 | attributes = dashboard.form_attributes(action)
15 |
16 | if attributes.is_a? Array
17 | attributes = {"" => attributes}
18 | end
19 |
20 | attributes.transform_values do |attrs|
21 | attrs.map do |attribute|
22 | attribute_field(dashboard, resource, attribute, :form)
23 | end
24 | end
25 | end
26 |
27 | def page_title
28 | dashboard.display_resource(resource)
29 | end
30 |
31 | private
32 |
33 | attr_reader :dashboard
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/application.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | @import "reset/normalize";
4 |
5 | @import "trix/dist/trix";
6 | @import "@selectize/selectize/dist/css/selectize";
7 | @import "@selectize/selectize/dist/css/selectize.default";
8 |
9 | @import "library/clearfix";
10 | @import "library/data-label";
11 | @import "library/variables";
12 |
13 | @import "base/forms";
14 | @import "base/layout";
15 | @import "base/lists";
16 | @import "base/tables";
17 | @import "base/typography";
18 |
19 | @import "components/app-container";
20 | @import "components/attributes";
21 | @import "components/buttons";
22 | @import "components/cells";
23 | @import "components/field-unit";
24 | @import "components/flashes";
25 | @import "components/form-actions";
26 | @import "components/main-content";
27 | @import "components/navigation";
28 | @import "components/pagination";
29 | @import "components/search";
30 |
--------------------------------------------------------------------------------
/config/locales/administrate.da.yml:
--------------------------------------------------------------------------------
1 | ---
2 | da:
3 | administrate:
4 | actions:
5 | confirm: Er du sikker?
6 | destroy: Slet
7 | edit: Rediger
8 | edit_resource: Rediger %{name}
9 | show_resource: Vis %{name}
10 | new_resource: Ny %{name}
11 | back: Tilbage
12 | controller:
13 | create:
14 | success: "%{resource} blev oprettet."
15 | destroy:
16 | success: "%{resource} er nu slettet."
17 | update:
18 | success: "%{resource} blev opdateret."
19 | fields:
20 | has_many:
21 | more: "Viser %{count} af %{total_count}"
22 | none: Ingen
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Tilbage til app
28 | search:
29 | clear: Ryd søgning
30 | label: Søg %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.sv.yml:
--------------------------------------------------------------------------------
1 | ---
2 | sv:
3 | administrate:
4 | actions:
5 | confirm: Är du säker?
6 | destroy: Ta bort
7 | edit: Ändra
8 | edit_resource: Ändra %{name}
9 | show_resource: Visa %{name}
10 | new_resource: Ny %{name}
11 | back: Tillbaka
12 | controller:
13 | create:
14 | success: "%{resource} har skapats."
15 | destroy:
16 | success: "%{resource} har tagits bort."
17 | update:
18 | success: "%{resource} har uppdaterats."
19 | fields:
20 | has_many:
21 | more: "%{count} av %{total_count}"
22 | none: Inga
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Tillbaka till appen
28 | search:
29 | clear: Rensa sökningen
30 | label: Sök %{resource}
31 |
--------------------------------------------------------------------------------
/spec/support/webdrivers.rb:
--------------------------------------------------------------------------------
1 | require "selenium/webdriver"
2 |
3 | Capybara.register_driver :chrome do |app|
4 | Capybara::Selenium::Driver.new(app, browser: :chrome)
5 | end
6 |
7 | Capybara.register_driver :headless_chrome do |app|
8 | options = ::Selenium::WebDriver::Chrome::Options.new
9 | options.add_argument "--headless=new"
10 | options.add_argument "--window-size=1680,1050"
11 | options.add_argument "--disable-gpu"
12 | options.add_argument "--disable-dev-shm-usage"
13 |
14 | Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
15 | end
16 |
17 | Capybara.javascript_driver = :headless_chrome
18 | Capybara.server = :webrick
19 |
20 | RSpec.configure do |config|
21 | config.before(:each, type: :system) do
22 | driven_by :rack_test
23 | end
24 |
25 | config.before(:each, type: :system, js: true) do
26 | driven_by Capybara.javascript_driver
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/blog/post_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | module Blog
4 | class PostDashboard < Administrate::BaseDashboard
5 | ATTRIBUTE_TYPES = {
6 | id: Field::Number,
7 | created_at: Field::DateTime,
8 | updated_at: Field::DateTime,
9 | title: Field::String,
10 | published_at: Field::DateTime,
11 | body: Field::Text,
12 | tags: Field::HasMany
13 | }
14 |
15 | READ_ONLY_ATTRIBUTES = [
16 | :id,
17 | :created_at,
18 | :updated_at
19 | ]
20 |
21 | COLLECTION_ATTRIBUTES = [
22 | :id,
23 | :title,
24 | :tags,
25 | :published_at
26 | ]
27 |
28 | FORM_ATTRIBUTES = ATTRIBUTE_TYPES.keys - READ_ONLY_ATTRIBUTES
29 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTE_TYPES.keys
30 |
31 | def display_resource(resource)
32 | resource.title
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/example_app/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | namespace :admin do
3 | resources :customers do
4 | member do
5 | get :become
6 | end
7 | end
8 | resources :line_items, except: [:index]
9 | resources :log_entries
10 | resources :orders
11 | resources :pages
12 | resources :products
13 | resources :product_meta_tags
14 | resources :payments, only: [:index, :show]
15 | resources :series
16 |
17 | namespace :blog do
18 | resources :posts
19 | resources :tags
20 | end
21 |
22 | resources :stats, only: [:index]
23 | resources :hosts, only: [:index]
24 |
25 | root to: "customers#index"
26 | end
27 |
28 | get "/files/receipts/*filename.txt", to: "files#download"
29 |
30 | get "/*page", to: "docs#show", constraints: ->(request) { !request.path.start_with?("/rails/") }
31 | root to: "docs#index"
32 | end
33 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/log_entries_controller.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class LogEntriesController < Admin::ApplicationController
3 | def filter_resources(resources, search_term:)
4 | return resources if search_term.blank?
5 |
6 | customer_ids = Customer.where(
7 | [
8 | "name ILIKE ?",
9 | "%#{search_term}%"
10 | ]
11 | ).pluck(:id)
12 | order_ids = Order.joins(:customer).where(
13 | [
14 | "customers.name ILIKE ?",
15 | "%#{search_term}%"
16 | ]
17 | ).pluck(:id)
18 |
19 | customers_filter = resources.where(
20 | logeable_type: "Customer",
21 | logeable_id: customer_ids
22 | )
23 | orders_filter = resources.where(
24 | logeable_type: "Order",
25 | logeable_id: order_ids
26 | )
27 | customers_filter.or(orders_filter)
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/rich_text/_index_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 | require "administrate/field/rich_text"
3 |
4 | describe "fields/rich_text/_index", type: :view do
5 | it "renders truncated plain text" do
6 | action_text = ActionText::RichText.new(body:
7 | "
8 |
AAAAAAAAAA
9 |
BBBBBBBBBB
10 |
")
11 | field = Administrate::Field::RichText.new(:document, action_text, :index)
12 |
13 | render(
14 | partial: "fields/rich_text/index",
15 | locals: {field: field, namespace: :admin}
16 | )
17 |
18 | expect(rendered).to_not have_css(%(div[class='trix-context']))
19 | expect(rendered).to have_text("AA")
20 | expect(rendered).to_not have_text("B")
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/administrate/application/_search.html.erb:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/config/locales/administrate.ar.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ar:
3 | administrate:
4 | actions:
5 | confirm: "هل أنت متأكد ؟"
6 | destroy: "حذف"
7 | edit: "تعديل"
8 | edit_resource: "تعديل %{name}"
9 | show_resource: "إظهار %{name}"
10 | new_resource: "جديد %{name}"
11 | back: "الى الخلف"
12 | controller:
13 | create:
14 | success: ".بنجاح %{resource} لقد تم إنشاء"
15 | destroy:
16 | success: ".بنجاح %{resource} لقد تم حذف"
17 | update:
18 | success: ".بنجاح %{resource} لقد تم تحديث"
19 | fields:
20 | has_many:
21 | more: إظهار %{count} من %{total_count}
22 | none: "لا يوجد"
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: العودة إلى التطبيق
28 | search:
29 | clear: مسح البحث
30 | label: بحث %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.en.yml:
--------------------------------------------------------------------------------
1 | ---
2 | en:
3 | administrate:
4 | actions:
5 | confirm: Are you sure?
6 | destroy: Destroy
7 | edit: Edit
8 | edit_resource: Edit %{name}
9 | show_resource: Show %{name}
10 | new_resource: New %{name}
11 | back: Back
12 | controller:
13 | create:
14 | success: "%{resource} was successfully created."
15 | destroy:
16 | success: "%{resource} was successfully destroyed."
17 | update:
18 | success: "%{resource} was successfully updated."
19 | fields:
20 | has_many:
21 | more: Showing %{count} of %{total_count}
22 | none: None
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Back to app
28 | search:
29 | clear: Clear search
30 | label: Search %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.nl.yml:
--------------------------------------------------------------------------------
1 | ---
2 | nl:
3 | administrate:
4 | actions:
5 | confirm: Weet u het zeker?
6 | destroy: Verwijder
7 | edit: Bewerk
8 | edit_resource: Bewerk %{name}
9 | show_resource: Toon %{name}
10 | new_resource: Nieuw %{name}
11 | back: Terug
12 | controller:
13 | create:
14 | success: "%{resource} is succesvol aangemaakt."
15 | destroy:
16 | success: "%{resource} is succesvol verwijderd."
17 | update:
18 | success: "%{resource} is succesvol geupdated."
19 | fields:
20 | has_many:
21 | more: Resultaat %{count} van %{total_count}
22 | none: Geen
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} maakten het onmogelijk %{resource_name} op:"
26 | navigation:
27 | back_to_app: Terug naar app
28 | search:
29 | clear: Wissen
30 | label: Zoeken %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.ru.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ru:
3 | administrate:
4 | actions:
5 | confirm: Вы уверены?
6 | destroy: Удалить
7 | edit: Редактировать
8 | edit_resource: Редактировать %{name}
9 | show_resource: Показать %{name}
10 | new_resource: Новый %{name}
11 | back: Вернуться назад
12 | controller:
13 | create:
14 | success: "%{resource} был успешно создан."
15 | destroy:
16 | success: "%{resource} был успешно удален."
17 | update:
18 | success: "%{resource} был успешно обновлен."
19 | fields:
20 | has_many:
21 | more: "%{count} из %{total_count}"
22 | none: Нет
23 | form:
24 | error: Ошибка
25 | errors: "При сохранении %{resource_name} произошли ошибки:"
26 | navigation:
27 | back_to_app: Вернуться в приложение
28 | search:
29 | clear: Очистить поиск
30 | label: Поиск %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.uk.yml:
--------------------------------------------------------------------------------
1 | ---
2 | uk:
3 | administrate:
4 | actions:
5 | confirm: Ви впевнені?
6 | destroy: Видалити
7 | edit: Редагувати
8 | edit_resource: Редагувати %{name}
9 | show_resource: Показати %{name}
10 | new_resource: Новий %{name}
11 | back: Назад
12 | controller:
13 | create:
14 | success: "Успішно створено %{resource}."
15 | destroy:
16 | success: "Успішно видалено %{resource}."
17 | update:
18 | success: "Успішно оновлено %{resource}."
19 | fields:
20 | has_many:
21 | more: "%{count} із %{total_count}"
22 | none: Немає
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Повернутися до програми
28 | search:
29 | clear: Очистити пошук
30 | label: пошук %{resource}
31 |
--------------------------------------------------------------------------------
/docs/using_administrate/stable_sorting.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Stable Sorting
3 | ---
4 |
5 | The display order of `index` pages and `HasMany` fields is controlled by `Administrate::Order`.
6 | By default, both are set to `nil`, so the model's default sort order is applied.
7 |
8 | You can toggle sorting by clicking on a table header attribute. When sorting by a specific attribute, a tiebreaker is used to ensure stable sorting.
9 |
10 | The tiebreaker uses the table's primary key.
11 | The generated SQL looks like this:
12 |
13 | ```sql
14 | -- When toggling the name attribute
15 | SELECT * FROM users ORDER BY name DESC, id DESC;
16 |
17 | -- When toggling the name attribute again
18 | SELECT * FROM users ORDER BY name ASC, id ASC;
19 | ```
20 |
21 | If there is no primary key (such as in a join table), the tiebreaker is not used.
22 |
23 | ```sql
24 | -- When toggling the name attribute
25 | SELECT * FROM users ORDER BY name DESC;
26 | ```
27 |
--------------------------------------------------------------------------------
/config/locales/administrate.vi.yml:
--------------------------------------------------------------------------------
1 | ---
2 | vi:
3 | administrate:
4 | actions:
5 | confirm: Bạn có chắc không?
6 | destroy: Xóa
7 | edit: Sửa
8 | edit_resource: Sửa %{name}
9 | show_resource: Xem %{name}
10 | new_resource: Mới %{name}
11 | back: Trở lại
12 | controller:
13 | create:
14 | success: "%{resource} đã được tạo thành công."
15 | destroy:
16 | success: "%{resource} đã được xóa thành công."
17 | update:
18 | success: "%{resource} đã được cập nhật thành công."
19 | fields:
20 | has_many:
21 | more: "%{count} trên %{total_count}"
22 | none: Không
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Quay lại ứng dụng
28 | search:
29 | clear: Tìm kiếm rõ ràng
30 | label: Tìm kiếm %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.bs.yml:
--------------------------------------------------------------------------------
1 | bs:
2 | administrate:
3 | actions:
4 | confirm: Jeste li sigurni?
5 | destroy: Izbrisati
6 | edit: Izmjena
7 | edit_resource: Izmjena %{name}
8 | show_resource: Pregled %{name}
9 | new_resource: Novi %{name}
10 | back: Nazad
11 | controller:
12 | create:
13 | success: "%{resource} je uspješno kreiran."
14 | destroy:
15 | success: "%{resource} je uspješno izbrisan."
16 | update:
17 | success: "%{resource} je uspješno izmijenjen."
18 | fields:
19 | has_many:
20 | more: Prikazuje %{count} od %{total_count}
21 | none: Niko
22 | form:
23 | error: error
24 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
25 | navigation:
26 | back_to_app: Natrag na aplikaciju
27 | search:
28 | clear: Izbriši pretraživanje
29 | label: Pretraga %{resource}
30 |
--------------------------------------------------------------------------------
/config/locales/administrate.ca.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ca:
3 | administrate:
4 | actions:
5 | confirm: Estàs segur?
6 | destroy: Destruir
7 | edit: Editar
8 | edit_resource: Edita %{name}
9 | show_resource: Mostra %{name}
10 | new_resource: Nou %{name}
11 | back: Tornar
12 | controller:
13 | create:
14 | success: "%{resource} s'ha creat amb èxit."
15 | destroy:
16 | success: "%{resource} s'ha destruït amb èxit."
17 | update:
18 | success: "%{resource} s'ha actualitzat amb èxit."
19 | fields:
20 | has_many:
21 | more: Mostrant %{count} de %{total_count}
22 | none: Cap
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} han impedit que %{resource_name} es guardés amb èxit:"
26 | navigation:
27 | back_to_app: Torna a l'aplicació
28 | search:
29 | clear: Esborrar la cerca
30 | label: Cerca %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.fi.yml:
--------------------------------------------------------------------------------
1 | ---
2 | fi:
3 | administrate:
4 | actions:
5 | confirm: Oletko varma?
6 | destroy: Poista
7 | edit: Muokkaa
8 | edit_resource: Muokkaa %{name}
9 | show_resource: Näytä %{name}
10 | new_resource: Uusi %{name}
11 | back: Takaisin
12 | controller:
13 | create:
14 | success: "%{resource} luotiin onnistuneesti."
15 | destroy:
16 | success: "%{resource} poistettiin onnistuneesti."
17 | update:
18 | success: "%{resource} päivitettiin onnistuneesti."
19 | fields:
20 | has_many:
21 | more: Näytetään %{count}/%{total_count}
22 | none: Ei yhtään
23 | form:
24 | error: virhe
25 | errors: "%{pluralized_errors} estivät tätä %{resource_name} tallentumasta:"
26 | navigation:
27 | back_to_app: Takaisin sovellukseen
28 | search:
29 | clear: Tyhjennä haku
30 | label: Etsi %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.sq.yml:
--------------------------------------------------------------------------------
1 | ---
2 | sq:
3 | administrate:
4 | actions:
5 | confirm: A jeni te sigurtë?
6 | destroy: Fshij
7 | edit: Ndrysho
8 | edit_resource: Ndrysho %{name}
9 | show_resource: Trego %{name}
10 | new_resource: Të re %{name}
11 | back: Prapa
12 | controller:
13 | create:
14 | success: "%{resource} është krijuar me sukses."
15 | destroy:
16 | success: "%{resource} është fshirë me sukses."
17 | update:
18 | success: "%{resource} është azhurnuar me sukses."
19 | fields:
20 | has_many:
21 | more: Duke treguar %{count} nga %{total_count}
22 | none: Asnjë
23 | form:
24 | error: gabim
25 | errors: "%{pluralized_errors} nuk e lejoj %{resource_name} të ruhet:"
26 | navigation:
27 | back_to_app: Kthehu tek aplikacioni
28 | search:
29 | clear: Pastro kërkimin
30 | label: Kërko %{resource}
31 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/log_entry_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class LogEntryDashboard < Administrate::BaseDashboard
4 | ATTRIBUTES = %i[action logeable].freeze
5 |
6 | ATTRIBUTE_TYPES = {
7 | id: Field::Number,
8 | action: Field::String,
9 | logeable: Field::Polymorphic.with_options(classes: [Customer, ::Order])
10 | }.freeze
11 |
12 | COLLECTION_ATTRIBUTES = [:id] + ATTRIBUTES
13 | FORM_ATTRIBUTES = ATTRIBUTES
14 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTES
15 |
16 | def display_resource(resource)
17 | "#{resource.action}[#{safe_display_logeable(resource.logeable)}]"
18 | end
19 |
20 | private
21 |
22 | def safe_display_logeable(logeable)
23 | logeable ? display_logeable(logeable) : ""
24 | end
25 |
26 | def display_logeable(logeable)
27 | (logeable.class.to_s + "Dashboard")
28 | .constantize
29 | .new
30 | .display_resource(logeable)
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/administrate/namespace.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | class Namespace
3 | def initialize(namespace)
4 | @namespace = namespace.to_sym
5 | end
6 |
7 | def resources
8 | @resources ||= routes.map(&:first).uniq.map do |path|
9 | Resource.new(namespace, path)
10 | end
11 | end
12 |
13 | def routes
14 | @routes ||= begin
15 | prefix = "#{namespace}/".freeze
16 | Rails.application.routes.routes.filter_map do |route|
17 | next unless route.defaults[:controller]&.start_with?(prefix)
18 |
19 | [
20 | route.defaults[:controller].delete_prefix(prefix),
21 | route.defaults[:action]
22 | ]
23 | end
24 | end
25 | end
26 |
27 | def resources_with_index_route
28 | routes.select { |_resource, route| route == "index" }.map(&:first).uniq
29 | end
30 |
31 | private
32 |
33 | attr_reader :namespace
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/time/_index_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe "fields/time/_index", type: :view do
4 | context "time value is nil" do
5 | it "display nothing" do
6 | time = double(data: nil)
7 |
8 | render(
9 | partial: "fields/time/index",
10 | locals: {field: time}
11 | )
12 |
13 | expect(rendered.strip).to eq("")
14 | end
15 | end
16 |
17 | context "time value is set" do
18 | it "renders time" do
19 | example_time = "12:34:00"
20 | customer = create(:customer, example_time: example_time)
21 | time = instance_double(
22 | "Administrate::Field::Time",
23 | data: customer.example_time,
24 | time: "12:34PM"
25 | )
26 | render(
27 | partial: "fields/time/index",
28 | locals: {field: time, namespace: :admin}
29 | )
30 |
31 | expect(rendered.strip).to eq("12:34PM")
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/config/locales/administrate.id.yml:
--------------------------------------------------------------------------------
1 | ---
2 | id:
3 | administrate:
4 | actions:
5 | confirm: Anda yakin?
6 | destroy: Hapus
7 | edit: Perbarui
8 | edit_resource: Perbarui %{name}
9 | show_resource: Tampilkan %{name}
10 | new_resource: "%{name} baru"
11 | back: Kembali
12 | controller:
13 | create:
14 | success: "%{resource} telah berhasil dibuat."
15 | destroy:
16 | success: "%{resource} telah berhasil dihapus."
17 | update:
18 | success: "%{resource} telah berhasil diperbarui."
19 | fields:
20 | has_many:
21 | more: Menampilkan %{count} dari %{total_count}
22 | none: Kosong
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} membuat %{resource_name} tidak bisa tersimpan:"
26 | navigation:
27 | back_to_app: Kembali ke aplikasi
28 | search:
29 | clear: Kosongkan pencarian
30 | label: Cari %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.es.yml:
--------------------------------------------------------------------------------
1 | ---
2 | es:
3 | administrate:
4 | actions:
5 | confirm: ¿Estás seguro?
6 | destroy: Destruir
7 | edit: Editar
8 | edit_resource: Editar %{name}
9 | show_resource: Mostrar %{name}
10 | new_resource: Nuevo %{name}
11 | back: Volver
12 | controller:
13 | create:
14 | success: "%{resource} fue creado exitosamente."
15 | destroy:
16 | success: "%{resource} fue destruido exitosamente."
17 | update:
18 | success: "%{resource} fue actualizado exitosamente."
19 | fields:
20 | has_many:
21 | more: Mostrando %{count} de %{total_count}
22 | none: Ninguno
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} no permitieron guardar este %{resource_name}:"
26 | navigation:
27 | back_to_app: Regresar a la aplicación
28 | search:
29 | clear: Borrar búsqueda
30 | label: Buscar %{resource}
31 |
--------------------------------------------------------------------------------
/lib/generators/administrate/test_record.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | module Generators
3 | # This class serves only to work around a strange behaviour in Rails 7
4 | # with Ruby 3.
5 | #
6 | # After running the spec for DashboardGenerator, the fake models that
7 | # it generates (eg: Foo, Shipment) linger around despite being removed
8 | # explicitly. This causes RouteGenerator to take them into
9 | # account and generate routes for them, which its spec doesn't expect,
10 | # causing a spec failure.
11 | #
12 | # To avoid this, the spec for DashboardGenerator defines its fake models
13 | # as children of TestRecord. Then RoutesGenerator explicitly filters
14 | # child classes of TestRecord when figuring out what models exist.
15 | #
16 | # Discussion at https://github.com/thoughtbot/administrate/pull/2324
17 | class TestRecord < ApplicationRecord
18 | self.abstract_class = true
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/config/locales/administrate.tr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | tr:
3 | administrate:
4 | actions:
5 | confirm: Emin misiniz?
6 | destroy: Sil
7 | edit: Düzenle
8 | edit_resource: "%{name} Kaydını Düzenle"
9 | show_resource: "%{name} Kaydını Göster"
10 | new_resource: Yeni %{name}
11 | back: Geri
12 | controller:
13 | create:
14 | success: "%{resource} kaydı başarıyla yaratıldı."
15 | destroy:
16 | success: "%{resource} kaydı başarıyla silindi."
17 | update:
18 | success: "%{resource} kaydı başarıyla düzenlendi."
19 | fields:
20 | has_many:
21 | more: Toplam %{total_count} kayıttan %{count} adedi gösteriliyor
22 | none: Yok
23 | form:
24 | error: Hata
25 | errors: "%{resource_name} kaydedilemedi: %{pluralized_errors}"
26 | navigation:
27 | back_to_app: Uygulamaya geri dön
28 | search:
29 | clear: Temizle
30 | label: "%{resource} içerisinde ara"
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.it.yml:
--------------------------------------------------------------------------------
1 | ---
2 | it:
3 | administrate:
4 | actions:
5 | confirm: Sei sicuro?
6 | destroy: Elimina
7 | edit: Modifica
8 | edit_resource: Modifica %{name}
9 | show_resource: Visualizza %{name}
10 | new_resource: Nuovo %{name}
11 | back: Indietro
12 | controller:
13 | create:
14 | success: "%{resource} è stato creato con successo."
15 | destroy:
16 | success: "%{resource} è stato eliminato con successo."
17 | update:
18 | success: "%{resource} è stato modificato con successo."
19 | fields:
20 | has_many:
21 | more: Visualizzo %{count} di %{total_count}
22 | none: Nessuno
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Torna all'app
28 | search:
29 | clear: Cancella ricerca
30 | label: Ricerca %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.pt.yml:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | ---
3 | pt:
4 | administrate:
5 | actions:
6 | confirm: Tem certeza?
7 | destroy: Remover
8 | edit: Editar
9 | edit_resource: Editar %{name}
10 | show_resource: Visualizar %{name}
11 | new_resource: Novo %{name}
12 | back: Voltar atrás
13 | controller:
14 | create:
15 | success: "%{resource} foi criado com sucesso."
16 | destroy:
17 | success: "%{resource} foi removido com sucesso."
18 | update:
19 | success: "%{resource} foi actualizado com sucesso."
20 | fields:
21 | has_many:
22 | more: "Mostrando %{count} de %{total_count}"
23 | none: Nenhum
24 | form:
25 | error: erro
26 | errors: "%{pluralized_errors} impediram %{resource_name} de ser gravado:"
27 | navigation:
28 | back_to_app: Voltar à aplicação
29 | search:
30 | clear: Limpar pesquisa
31 | label: Pesquisa %{resource}
32 |
--------------------------------------------------------------------------------
/bin/generate-database-schema:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | run_in_container() {
6 | container_id=$1
7 | shift
8 | cmd=$*
9 |
10 | docker exec --workdir /app "$container_id" bash -c "$cmd"
11 | }
12 |
13 | ruby_version=$(cat .ruby-version)
14 | owner=$(whoami)
15 |
16 | echo "Starting container using Ruby $ruby_version..."
17 | container_id=$(
18 | docker run --network="host" \
19 | --env PGHOST --env PGUSER --env PGPASSWORD \
20 | -d -v .:/app ruby:"$ruby_version" sleep infinity
21 | )
22 |
23 | echo "Check the environment is correct..."
24 | run_in_container "$container_id" "env"
25 |
26 | echo "Run bundle install..."
27 | run_in_container "$container_id" "bundle install"
28 |
29 | echo "Running db:setup..."
30 | run_in_container "$container_id" "bundle exec rake db:setup"
31 |
32 | echo "Tidying up container..."
33 | docker stop "$container_id" > /dev/null
34 | docker rm "$container_id" > /dev/null
35 |
36 | echo "Restoring file permissions..."
37 | sudo chown -R "$owner" .
38 |
--------------------------------------------------------------------------------
/config/locales/administrate.fr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | fr:
3 | administrate:
4 | actions:
5 | confirm: Êtes-vous sûr(e) ?
6 | destroy: Supprimer
7 | edit: Modifier
8 | edit_resource: Modifier %{name}
9 | show_resource: Détails %{name}
10 | new_resource: Création %{name}
11 | back: Précédent
12 | controller:
13 | create:
14 | success: "%{resource} a été correctement créé(e)."
15 | destroy:
16 | success: "%{resource} a été correctement supprimé(e)."
17 | update:
18 | success: "%{resource} a été correctement modifié(e)."
19 | fields:
20 | has_many:
21 | more: "%{count} sur %{total_count}"
22 | none: Aucun
23 | form:
24 | error: erreur
25 | errors: "%{pluralized_errors} ont empêché %{resource_name} d'être sauvegardé(e) :"
26 | navigation:
27 | back_to_app: Retour à l'application
28 | search:
29 | clear: Effacer la recherche
30 | label: Chercher %{resource}
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.pt-BR.yml:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | ---
3 | pt-BR:
4 | administrate:
5 | actions:
6 | confirm: Você tem certeza?
7 | destroy: Deletar
8 | edit: Editar
9 | edit_resource: Editar %{name}
10 | show_resource: Visualizar %{name}
11 | new_resource: Criar %{name}
12 | back: Voltar
13 | controller:
14 | create:
15 | success: "%{resource} foi criado com sucesso."
16 | destroy:
17 | success: "%{resource} foi deletado com sucesso."
18 | update:
19 | success: "%{resource} foi atualizado com sucesso."
20 | fields:
21 | has_many:
22 | more: "Exibindo %{count} de %{total_count}"
23 | none: Nenhum
24 | form:
25 | error: erro
26 | errors: "%{pluralized_errors} impediram %{resource_name} de ser gravado:"
27 | navigation:
28 | back_to_app: Voltar ao aplicativo
29 | search:
30 | clear: Limpar pesquisa
31 | label: Pesquisa %{resource}
32 |
--------------------------------------------------------------------------------
/config/locales/administrate.de.yml:
--------------------------------------------------------------------------------
1 | ---
2 | de:
3 | administrate:
4 | actions:
5 | confirm: Sind Sie sicher?
6 | destroy: Löschen
7 | edit: Editieren
8 | edit_resource: "%{name} editieren"
9 | show_resource: "%{name} anzeigen"
10 | new_resource: "%{name} erstellen"
11 | back: Zurück
12 | controller:
13 | create:
14 | success: "%{resource} wurde erfolgreich erstellt."
15 | destroy:
16 | success: "%{resource} wurde erfolgreich gelöscht."
17 | update:
18 | success: "%{resource} wurde erfolgreich aktualisiert."
19 | fields:
20 | has_many:
21 | more: "%{count} von %{total_count}"
22 | none: Keine
23 | form:
24 | error: Fehler
25 | errors: "%{resource_name} konnte nicht gespeichert werden, es gab %{pluralized_errors}."
26 | navigation:
27 | back_to_app: Zurück zur App
28 | search:
29 | clear: Suche zurücksetzen
30 | label: "%{resource} durchsuchen"
31 |
--------------------------------------------------------------------------------
/config/locales/administrate.pl.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pl:
3 | administrate:
4 | actions:
5 | confirm: Czy jesteś pewien?
6 | destroy: Usuń
7 | edit: Edytuj
8 | edit_resource: Edytuj %{name}
9 | show_resource: Wyświetl %{name}
10 | new_resource: Nowy %{name}
11 | back: Wstecz
12 | controller:
13 | create:
14 | success: "Zasób %{resource} został pomyślnie utworzony."
15 | destroy:
16 | success: "Zasób %{resource} został pomyślnie usunięty."
17 | update:
18 | success: "Zasób %{resource} został pomyślnie zaktualizowany."
19 | fields:
20 | has_many:
21 | more: Wyświetlanie %{count} z %{total_count}
22 | none: Brak
23 | form:
24 | error: error
25 | errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26 | navigation:
27 | back_to_app: Powrót do aplikacji
28 | search:
29 | clear: Wyczyść wyszukiwanie
30 | label: Szukanie %{resource}
31 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] = "test"
2 | require "dotenv"
3 | Dotenv.load
4 |
5 | require File.expand_path("../../spec/example_app/config/environment", __FILE__)
6 |
7 | require "rspec/rails"
8 | require "shoulda/matchers"
9 |
10 | Dir[Rails.root.join("../../spec/support/**/*.rb")].each { |file| require file }
11 |
12 | require "factories"
13 |
14 | module Features
15 | # Extend this module in spec/support/features/*.rb
16 | include Formulaic::Dsl
17 | end
18 |
19 | RSpec.configure do |config|
20 | config.include Features, type: :feature
21 | config.include DashboardHelpers
22 | config.include ControllerHelpers
23 | config.infer_base_class_for_anonymous_controllers = false
24 | config.infer_spec_type_from_file_location!
25 | config.use_transactional_fixtures = false
26 |
27 | config.before(:each, type: :generator) do
28 | allow(Rails).to receive(:root).and_return(Pathname.new(file(".")))
29 | end
30 | end
31 |
32 | ActiveRecord::Migration.maintain_test_schema!
33 |
--------------------------------------------------------------------------------
/spec/example_app/app/controllers/admin/application_controller.rb:
--------------------------------------------------------------------------------
1 | # All Administrate controllers inherit from this `Admin::ApplicationController`,
2 | # making it the ideal place to put authentication logic or other
3 | # before_actions.
4 | #
5 | # If you want to add pagination or other controller-level concerns,
6 | # you're free to overwrite the RESTful controller actions.
7 | module Admin
8 | class ApplicationController < Administrate::ApplicationController
9 | include Administrate::Punditize
10 |
11 | class AdminUser
12 | def id
13 | nil
14 | end
15 |
16 | def name
17 | "Admin"
18 | end
19 |
20 | def admin?
21 | true
22 | end
23 | end
24 |
25 | before_action :authenticate_admin
26 |
27 | def authenticate_admin
28 | user_id = session[:user_id]
29 | @current_user = user_id ? Customer.find(user_id) : AdminUser.new
30 | end
31 |
32 | def pundit_user
33 | @current_user
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build": "esbuild app/assets/javascripts/administrate/application.js --bundle --sourcemap --outdir=app/assets/builds/administrate --public-path=/assets/administrate",
4 | "build:css": "sass ./app/assets/stylesheets/administrate/application.scss:./app/assets/builds/administrate/application.css ./app/assets/stylesheets/administrate-internal/docs.scss:./app/assets/builds/administrate-internal/docs.css --source-map --embed-sources --load-path=node_modules",
5 | "lint:css": "stylelint app/assets/stylesheets/administrate/**/*.scss"
6 | },
7 | "dependencies": {
8 | "@hotwired/stimulus": "^3.2.2",
9 | "@hotwired/turbo-rails": "^8.0.0",
10 | "@rails/actiontext": "^8.0.0",
11 | "@selectize/selectize": "^0.15.2",
12 | "esbuild": "^0.25.0",
13 | "jquery": "^3.7.0",
14 | "sass": "^1.63.6",
15 | "trix": "^2.1.15"
16 | },
17 | "devDependencies": {
18 | "@thoughtbot/stylelint-config": "^4.0.0",
19 | "stylelint": "^16.2.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spec/example_app/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '* '
11 | error_notification:
12 | default_message: "Please review the problems below:"
13 | # Examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 | # include_blanks:
27 | # defaults:
28 | # age: 'Rather not say'
29 | # prompts:
30 | # defaults:
31 | # age: 'Select your age'
32 |
--------------------------------------------------------------------------------
/lib/administrate/page/collection.rb:
--------------------------------------------------------------------------------
1 | require_relative "base"
2 |
3 | module Administrate
4 | module Page
5 | class Collection < Page::Base
6 | def attribute_names
7 | options.fetch(:collection_attributes, nil) ||
8 | dashboard.collection_attributes
9 | end
10 |
11 | def attributes_for(resource)
12 | attribute_names.map do |attr_name|
13 | attribute_field(dashboard, resource, attr_name, :index)
14 | end
15 | end
16 |
17 | def attribute_types
18 | dashboard.attribute_types_for(attribute_names)
19 | end
20 |
21 | def ordered_html_class(attr)
22 | ordered_by?(attr) && order.direction
23 | end
24 |
25 | delegate :ordered_by?, to: :order
26 |
27 | def order_params_for(attr, key: resource_name)
28 | {key => order.order_params_for(attr)}
29 | end
30 |
31 | private
32 |
33 | def order
34 | options[:order] || Order.new
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/example_app/config/newrelic.yml:
--------------------------------------------------------------------------------
1 | common: &default_settings
2 | app_name: "administrate-prototype"
3 | audit_log:
4 | enabled: false
5 | browser_monitoring:
6 | auto_instrument: true
7 | capture_params: false
8 | developer_mode: false
9 | error_collector:
10 | capture_source: true
11 | enabled: true
12 | ignore_errors: "ActionController::RoutingError,Sinatra::NotFound"
13 | license_key: "<%= ENV["NEW_RELIC_LICENSE_KEY"] %>"
14 | log_level: info
15 | monitor_mode: true
16 | transaction_tracer:
17 | enabled: true
18 | record_sql: obfuscated
19 | stack_trace_threshold: 0.500
20 | transaction_threshold: apdex_f
21 | development:
22 | <<: *default_settings
23 | monitor_mode: false
24 | developer_mode: true
25 | test:
26 | <<: *default_settings
27 | monitor_mode: false
28 | production:
29 | <<: *default_settings
30 | monitor_mode: true
31 | staging:
32 | <<: *default_settings
33 | app_name: "administrate-prototype (Staging)"
34 | monitor_mode: true
35 |
--------------------------------------------------------------------------------
/spec/generators/views/form_generator_spec.rb:
--------------------------------------------------------------------------------
1 | require "support/generator_spec_helpers"
2 | require "generators/administrate/views/form_generator"
3 |
4 | describe Administrate::Generators::Views::FormGenerator, :generator do
5 | describe "administrate:views:form" do
6 | it "copies the form partial into the `admin/application` namespace" do
7 | expected_contents = contents_for_application_template("_form")
8 |
9 | run_generator []
10 | contents = File.read(file("app/views/admin/application/_form.html.erb"))
11 |
12 | expect(contents).to eq(expected_contents)
13 | end
14 | end
15 |
16 | describe "administrate:views:form resource" do
17 | it "copies the form view into the `admin/resource` namespace" do
18 | expected_contents = contents_for_application_template("_form")
19 |
20 | run_generator ["users"]
21 | contents = File.read(file("app/views/admin/users/_form.html.erb"))
22 |
23 | expect(contents).to eq(expected_contents)
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/views/fields/belongs_to/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # BelongsTo Form Partial
3 |
4 | This partial renders an input element for belongs_to relationships.
5 | By default, the input is a collection select box
6 | that displays all possible records to associate with.
7 |
8 | ## Local variables:
9 |
10 | - `f`:
11 | A Rails form generator, used to help create the appropriate input fields.
12 | - `field`:
13 | An instance of [Administrate::Field::BelongsTo][1].
14 | Contains helper methods for displaying a collection select box.
15 |
16 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
17 | %>
18 |
19 |
20 | <%= f.label field.attribute, for: "#{f.object_name}_#{field.permitted_attribute}" %>
21 |
22 |
23 | <%= f.select(field.permitted_attribute,
24 | options_for_select(field.associated_resource_options, field.selected_option),
25 | field.tag_options,
26 | field.html_options) %>
27 |
28 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/line_item_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class LineItemDashboard < Administrate::BaseDashboard
4 | ATTRIBUTES = [
5 | :order,
6 | :product,
7 | :quantity,
8 | :unit_price
9 | ]
10 |
11 | ATTRIBUTE_TYPES = {
12 | created_at: Field::DateTime,
13 | updated_at: Field::DateTime,
14 | order: Field::BelongsTo,
15 | product: Field::BelongsTo.with_options(sorting_column: :name),
16 | quantity: Field::Number,
17 | total_price: Field::Number.with_options(prefix: "$", decimals: 2),
18 | unit_price: Field::Number.with_options(prefix: "$", decimals: 2)
19 | }
20 |
21 | COLLECTION_ATTRIBUTES = ATTRIBUTES + [:total_price]
22 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTES + [:total_price]
23 | FORM_ATTRIBUTES = ATTRIBUTES
24 | FORM_ATTRIBUTES_NEW = ATTRIBUTES - %i[quantity unit_price]
25 | FORM_ATTRIBUTES_EDIT = ATTRIBUTES - [:unit_price]
26 |
27 | def display_resource(line_item)
28 | "Line Item #%04d" % line_item.id
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/example_app/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%#
8 | Configure default and controller-, and view-specific titles in
9 | config/locales/en.yml. For more see:
10 | https://github.com/calebthompson/title#usage
11 | %>
12 | <%= title %>
13 | <%= stylesheet_link_tag "//fonts.googleapis.com/css?family=Lato:300,400,900", media: "all" %>
14 | <%= stylesheet_link_tag "application", media: "all" %>
15 | <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
16 | <%= csrf_meta_tags %>
17 | <%= render "javascript" %>
18 |
19 |
20 |
21 |
22 | <%= render "navigation" -%>
23 |
24 |
25 | <%= render "flashes" -%>
26 |
27 | <%= yield %>
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/spec/example_app/spec/features/log_search_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Log search", type: :feature do
4 | it "filters logs related to a customer name", :js do
5 | c1 = create(:customer, name: "John Petrucci")
6 | c2 = create(:customer, name: "John Myung")
7 | c3 = create(:customer, name: "James LaBrie")
8 | o1a = create(:order, customer: c1)
9 | o2 = create(:order, customer: c2)
10 | o3 = create(:order, customer: c3)
11 | o1b = create(:order, customer: c1)
12 |
13 | [c1, c2, o1a, c3, o2, o3, o1b].each do |record|
14 | create(:log_entry, logeable: record)
15 | end
16 |
17 | visit admin_log_entries_path
18 | expect(page).to have_records_table(rows: 7)
19 |
20 | fill_in :search, with: "John"
21 | submit_search
22 | expect(page).to have_records_table(rows: 5)
23 | end
24 |
25 | def have_records_table(rows:)
26 | have_css("table tr[data-url]", count: rows)
27 | end
28 |
29 | def submit_search
30 | page.find_field("Search").send_keys(:enter)
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/views/fields/polymorphic/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Polymorphic Form Partial
3 |
4 | This partial renders an input element for polymorphic relationships.
5 |
6 | ## Local variables:
7 |
8 | - `f`:
9 | A Rails form generator, used to help create the appropriate input fields.
10 | - `field`:
11 | An instance of [Administrate::Field::Polymorphic][1].
12 | A wrapper around the polymorphic belongs_to relationship
13 | pulled from the database.
14 |
15 | [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
16 | %>
17 |
18 | <%= f.fields_for field.attribute do |pf| %>
19 |
20 | <%= pf.label :value, field.name.humanize %>
21 |
22 |
23 |
24 | <%= pf.hidden_field(:type, value: field.class.name) %>
25 | <%= pf.select(:value, nil, {}, data: {controller: field.html_controller}) do %>
26 | <%= grouped_options_for_select(field.associated_resource_grouped_options, field.selected_global_id, prompt: true) %>
27 | <% end %>
28 |
29 | <% end %>
30 |
--------------------------------------------------------------------------------
/spec/features/payments_index_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "payment index page" do
4 | it "displays payments' id and receipt link" do
5 | payment = create(:payment)
6 |
7 | visit admin_payments_path
8 |
9 | expect(page).to have_header("Payments")
10 | expect(page).to have_content(payment.id)
11 | expect(page).to have_content("receipt-#{payment.id}.txt")
12 | end
13 |
14 | it "allows downloading the receipt" do
15 | payment = create(:payment)
16 |
17 | visit admin_payments_path
18 | click_on("receipt-#{payment.id}.txt")
19 |
20 | expect(page.body).to eq("This is the receipt for payment ##{payment.id}")
21 | expect(response_headers["Content-Disposition"]).to match(%r{^attachment; filename=})
22 | end
23 |
24 | it "links to the payment show page", :js do
25 | payment = create(:payment)
26 |
27 | visit admin_payments_path
28 | click_row_for(payment)
29 |
30 | expect(page).to have_content(payment.id)
31 | expect(page).to have_current_path(admin_payment_path(payment))
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/administrate/views/fields/text/_form_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 | require "administrate/field/text"
3 |
4 | RSpec.describe "fields/text/_form", type: :view do
5 | it "allows configuring input options" do
6 | textarea = instance_double(
7 | "Administrate::Field::Text",
8 | attribute: :name,
9 | data: nil,
10 | options: {input_options: {rows: 50}}
11 | )
12 |
13 | render_partial(textarea)
14 |
15 | expect(rendered).to have_css(%(textarea[rows=50]))
16 | end
17 |
18 | it "doesn't require input options" do
19 | textarea = instance_double(
20 | "Administrate::Field::Text",
21 | attribute: :name,
22 | data: nil,
23 | options: {}
24 | )
25 |
26 | render_partial(textarea)
27 |
28 | expect(rendered).to have_css(%(textarea))
29 | end
30 |
31 | def render_partial(field)
32 | product = build(:product)
33 |
34 | fields model: product do |f|
35 | render(
36 | partial: "fields/text/form",
37 | locals: {field: field, f: f}
38 | )
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/example_app/app/dashboards/product_dashboard.rb:
--------------------------------------------------------------------------------
1 | require "administrate/base_dashboard"
2 |
3 | class ProductDashboard < Administrate::BaseDashboard
4 | ATTRIBUTES = [
5 | :name,
6 | :pages,
7 | :price,
8 | :description,
9 | :image_url,
10 | :product_meta_tag,
11 | :release_year,
12 | :banner
13 | ]
14 |
15 | ATTRIBUTE_TYPES = {
16 | banner: Field::RichText,
17 | created_at: Field::DateTime,
18 | updated_at: Field::DateTime,
19 | description: Field::Text,
20 | image_url: Field::Url,
21 | name: Field::String,
22 | pages: Field::HasMany,
23 | price: Field::Number.with_options(prefix: "$", decimals: 2),
24 | product_meta_tag: Field::HasOne.with_options(order: "meta_title"),
25 | release_year: Field::Select.with_options(
26 | collection: -> { (Time.current.year - 10)..Time.current.year }
27 | )
28 | }
29 |
30 | COLLECTION_ATTRIBUTES = ATTRIBUTES
31 | FORM_ATTRIBUTES = ATTRIBUTES
32 | SHOW_PAGE_ATTRIBUTES = ATTRIBUTES
33 |
34 | def display_resource(resource)
35 | resource.name
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/administrate/components/_cells.scss:
--------------------------------------------------------------------------------
1 | .cell-label__sort-indicator {
2 | float: right;
3 | margin-left: 5px;
4 |
5 | svg {
6 | color: $hint-grey;
7 | height: 13px;
8 | transition: transform $base-duration $base-timing;
9 | width: 13px;
10 | }
11 | }
12 |
13 | .cell-label {
14 | padding-top: 0.15em;
15 |
16 | a {
17 | color: inherit;
18 | display: inline-block;
19 | transition: color $base-duration $base-timing;
20 | width: 100%;
21 | }
22 |
23 | &:hover {
24 | a {
25 | color: $action-color;
26 | }
27 |
28 | svg {
29 | fill: $action-color;
30 | transform: rotate(180deg);
31 | }
32 | }
33 | }
34 |
35 | .cell-label--asc,
36 | .cell-label--desc {
37 | font-weight: $bold-font-weight;
38 | }
39 |
40 | .cell-label__sort-indicator--desc {
41 | transform: rotate(180deg);
42 | }
43 |
44 | .cell-label--action-button {
45 | white-space: nowrap;
46 | width: 1rem;
47 | }
48 |
49 | .cell-data--number,
50 | .cell-label--number {
51 | text-align: right;
52 | white-space: nowrap;
53 | width: 1rem;
54 | }
55 |
--------------------------------------------------------------------------------
/app/views/layouts/administrate/application.html.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | # Application Layout
3 |
4 | This view template is used as the layout
5 | for every page that Administrate generates.
6 |
7 | By default, it renders:
8 | - Navigation
9 | - Content for a search bar
10 | (if provided by a `content_for` block in a nested page)
11 | - Flashes
12 | - Links to stylesheets and JavaScripts
13 | %>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | <%= content_for(:title) %> - <%= application_title %>
23 |
24 | <%= render "stylesheet" %>
25 | <%= csrf_meta_tags %>
26 | <%= csp_meta_tag if defined?(csp_meta_tag) %>
27 | <%= render "javascript" %>
28 |
29 |
30 | <%= render "icons" %>
31 |
32 |
33 | <%= render "navigation" -%>
34 |
35 |
36 | <%= render "flashes" -%>
37 | <%= yield %>
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/guides/scoping_has_many_relations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Scoping HasMany Relations
3 | ---
4 |
5 | To show a subset of a has_many relationship, create a new [has_many](https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many) relationship in your model (using the `scope` argument) and add it to the model's dashboard.
6 |
7 | ## Creating a scoped has_many relationship
8 |
9 | Models can define subsets of a `has_many` relationship by passing a callable (i.e. proc or lambda) as its second argument.
10 |
11 | ```ruby
12 | class Customer < ApplicationRecord
13 | has_many :orders
14 | has_many :processed_orders, ->{ where(processed: true) }, class_name: "Order"
15 | ```
16 |
17 | Since ActiveRecord infers the class name from the first argument, the new `has_many` relation needs to specify the model using the `class_name` option.
18 |
19 | ## Add new relationship to dashboard
20 |
21 | Your new scoped relation can be used in the dashboard just like the original `HasMany`.
22 |
23 | ```ruby
24 | ATTRIBUTE_TYPES = {
25 | orders: Field::HasMany,
26 | processed_orders: Field::HasMany
27 | ```
28 |
--------------------------------------------------------------------------------
/lib/administrate/page/base.rb:
--------------------------------------------------------------------------------
1 | module Administrate
2 | module Page
3 | class Base
4 | def initialize(dashboard, options = {})
5 | @dashboard = dashboard
6 | @options = options
7 | end
8 |
9 | def resource_name
10 | @resource_name ||=
11 | dashboard.class.to_s.scan(/(.+)Dashboard/).first.first.underscore
12 | end
13 |
14 | def resource_path
15 | @resource_path ||= resource_name.tr("/", "_")
16 | end
17 |
18 | def collection_includes
19 | dashboard.try(:collection_includes) || []
20 | end
21 |
22 | def item_includes
23 | dashboard.try(:item_includes) || []
24 | end
25 |
26 | def item_associations
27 | dashboard.try(:item_associations) || []
28 | end
29 |
30 | private
31 |
32 | def attribute_field(dashboard, resource, attribute_name, page)
33 | field = dashboard.attribute_type_for(attribute_name)
34 | field.new(attribute_name, nil, page, resource: resource)
35 | end
36 |
37 | attr_reader :dashboard, :options
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: CodeQL
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | types: [opened, synchronize, reopened]
9 | schedule:
10 | - cron: '44 6 * * 4'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 | permissions:
17 | actions: read
18 | contents: read
19 | security-events: write
20 |
21 | strategy:
22 | fail-fast: false
23 | matrix:
24 | language: ['javascript', 'ruby']
25 |
26 | steps:
27 | - name: Checkout repository
28 | uses: actions/checkout@v6
29 |
30 | - name: Initialize CodeQL
31 | uses: github/codeql-action/init@v4
32 | with:
33 | languages: ${{ matrix.language }}
34 | config: |
35 | paths-ignore:
36 | - 'app/assets/builds'
37 | - 'app/assets/javascripts/administrate/vendor'
38 |
39 | - name: Autobuild
40 | uses: github/codeql-action/autobuild@v4
41 |
42 | - name: Perform CodeQL Analysis
43 | uses: github/codeql-action/analyze@v4
44 |
--------------------------------------------------------------------------------