panda-core 0.1.16 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -16
- data/Rakefile +3 -0
- data/app/builders/panda/core/form_builder.rb +225 -0
- data/app/components/panda/core/admin/button_component.rb +70 -0
- data/app/components/panda/core/admin/container_component.html.erb +12 -0
- data/app/components/panda/core/admin/container_component.rb +13 -0
- data/app/components/panda/core/admin/flash_message_component.html.erb +31 -0
- data/app/components/panda/core/admin/flash_message_component.rb +47 -0
- data/app/components/panda/core/admin/heading_component.rb +46 -0
- data/app/components/panda/core/admin/panel_component.html.erb +7 -0
- data/app/components/panda/core/admin/panel_component.rb +13 -0
- data/app/components/panda/core/admin/slideover_component.html.erb +9 -0
- data/app/components/panda/core/admin/slideover_component.rb +15 -0
- data/app/components/panda/core/admin/table_component.html.erb +29 -0
- data/app/components/panda/core/admin/table_component.rb +46 -0
- data/app/components/panda/core/admin/tag_component.rb +35 -0
- data/app/constraints/panda/core/admin_constraint.rb +14 -0
- data/app/controllers/panda/core/admin/dashboard_controller.rb +22 -0
- data/app/controllers/panda/core/admin/my_profile_controller.rb +49 -0
- data/app/controllers/panda/core/admin/sessions_controller.rb +69 -0
- data/app/controllers/panda/core/admin_controller.rb +28 -0
- data/app/controllers/panda/core/application_controller.rb +59 -0
- data/app/helpers/panda/core/asset_helper.rb +32 -0
- data/app/javascript/panda/core/application.js +9 -0
- data/app/javascript/panda/core/controllers/index.js +20 -0
- data/app/javascript/panda/core/controllers/theme_form_controller.js +25 -0
- data/app/javascript/panda/core/tailwindcss-stimulus-components.js +3 -0
- data/app/models/panda/core/application_record.rb +9 -0
- data/app/models/panda/core/breadcrumb.rb +17 -0
- data/app/models/panda/core/current.rb +16 -0
- data/app/models/panda/core/user.rb +51 -0
- data/app/views/layouts/panda/core/admin.html.erb +59 -0
- data/app/views/panda/core/admin/dashboard/show.html.erb +27 -0
- data/app/views/panda/core/admin/my_profile/edit.html.erb +49 -0
- data/app/views/panda/core/admin/sessions/new.html.erb +38 -0
- data/app/views/panda/core/admin/shared/_breadcrumbs.html.erb +35 -0
- data/app/views/panda/core/admin/shared/_flash.html.erb +31 -0
- data/app/views/panda/core/admin/shared/_sidebar.html.erb +27 -0
- data/app/views/panda/core/admin/shared/_slideover.html.erb +33 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20241210000003_add_current_theme_to_panda_core_users.rb +7 -0
- data/db/migrate/20250809000001_create_panda_core_users.rb +16 -0
- data/lib/generators/panda/core/dev_tools/USAGE +24 -0
- data/lib/generators/panda/core/dev_tools/templates/lefthook.yml +13 -0
- data/lib/generators/panda/core/dev_tools/templates/spec_support_panda_core_helpers.rb +18 -0
- data/lib/generators/panda/core/dev_tools_generator.rb +143 -0
- data/lib/panda/core/asset_loader.rb +221 -0
- data/lib/panda/core/authentication.rb +36 -0
- data/lib/panda/core/component_registry.rb +37 -0
- data/lib/panda/core/configuration.rb +31 -1
- data/lib/panda/core/engine.rb +43 -7
- data/lib/panda/core/notifications.rb +40 -0
- data/lib/panda/core/rake_tasks.rb +16 -0
- data/lib/panda/core/subscribers/authentication_subscriber.rb +61 -0
- data/lib/panda/core/testing/capybara_config.rb +70 -0
- data/lib/panda/core/testing/omniauth_helpers.rb +52 -0
- data/lib/panda/core/testing/rspec_config.rb +72 -0
- data/lib/panda/core/version.rb +1 -1
- data/lib/panda/core.rb +2 -8
- data/lib/tasks/assets.rake +423 -0
- data/lib/tasks/panda/core/migrations.rake +13 -0
- data/lib/tasks/panda_core.rake +52 -0
- metadata +320 -11
- data/db/migrate/20250121012333_logidze_install.rb +0 -577
- data/db/migrate/20250121012334_enable_hstore.rb +0 -5
@@ -9,7 +9,20 @@ module Panda
|
|
9
9
|
:parent_mailer,
|
10
10
|
:mailer_sender,
|
11
11
|
:mailer_default_url_options,
|
12
|
-
:session_token_cookie
|
12
|
+
:session_token_cookie,
|
13
|
+
:authentication_providers,
|
14
|
+
:admin_path,
|
15
|
+
:admin_navigation_items,
|
16
|
+
:admin_dashboard_widgets,
|
17
|
+
:user_attributes,
|
18
|
+
:user_associations,
|
19
|
+
:authorization_policy,
|
20
|
+
:additional_user_params,
|
21
|
+
:available_themes,
|
22
|
+
:login_logo_path,
|
23
|
+
:login_page_title,
|
24
|
+
:initial_admin_breadcrumb,
|
25
|
+
:dashboard_redirect_path
|
13
26
|
|
14
27
|
def initialize
|
15
28
|
@user_class = "Panda::Core::User"
|
@@ -21,6 +34,23 @@ module Panda
|
|
21
34
|
@mailer_sender = "support@example.com"
|
22
35
|
@mailer_default_url_options = {host: "localhost:3000"}
|
23
36
|
@session_token_cookie = :panda_session
|
37
|
+
@authentication_providers = {}
|
38
|
+
@admin_path = "/admin"
|
39
|
+
|
40
|
+
# Hook system for extending admin UI
|
41
|
+
@admin_navigation_items = ->(user) { [] }
|
42
|
+
@admin_dashboard_widgets = ->(user) { [] }
|
43
|
+
@user_attributes = []
|
44
|
+
@user_associations = []
|
45
|
+
@authorization_policy = ->(user, action, resource) { user.admin? }
|
46
|
+
|
47
|
+
# Profile and UI customization
|
48
|
+
@additional_user_params = []
|
49
|
+
@available_themes = [["Default", "default"], ["Sky", "sky"]]
|
50
|
+
@login_logo_path = nil
|
51
|
+
@login_page_title = "Sign in to your account"
|
52
|
+
@initial_admin_breadcrumb = nil # Proc that returns [label, path]
|
53
|
+
@dashboard_redirect_path = nil # Path to redirect to after login (defaults to admin_root_path)
|
24
54
|
end
|
25
55
|
end
|
26
56
|
|
data/lib/panda/core/engine.rb
CHANGED
@@ -1,26 +1,62 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
|
3
3
|
require "rails/engine"
|
4
|
+
require "omniauth"
|
5
|
+
require "omniauth/rails_csrf_protection"
|
6
|
+
require "view_component"
|
4
7
|
|
5
8
|
module Panda
|
6
9
|
module Core
|
7
10
|
class Engine < ::Rails::Engine
|
8
11
|
isolate_namespace Panda::Core
|
9
12
|
|
13
|
+
config.eager_load_namespaces << Panda::Core::Engine
|
14
|
+
|
15
|
+
# Add engine's app directories to autoload paths
|
16
|
+
config.autoload_paths += Dir[root.join("app", "models")]
|
17
|
+
config.autoload_paths += Dir[root.join("app", "controllers")]
|
18
|
+
config.autoload_paths += Dir[root.join("app", "builders")]
|
19
|
+
config.autoload_paths += Dir[root.join("app", "components")]
|
20
|
+
|
10
21
|
config.generators do |g|
|
11
22
|
g.test_framework :rspec
|
12
23
|
g.fixture_replacement :factory_bot
|
13
24
|
g.factory_bot dir: "spec/factories"
|
14
25
|
end
|
15
26
|
|
27
|
+
initializer "panda_core.append_migrations" do |app|
|
28
|
+
unless app.root.to_s.match?(root.to_s)
|
29
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
30
|
+
app.config.paths["db/migrate"] << expanded_path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
16
35
|
initializer "panda_core.configuration" do |app|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
36
|
+
# Configuration is already initialized with defaults in Configuration class
|
37
|
+
end
|
38
|
+
|
39
|
+
initializer "panda_core.omniauth" do |app|
|
40
|
+
# Mount OmniAuth at configurable admin path
|
41
|
+
app.middleware.use OmniAuth::Builder do
|
42
|
+
# Configure OmniAuth to use the configured admin path
|
43
|
+
configure do |config|
|
44
|
+
config.path_prefix = "#{Panda::Core.configuration.admin_path}/auth"
|
45
|
+
# Allow POST requests for request phase (required for CSRF protection)
|
46
|
+
config.allowed_request_methods = [:get, :post]
|
47
|
+
end
|
48
|
+
|
49
|
+
Panda::Core.configuration.authentication_providers.each do |provider_name, settings|
|
50
|
+
case provider_name.to_s
|
51
|
+
when "microsoft_graph"
|
52
|
+
provider :microsoft_graph, settings[:client_id], settings[:client_secret], settings[:options] || {}
|
53
|
+
when "google_oauth2"
|
54
|
+
provider :google_oauth2, settings[:client_id], settings[:client_secret], settings[:options] || {}
|
55
|
+
when "github"
|
56
|
+
provider :github, settings[:client_id], settings[:client_secret], settings[:options] || {}
|
57
|
+
when "developer"
|
58
|
+
provider :developer if Rails.env.development?
|
59
|
+
end
|
24
60
|
end
|
25
61
|
end
|
26
62
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module Core
|
5
|
+
module Notifications
|
6
|
+
# Event names following Rails convention: namespace.event
|
7
|
+
USER_CREATED = "panda.core.user_created"
|
8
|
+
USER_LOGIN = "panda.core.user_login"
|
9
|
+
USER_LOGOUT = "panda.core.user_logout"
|
10
|
+
ADMIN_ACTION = "panda.core.admin_action"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Subscribe to a Panda Core event
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# Panda::Core::Notifications.subscribe(:user_created) do |event|
|
17
|
+
# UserMailer.welcome(event.payload[:user]).deliver_later
|
18
|
+
# end
|
19
|
+
def subscribe(event_name, &block)
|
20
|
+
event_key = const_get(event_name.to_s.upcase)
|
21
|
+
ActiveSupport::Notifications.subscribe(event_key, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Instrument a Panda Core event
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Panda::Core::Notifications.instrument(:user_created, user: user, provider: provider)
|
28
|
+
def instrument(event_name, payload = {})
|
29
|
+
event_key = const_get(event_name.to_s.upcase)
|
30
|
+
ActiveSupport::Notifications.instrument(event_key, payload)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Unsubscribe from a Panda Core event
|
34
|
+
def unsubscribe(subscriber)
|
35
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module Core
|
5
|
+
module RakeTasks
|
6
|
+
def self.install_tasks
|
7
|
+
Dir[File.expand_path("../tasks/*.rake", __dir__)].each { |f| load f }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Load tasks if Rake is available
|
14
|
+
if defined?(Rake)
|
15
|
+
Panda::Core::RakeTasks.install_tasks
|
16
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module Core
|
5
|
+
module Subscribers
|
6
|
+
# Example subscriber for authentication events
|
7
|
+
#
|
8
|
+
# To enable in your application, add to an initializer:
|
9
|
+
# Panda::Core::Subscribers::AuthenticationSubscriber.attach
|
10
|
+
class AuthenticationSubscriber
|
11
|
+
class << self
|
12
|
+
def attach
|
13
|
+
# Subscribe to user creation
|
14
|
+
ActiveSupport::Notifications.subscribe("panda.core.user_created") do |event|
|
15
|
+
user = event.payload[:user]
|
16
|
+
provider = event.payload[:provider]
|
17
|
+
|
18
|
+
Rails.logger.info "[AuthSubscriber] New user created: #{user.email} via #{provider}"
|
19
|
+
|
20
|
+
# Example: Send welcome email
|
21
|
+
# UserMailer.welcome(user).deliver_later if defined?(UserMailer)
|
22
|
+
|
23
|
+
# Example: Track analytics
|
24
|
+
# Analytics.track("User Signup", user_id: user.id, provider: provider) if defined?(Analytics)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Subscribe to user login
|
28
|
+
ActiveSupport::Notifications.subscribe("panda.core.user_login") do |event|
|
29
|
+
user = event.payload[:user]
|
30
|
+
provider = event.payload[:provider]
|
31
|
+
|
32
|
+
Rails.logger.info "[AuthSubscriber] User logged in: #{user.email} via #{provider}"
|
33
|
+
|
34
|
+
# Example: Update last login time
|
35
|
+
# user.touch(:last_login_at) if user.respond_to?(:last_login_at)
|
36
|
+
|
37
|
+
# Example: Track analytics
|
38
|
+
# Analytics.track("User Login", user_id: user.id, provider: provider) if defined?(Analytics)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Subscribe to user logout
|
42
|
+
ActiveSupport::Notifications.subscribe("panda.core.user_logout") do |event|
|
43
|
+
user = event.payload[:user]
|
44
|
+
|
45
|
+
Rails.logger.info "[AuthSubscriber] User logged out: #{user.email}"
|
46
|
+
|
47
|
+
# Example: Clean up session data
|
48
|
+
# SessionCleanupJob.perform_later(user.id) if defined?(SessionCleanupJob)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def detach
|
53
|
+
ActiveSupport::Notifications.unsubscribe("panda.core.user_created")
|
54
|
+
ActiveSupport::Notifications.unsubscribe("panda.core.user_login")
|
55
|
+
ActiveSupport::Notifications.unsubscribe("panda.core.user_logout")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "capybara/rspec"
|
4
|
+
|
5
|
+
module Panda
|
6
|
+
module Core
|
7
|
+
module Testing
|
8
|
+
module CapybaraConfig
|
9
|
+
def self.configure
|
10
|
+
Capybara.server = :puma, {Silent: true}
|
11
|
+
Capybara.default_max_wait_time = 5
|
12
|
+
Capybara.disable_animation = true
|
13
|
+
|
14
|
+
# Register Chrome driver with sensible defaults
|
15
|
+
if defined?(Cuprite)
|
16
|
+
Capybara.register_driver :panda_chrome do |app|
|
17
|
+
Cuprite::Driver.new(
|
18
|
+
app,
|
19
|
+
window_size: [1400, 1400],
|
20
|
+
browser_options: {
|
21
|
+
"no-sandbox": nil,
|
22
|
+
"disable-gpu": nil,
|
23
|
+
"disable-dev-shm-usage": nil
|
24
|
+
},
|
25
|
+
inspector: ENV["INSPECTOR"] == "true",
|
26
|
+
headless: ENV["HEADLESS"] != "false"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
Capybara.javascript_driver = :panda_chrome
|
31
|
+
Capybara.default_driver = :rack_test
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Helper methods for system tests
|
36
|
+
module Helpers
|
37
|
+
def wait_for_ajax
|
38
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
39
|
+
loop until page.evaluate_script("jQuery.active").zero?
|
40
|
+
end
|
41
|
+
rescue Timeout::Error
|
42
|
+
# Ajax didn't finish, but continue anyway
|
43
|
+
end
|
44
|
+
|
45
|
+
def wait_for_turbo
|
46
|
+
has_css?("html", wait: 0.1)
|
47
|
+
return unless page.evaluate_script("typeof Turbo !== 'undefined'")
|
48
|
+
|
49
|
+
page.evaluate_script("Turbo.session.drive = false")
|
50
|
+
yield if block_given?
|
51
|
+
page.evaluate_script("Turbo.session.drive = true")
|
52
|
+
end
|
53
|
+
|
54
|
+
def take_screenshot_on_failure
|
55
|
+
return unless page.driver.respond_to?(:save_screenshot)
|
56
|
+
|
57
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
58
|
+
filename = "screenshot_#{timestamp}_#{example.description.parameterize}.png"
|
59
|
+
path = Rails.root.join("tmp", "screenshots", filename)
|
60
|
+
|
61
|
+
FileUtils.mkdir_p(File.dirname(path))
|
62
|
+
page.driver.save_screenshot(path)
|
63
|
+
|
64
|
+
puts "\nScreenshot saved: #{path}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module Core
|
5
|
+
module Testing
|
6
|
+
module OmniAuthHelpers
|
7
|
+
def mock_omniauth_login(email: "admin@example.com", name: "Test User", provider: :google_oauth2, uid: "123456789", is_admin: true)
|
8
|
+
OmniAuth.config.test_mode = true
|
9
|
+
OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new(
|
10
|
+
provider: provider.to_s,
|
11
|
+
uid: uid,
|
12
|
+
info: {
|
13
|
+
email: email,
|
14
|
+
name: name,
|
15
|
+
image: "https://example.com/avatar.jpg"
|
16
|
+
}
|
17
|
+
)
|
18
|
+
|
19
|
+
# Create or update the user in the test database
|
20
|
+
Panda::Core::User.find_or_create_by(email: email) do |u|
|
21
|
+
# Split name into firstname and lastname
|
22
|
+
parts = name.split(' ', 2)
|
23
|
+
u.firstname = parts[0] || name
|
24
|
+
u.lastname = parts[1] || ''
|
25
|
+
u.admin = is_admin
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def login_as_admin(email: "admin@example.com", name: "Admin User")
|
30
|
+
user = mock_omniauth_login(email: email, name: name, is_admin: true)
|
31
|
+
visit "/admin/auth/google_oauth2"
|
32
|
+
user
|
33
|
+
end
|
34
|
+
|
35
|
+
def login_as_user(email: "user@example.com", name: "Regular User")
|
36
|
+
user = mock_omniauth_login(email: email, name: name, is_admin: false)
|
37
|
+
visit "/admin/auth/google_oauth2"
|
38
|
+
user
|
39
|
+
end
|
40
|
+
|
41
|
+
def logout
|
42
|
+
visit "/admin/logout"
|
43
|
+
end
|
44
|
+
|
45
|
+
def mock_omniauth_failure(message = "Authentication failed")
|
46
|
+
OmniAuth.config.test_mode = true
|
47
|
+
OmniAuth.config.mock_auth[:google_oauth2] = :invalid_credentials
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Panda
|
4
|
+
module Core
|
5
|
+
module Testing
|
6
|
+
module RSpecConfig
|
7
|
+
def self.configure(config)
|
8
|
+
# Add common RSpec configurations
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.mock_with :rspec do |mocks|
|
14
|
+
mocks.verify_partial_doubles = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
18
|
+
config.filter_run_when_matching :focus
|
19
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
20
|
+
config.disable_monkey_patching!
|
21
|
+
|
22
|
+
if config.files_to_run.one?
|
23
|
+
config.default_formatter = "doc"
|
24
|
+
end
|
25
|
+
|
26
|
+
config.order = :random
|
27
|
+
Kernel.srand config.seed
|
28
|
+
|
29
|
+
# Database cleaner setup
|
30
|
+
if defined?(DatabaseCleaner)
|
31
|
+
config.before(:suite) do
|
32
|
+
DatabaseCleaner.strategy = :transaction
|
33
|
+
DatabaseCleaner.clean_with(:truncation)
|
34
|
+
end
|
35
|
+
|
36
|
+
config.around(:each) do |example|
|
37
|
+
DatabaseCleaner.cleaning do
|
38
|
+
example.run
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# OmniAuth test mode
|
44
|
+
if defined?(OmniAuth)
|
45
|
+
config.before(:each) do
|
46
|
+
OmniAuth.config.test_mode = true
|
47
|
+
end
|
48
|
+
|
49
|
+
config.after(:each) do
|
50
|
+
OmniAuth.config.mock_auth.clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Common matchers for Panda gems
|
56
|
+
def self.setup_matchers
|
57
|
+
RSpec::Matchers.define :have_breadcrumb do |expected|
|
58
|
+
match do |page|
|
59
|
+
page.has_css?(".breadcrumb", text: expected)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
RSpec::Matchers.define :have_flash_message do |type, message|
|
64
|
+
match do |page|
|
65
|
+
page.has_css?(".flash-message.flash-#{type}", text: message)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/panda/core/version.rb
CHANGED
data/lib/panda/core.rb
CHANGED
@@ -1,22 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "rails"
|
4
|
-
require "dry-configurable"
|
5
4
|
|
6
5
|
module Panda
|
7
6
|
module Core
|
8
|
-
extend Dry::Configurable
|
9
|
-
|
10
|
-
setting :user_class
|
11
|
-
setting :authentication_providers, default: []
|
12
|
-
setting :storage_provider, default: :active_storage
|
13
|
-
setting :cache_store, default: :memory_store
|
14
|
-
|
15
7
|
def self.root
|
16
8
|
File.expand_path("../..", __FILE__)
|
17
9
|
end
|
18
10
|
end
|
19
11
|
end
|
20
12
|
|
13
|
+
require_relative "core/version"
|
21
14
|
require_relative "core/configuration"
|
15
|
+
require_relative "core/asset_loader"
|
22
16
|
require_relative "core/engine" if defined?(Rails)
|