ledermann-rails-settings 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +39 -0
- data/MIT-LICENSE +1 -1
- data/README.md +5 -9
- data/Rakefile +1 -1
- data/{ci/Gemfile-rails-6-0 → gemfiles/rails_6_1.gemfile} +1 -1
- data/gemfiles/rails_7_0.gemfile +6 -0
- data/gemfiles/rails_7_1.gemfile +6 -0
- data/lib/generators/rails_settings/migration/migration_generator.rb +5 -4
- data/lib/generators/rails_settings/migration/templates/migration.rb +6 -12
- data/lib/rails-settings/base.rb +19 -10
- data/lib/rails-settings/configuration.rb +23 -10
- data/lib/rails-settings/scopes.rb +10 -11
- data/lib/rails-settings/setting_object.rb +40 -11
- data/lib/rails-settings/version.rb +1 -1
- data/rails-settings.gemspec +15 -14
- data/spec/configuration_spec.rb +54 -53
- data/spec/database.yml +1 -1
- data/spec/queries_spec.rb +9 -17
- data/spec/scopes_spec.rb +18 -10
- data/spec/serialize_spec.rb +13 -11
- data/spec/setting_object_spec.rb +64 -45
- data/spec/settings_spec.rb +106 -47
- data/spec/spec_helper.rb +28 -22
- data/spec/support/matchers/perform_queries.rb +5 -4
- data/spec/support/query_counter.rb +2 -1
- metadata +15 -17
- data/.travis.yml +0 -17
- data/ci/Gemfile-rails-4-2 +0 -7
- data/ci/Gemfile-rails-5-0 +0 -6
- data/ci/Gemfile-rails-5-1 +0 -6
- data/ci/Gemfile-rails-5-2 +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 525e4533dc661bea55c6e6803a14e977041d3028f84c9242a3507f8056caa6a4
|
4
|
+
data.tar.gz: 79f1e5eb504fba6da95b7f3b1d13576851de5e45be5cb1aad941f6a69b025ba9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 642b112d09ca8c17ac941e7423b35c1bd03d686604219cb0c9f96f7e18791ed2fdf55c98ab4f0e0204aa0a85730968420e5726e82ef663797643c75605410b4e
|
7
|
+
data.tar.gz: 1582f37a35eb83b9bba14f9ed34aeb4a0eb1161e66d193584988e46b2cd246ca30f3ac252086e22a62c37fe172118271728606c001f9542b13e41c2b61420d73
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on:
|
4
|
+
- push
|
5
|
+
- pull_request
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
ruby_version:
|
15
|
+
- '3.0'
|
16
|
+
- '3.1'
|
17
|
+
- '3.2'
|
18
|
+
gemfile:
|
19
|
+
- gemfiles/rails_6_1.gemfile
|
20
|
+
- gemfiles/rails_7_0.gemfile
|
21
|
+
- gemfiles/rails_7_1.gemfile
|
22
|
+
|
23
|
+
name: Ruby ${{ matrix.ruby_version }} / Gemfile ${{ matrix.gemfile }}
|
24
|
+
|
25
|
+
env:
|
26
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
27
|
+
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
28
|
+
|
29
|
+
steps:
|
30
|
+
- uses: actions/checkout@v4
|
31
|
+
|
32
|
+
- name: Setup Ruby
|
33
|
+
uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
ruby-version: ${{ matrix.ruby_version }}
|
36
|
+
bundler-cache: true
|
37
|
+
|
38
|
+
- name: RSpec
|
39
|
+
run: bundle exec rake
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Settings for Rails
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/ledermann/rails-settings/actions/workflows/main.yml/badge.svg)](https://github.com/ledermann/rails-settings/actions)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/ledermann/rails-settings.svg)](https://codeclimate.com/github/ledermann/rails-settings)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/ledermann/rails-settings/badge.svg?branch=master)](https://coveralls.io/r/ledermann/rails-settings?branch=master)
|
6
6
|
|
@@ -8,9 +8,8 @@ Ruby gem to handle settings for ActiveRecord instances by storing them as serial
|
|
8
8
|
|
9
9
|
## Requirements
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
- Ruby 3.0 or newer
|
12
|
+
- Rails 6.1 or newer (including Rails 7.0)
|
14
13
|
|
15
14
|
## Installation
|
16
15
|
|
@@ -27,7 +26,6 @@ rails g rails_settings:migration
|
|
27
26
|
rake db:migrate
|
28
27
|
```
|
29
28
|
|
30
|
-
|
31
29
|
## Usage
|
32
30
|
|
33
31
|
### Define settings
|
@@ -103,7 +101,6 @@ user.settings(:dashboard).update! :theme => 'black'
|
|
103
101
|
user.settings(:calendar).update! :scope => 'all', :display => 'daily'
|
104
102
|
```
|
105
103
|
|
106
|
-
|
107
104
|
### Get settings
|
108
105
|
|
109
106
|
```ruby
|
@@ -145,6 +142,7 @@ User.without_settings_for(:calendar)
|
|
145
142
|
```
|
146
143
|
|
147
144
|
### Eager Loading
|
145
|
+
|
148
146
|
```ruby
|
149
147
|
User.includes(:setting_objects)
|
150
148
|
# => Eager load setting_objects when querying many users
|
@@ -156,16 +154,14 @@ Version 2 is a complete rewrite and has a new DSL, so it's **not** compatible wi
|
|
156
154
|
|
157
155
|
If you don't want to upgrade, you find the old version in the [1.x](https://github.com/ledermann/rails-settings/commits/1.x) branch. But don't expect any updates there.
|
158
156
|
|
159
|
-
|
160
157
|
## Changelog
|
161
158
|
|
162
159
|
See https://github.com/ledermann/rails-settings/releases
|
163
160
|
|
164
|
-
|
165
161
|
## License
|
166
162
|
|
167
163
|
MIT License
|
168
164
|
|
169
|
-
Copyright (c) 2012-
|
165
|
+
Copyright (c) 2012-2023 [Georg Ledermann](https://ledermann.dev)
|
170
166
|
|
171
167
|
This gem is a complete rewrite of [rails-settings](https://github.com/Squeegy/rails-settings) by [Alex Wayne](https://github.com/Squeegy)
|
data/Rakefile
CHANGED
@@ -5,18 +5,19 @@ module RailsSettings
|
|
5
5
|
class MigrationGenerator < Rails::Generators::Base
|
6
6
|
include Rails::Generators::Migration
|
7
7
|
|
8
|
-
desc
|
8
|
+
desc 'Generates migration for rails-settings'
|
9
9
|
source_root File.expand_path('../templates', __FILE__)
|
10
10
|
|
11
11
|
def create_migration_file
|
12
|
-
migration_template 'migration.rb',
|
12
|
+
migration_template 'migration.rb',
|
13
|
+
'db/migrate/rails_settings_migration.rb'
|
13
14
|
end
|
14
15
|
|
15
16
|
def self.next_migration_number(dirname)
|
16
17
|
if ActiveRecord::Base.timestamped_migrations
|
17
|
-
Time.now.utc.strftime(
|
18
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S')
|
18
19
|
else
|
19
|
-
|
20
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -1,18 +1,12 @@
|
|
1
|
-
|
2
|
-
ActiveRecord::Migration[5.0]
|
3
|
-
else
|
4
|
-
ActiveRecord::Migration
|
5
|
-
end
|
6
|
-
|
7
|
-
class RailsSettingsMigration < MIGRATION_BASE_CLASS
|
1
|
+
class RailsSettingsMigration < ActiveRecord::Migration[5.0]
|
8
2
|
def self.up
|
9
3
|
create_table :settings do |t|
|
10
|
-
t.string
|
11
|
-
t.text
|
12
|
-
t.references :target, :
|
13
|
-
t.timestamps :
|
4
|
+
t.string :var, null: false
|
5
|
+
t.text :value
|
6
|
+
t.references :target, null: false, polymorphic: true
|
7
|
+
t.timestamps null: true
|
14
8
|
end
|
15
|
-
add_index :settings, [
|
9
|
+
add_index :settings, %i[target_type target_id var], unique: true
|
16
10
|
end
|
17
11
|
|
18
12
|
def self.down
|
data/lib/rails-settings/base.rb
CHANGED
@@ -3,19 +3,23 @@ module RailsSettings
|
|
3
3
|
def self.included(base)
|
4
4
|
base.class_eval do
|
5
5
|
has_many :setting_objects,
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
6
|
+
as: :target,
|
7
|
+
autosave: true,
|
8
|
+
dependent: :delete_all,
|
9
|
+
class_name: self.setting_object_class_name
|
10
10
|
|
11
11
|
def settings(var)
|
12
12
|
raise ArgumentError unless var.is_a?(Symbol)
|
13
|
-
|
13
|
+
unless self.class.default_settings[var]
|
14
|
+
raise ArgumentError.new("Unknown key: #{var}")
|
15
|
+
end
|
14
16
|
|
15
17
|
if RailsSettings.can_protect_attributes?
|
16
|
-
setting_objects.detect { |s| s.var == var.to_s } ||
|
18
|
+
setting_objects.detect { |s| s.var == var.to_s } ||
|
19
|
+
setting_objects.build({ var: var.to_s }, without_protection: true)
|
17
20
|
else
|
18
|
-
setting_objects.detect { |s| s.var == var.to_s } ||
|
21
|
+
setting_objects.detect { |s| s.var == var.to_s } ||
|
22
|
+
setting_objects.build(var: var.to_s, target: self)
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
@@ -27,9 +31,12 @@ module RailsSettings
|
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
def settings?(var=nil)
|
34
|
+
def settings?(var = nil)
|
31
35
|
if var.nil?
|
32
|
-
setting_objects.any?
|
36
|
+
setting_objects.any? do |setting_object|
|
37
|
+
!setting_object.marked_for_destruction? &&
|
38
|
+
setting_object.value.present?
|
39
|
+
end
|
33
40
|
else
|
34
41
|
settings(var).value.present?
|
35
42
|
end
|
@@ -38,7 +45,9 @@ module RailsSettings
|
|
38
45
|
def to_settings_hash
|
39
46
|
settings_hash = self.class.default_settings.dup
|
40
47
|
settings_hash.each do |var, vals|
|
41
|
-
settings_hash[var] = settings_hash[var].merge(
|
48
|
+
settings_hash[var] = settings_hash[var].merge(
|
49
|
+
settings(var.to_sym).value,
|
50
|
+
)
|
42
51
|
end
|
43
52
|
settings_hash
|
44
53
|
end
|
@@ -10,30 +10,43 @@ module RailsSettings
|
|
10
10
|
@klass = klass
|
11
11
|
|
12
12
|
if options[:persistent]
|
13
|
-
|
13
|
+
unless @klass.methods.include?(:default_settings)
|
14
|
+
@klass.class_attribute :default_settings
|
15
|
+
end
|
14
16
|
else
|
15
17
|
@klass.class_attribute :default_settings
|
16
18
|
end
|
17
19
|
|
18
20
|
@klass.class_attribute :setting_object_class_name
|
19
21
|
@klass.default_settings ||= {}
|
20
|
-
@klass.setting_object_class_name =
|
22
|
+
@klass.setting_object_class_name =
|
23
|
+
options[:class_name] || 'RailsSettings::SettingObject'
|
21
24
|
|
22
25
|
if block_given?
|
23
26
|
yield(self)
|
24
27
|
else
|
25
|
-
keys.each
|
26
|
-
key(k)
|
27
|
-
end
|
28
|
+
keys.each { |k| key(k) }
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
+
if @klass.default_settings.blank?
|
32
|
+
raise ArgumentError.new('has_settings: No keys defined')
|
33
|
+
end
|
31
34
|
end
|
32
35
|
|
33
|
-
def key(name, options={})
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
def key(name, options = {})
|
37
|
+
unless name.is_a?(Symbol)
|
38
|
+
raise ArgumentError.new(
|
39
|
+
"has_settings: Symbol expected, but got a #{name.class}",
|
40
|
+
)
|
41
|
+
end
|
42
|
+
unless options.blank? || (options.keys == [:defaults])
|
43
|
+
raise ArgumentError.new(
|
44
|
+
"has_settings: Option :defaults expected, but got #{options.keys.join(', ')}",
|
45
|
+
)
|
46
|
+
end
|
47
|
+
@klass.default_settings[name] = (
|
48
|
+
options[:defaults] || {}
|
49
|
+
).stringify_keys.freeze
|
37
50
|
end
|
38
51
|
end
|
39
52
|
end
|
@@ -2,28 +2,27 @@ module RailsSettings
|
|
2
2
|
module Scopes
|
3
3
|
def with_settings
|
4
4
|
result = joins("INNER JOIN settings ON #{settings_join_condition}")
|
5
|
-
|
6
|
-
if ActiveRecord::VERSION::MAJOR < 5
|
7
|
-
result.uniq
|
8
|
-
else
|
9
|
-
result.distinct
|
10
|
-
end
|
5
|
+
result.distinct
|
11
6
|
end
|
12
7
|
|
13
8
|
def with_settings_for(var)
|
14
9
|
raise ArgumentError.new('Symbol expected!') unless var.is_a?(Symbol)
|
15
|
-
joins(
|
10
|
+
joins(
|
11
|
+
"INNER JOIN settings ON #{settings_join_condition} AND settings.var = '#{var}'",
|
12
|
+
)
|
16
13
|
end
|
17
14
|
|
18
15
|
def without_settings
|
19
|
-
joins("LEFT JOIN settings ON #{settings_join_condition}").
|
20
|
-
|
16
|
+
joins("LEFT JOIN settings ON #{settings_join_condition}").where(
|
17
|
+
'settings.id IS NULL',
|
18
|
+
)
|
21
19
|
end
|
22
20
|
|
23
21
|
def without_settings_for(var)
|
24
22
|
raise ArgumentError.new('Symbol expected!') unless var.is_a?(Symbol)
|
25
|
-
joins(
|
26
|
-
|
23
|
+
joins(
|
24
|
+
"LEFT JOIN settings ON #{settings_join_condition} AND settings.var = '#{var}'",
|
25
|
+
).where('settings.id IS NULL')
|
27
26
|
end
|
28
27
|
|
29
28
|
def settings_join_condition
|
@@ -2,18 +2,22 @@ module RailsSettings
|
|
2
2
|
class SettingObject < ActiveRecord::Base
|
3
3
|
self.table_name = 'settings'
|
4
4
|
|
5
|
-
belongs_to :target, :
|
5
|
+
belongs_to :target, polymorphic: true
|
6
6
|
|
7
7
|
validates_presence_of :var, :target_type
|
8
8
|
validate do
|
9
|
-
errors.add(:value,
|
9
|
+
errors.add(:value, 'Invalid setting value') unless value.is_a? Hash
|
10
10
|
|
11
11
|
unless _target_class.default_settings[var.to_sym]
|
12
12
|
errors.add(:var, "#{var} is not defined!")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
if ActiveRecord.version >= Gem::Version.new('7.1.0.beta1')
|
17
|
+
serialize :value, type: Hash
|
18
|
+
else
|
19
|
+
serialize :value, Hash
|
20
|
+
end
|
17
21
|
|
18
22
|
if RailsSettings.can_protect_attributes?
|
19
23
|
# attr_protected can not be used here because it touches the database which is not connected yet.
|
@@ -21,10 +25,10 @@ module RailsSettings
|
|
21
25
|
attr_accessible
|
22
26
|
end
|
23
27
|
|
24
|
-
REGEX_SETTER = /\A([a-z]\w
|
25
|
-
REGEX_GETTER = /\A([a-z]\w
|
28
|
+
REGEX_SETTER = /\A([a-z]\w*)=\Z/i
|
29
|
+
REGEX_GETTER = /\A([a-z]\w*)\Z/i
|
26
30
|
|
27
|
-
def respond_to?(method_name, include_priv=false)
|
31
|
+
def respond_to?(method_name, include_priv = false)
|
28
32
|
super || method_name.to_s =~ REGEX_SETTER || _setting?(method_name)
|
29
33
|
end
|
30
34
|
|
@@ -32,7 +36,7 @@ module RailsSettings
|
|
32
36
|
if block_given?
|
33
37
|
super
|
34
38
|
else
|
35
|
-
if attribute_names.include?(method_name.to_s.sub('=',''))
|
39
|
+
if attribute_names.include?(method_name.to_s.sub('=', ''))
|
36
40
|
super
|
37
41
|
elsif method_name.to_s =~ REGEX_SETTER && args.size == 1
|
38
42
|
_set_value($1, args.first)
|
@@ -44,23 +48,48 @@ module RailsSettings
|
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
47
|
-
|
51
|
+
protected
|
52
|
+
|
48
53
|
if RailsSettings.can_protect_attributes?
|
49
54
|
# Simulate attr_protected by removing all regular attributes
|
50
55
|
def sanitize_for_mass_assignment(attributes, role = nil)
|
51
|
-
attributes.except(
|
56
|
+
attributes.except(
|
57
|
+
'id',
|
58
|
+
'var',
|
59
|
+
'value',
|
60
|
+
'target_id',
|
61
|
+
'target_type',
|
62
|
+
'created_at',
|
63
|
+
'updated_at',
|
64
|
+
)
|
52
65
|
end
|
53
66
|
end
|
54
67
|
|
55
|
-
|
68
|
+
private
|
69
|
+
|
56
70
|
def _get_value(name)
|
57
71
|
if value[name].nil?
|
58
|
-
|
72
|
+
default_value = _get_default_value(name)
|
73
|
+
_deep_dup(default_value)
|
59
74
|
else
|
60
75
|
value[name]
|
61
76
|
end
|
62
77
|
end
|
63
78
|
|
79
|
+
def _get_default_value(name)
|
80
|
+
default_value = _target_class.default_settings[var.to_sym][name]
|
81
|
+
|
82
|
+
if default_value.respond_to?(:call)
|
83
|
+
default_value.call(target)
|
84
|
+
else
|
85
|
+
default_value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _deep_dup(nested_hashes_and_or_arrays)
|
90
|
+
Marshal.load(Marshal.dump(nested_hashes_and_or_arrays))
|
91
|
+
end
|
92
|
+
|
64
93
|
def _set_value(name, v)
|
65
94
|
if value[name] != v
|
66
95
|
value_will_change!
|
data/rails-settings.gemspec
CHANGED
@@ -4,26 +4,27 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'rails-settings/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
|
-
gem.name
|
8
|
-
gem.version
|
9
|
-
gem.licenses
|
10
|
-
gem.authors
|
11
|
-
gem.email
|
12
|
-
gem.description
|
13
|
-
gem.summary
|
14
|
-
|
15
|
-
gem.
|
7
|
+
gem.name = 'ledermann-rails-settings'
|
8
|
+
gem.version = RailsSettings::VERSION
|
9
|
+
gem.licenses = ['MIT']
|
10
|
+
gem.authors = ['Georg Ledermann']
|
11
|
+
gem.email = ['georg@ledermann.dev']
|
12
|
+
gem.description = 'Settings gem for Ruby on Rails'
|
13
|
+
gem.summary =
|
14
|
+
'Ruby gem to handle settings for ActiveRecord instances by storing them as serialized Hash in a separate database table. Namespaces and defaults included.'
|
15
|
+
gem.homepage = 'https://github.com/ledermann/rails-settings'
|
16
|
+
gem.required_ruby_version = '>= 3.0'
|
16
17
|
|
17
|
-
gem.files
|
18
|
-
gem.executables
|
19
|
-
gem.test_files
|
18
|
+
gem.files = `git ls-files`.split($/)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
21
|
gem.require_paths = ['lib']
|
21
22
|
|
22
|
-
gem.add_dependency 'activerecord', '>=
|
23
|
+
gem.add_dependency 'activerecord', '>= 6.1'
|
23
24
|
|
24
25
|
gem.add_development_dependency 'rake'
|
25
26
|
gem.add_development_dependency 'sqlite3'
|
26
27
|
gem.add_development_dependency 'rspec'
|
27
|
-
gem.add_development_dependency '
|
28
|
+
gem.add_development_dependency 'coveralls_reborn'
|
28
29
|
gem.add_development_dependency 'simplecov', '>= 0.11.2'
|
29
30
|
end
|