Class: Railpack::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/railpack/config.rb

Overview

Configuration handler for Railpack bundling settings.

This class provides immutable, environment-aware configuration management with:

  • YAML-based config loading from config/railpack.yml
  • Three-level merge order: defaults → bundler-specific → environment-specific
  • Deep-frozen configs for immutability and thread safety
  • Explicit accessors for common config keys with method_missing fallback

Example config/railpack.yml:

default:
bundler: bun
outdir: app/assets/builds
development:
sourcemap: true
production:
minify: true

All configs are immutable after loading. Set values in config/railpack.yml only.

Defined Under Namespace

Classes: Error

Constant Summary collapse

CONFIG_KEYS =

Known config keys that get explicit accessors

%w[
  target format minify sourcemap splitting
  entrypoint entrypoints outdir platform mode analyze_bundle
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Config

Returns a new instance of Config.



33
34
35
36
# File 'lib/railpack/config.rb', line 33

def initialize
  @config = load_config
  @merged_cache = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/railpack/config.rb', line 97

def method_missing(method, *args)
  config_key = method.to_s
  if method.end_with?('=')
    # Setter - no longer allowed, config is immutable
    raise Error, "Config is immutable. Set values in config/railpack.yml"
  else
    # Getter - read from merged config (backward compatibility)
    # TODO: In v2.0, remove this fallback and require explicit accessors
    warn "DEPRECATED: Dynamic config access via '#{config_key}' will be removed in v2.0. Use explicit accessors instead." if defined?(Rails) && Rails.logger
    env = args.first || current_env
    return for_environment(env)[config_key] if for_environment(env).key?(config_key)
    super
  end
end

Instance Attribute Details

#config ⇒ Object (readonly)

Returns the value of attribute config.



31
32
33
# File 'lib/railpack/config.rb', line 31

def config
  @config
end

Instance Method Details

#build_args(env = current_env) ⇒ Object

Build command arguments



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/railpack/config.rb', line 119

def build_args(env = current_env)
  cfg = for_environment(env)
  args = []

  # Support multiple entrypoints or single entrypoint
  entrypoints = cfg['entrypoints']
  if entrypoints.is_a?(Array) && entrypoints.any?
    args.concat(entrypoints)
  elsif cfg['entrypoint'].is_a?(String) && !cfg['entrypoint'].empty?
    args << cfg['entrypoint']
  end

  args << "--outdir=#{cfg['outdir']}" if cfg['outdir']
  args.concat(build_flags(env))

  args
end

#build_flags(env = current_env) ⇒ Object

Build command flags (for dynamic bundler construction)



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/railpack/config.rb', line 138

def build_flags(env = current_env)
  cfg = for_environment(env)
  flags = []

  flags << "--target=#{cfg['target']}" if cfg['target']
  flags << "--format=#{cfg['format']}" if cfg['format']
  flags << "--minify" if cfg['minify']
  flags << "--sourcemap" if cfg['sourcemap']
  flags << "--splitting" if cfg['splitting']

  flags
end

#bundler(env = current_env) ⇒ Object



77
78
79
80
81
82
# File 'lib/railpack/config.rb', line 77

def bundler(env = current_env)
  # Look directly in config to avoid circular dependency
  env_config = @config[env.to_s] || {}
  default_config = @config["default"] || {}
  env_config['bundler'] || default_config['bundler'] || 'bun'
end

#bundler_command_overrides(env = current_env) ⇒ Object

Get bundler-specific command overrides from config



90
91
92
93
94
95
# File 'lib/railpack/config.rb', line 90

def bundler_command_overrides(env = current_env)
  bundler_name = bundler(env)
  overrides = @config.dig('bundlers', bundler_name, 'commands') || {}
  # Deep freeze for immutability
  deep_freeze(overrides)
end

#bundler_config(env = current_env) ⇒ Object



84
85
86
87
# File 'lib/railpack/config.rb', line 84

def bundler_config(env = current_env)
  bundler_name = bundler(env)
  @config[bundler_name] || {}
end

#current_env ⇒ Object



38
39
40
41
42
43
44
# File 'lib/railpack/config.rb', line 38

def current_env
  if defined?(Rails) && Rails.respond_to?(:env)
    Rails.env
  else
    :development
  end
end

#for_environment(env = current_env) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/railpack/config.rb', line 53

def for_environment(env = current_env)
  @merged_cache[env.to_s] ||= begin
    base_config = @config["default"] || {}
    bundler_config = bundler_config(env)
    env_config = @config[env.to_s] || {}

    # Merge: default <- bundler <- environment
    merged = deep_merge(deep_merge(base_config, bundler_config), env_config)

    # Validate critical config values
    validate_config!(merged, env)

    # Deep freeze for immutability
    deep_freeze(merged)
  end
end

#reload! ⇒ Object

Reload configuration (useful for development/testing)



71
72
73
74
75
# File 'lib/railpack/config.rb', line 71

def reload!
  @config = load_config
  @merged_cache.clear
  self
end

#respond_to_missing?(method, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


112
113
114
115
116
# File 'lib/railpack/config.rb', line 112

def respond_to_missing?(method, include_private = false)
  config_key = method.to_s.chomp('=')
  env = current_env
  for_environment(env).key?(config_key) || super
end

#watch_args(env = current_env) ⇒ Object

Watch command arguments (can be overridden in config)



152
153
154
# File 'lib/railpack/config.rb', line 152

def watch_args(env = current_env)
  build_args(env) # Default to same as build
end

#watch_flags(env = current_env) ⇒ Object

Watch command flags (can be overridden in config)



157
158
159
# File 'lib/railpack/config.rb', line 157

def watch_flags(env = current_env)
  build_flags(env) # Default to same as build
end