run_time_settings 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0547302343cffb55114807f167926c1dda688718f572b985e56605c9020ae155
4
+ data.tar.gz: d0abf2cb43695acb5f42fca3d82e1417a7943ec163f3b27430f536f4ab48af31
5
+ SHA512:
6
+ metadata.gz: 8f3d032ed5789b10d4616e0029bba210e940b1066ad8752395ec911e669a94d1c7cc84bb5b00c1d00016412e4cd9c06f05af498cd1f66182c0c739de91b52c37
7
+ data.tar.gz: dfae24669973f55748e23e69c89203fc722d98084a0479a86ed7833d018397d03b1776680770ada5149e1efae2a9898c4434e1d6c5cf5d0cc7e04eae5ab6b5b2
@@ -0,0 +1,120 @@
1
+ ![RSpec](https://github.com/RockSolt/run_time_settings/workflows/RSpec/badge.svg)
2
+ ![RuboCop](https://github.com/RockSolt/run_time_settings/workflows/RuboCop/badge.svg)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/73c3c733ef1f3173fff3/maintainability)](https://codeclimate.com/github/RockSolt/run_time_settings/maintainability)
4
+
5
+ # RunTimeSettings
6
+
7
+ Run time is the most important part. If you're okay with doing a deployment to update a setting, then that sounds like an environment variable (for which there are lots of good solutions). However, if you find yourself wanting to change a value more frequently, or you want someone without the ability to do a deployment to be able to change a value, then run-time settings might be for you.
8
+
9
+ In the end these are just namespaced key-value pairs stored in the database. You could create a settings model (and we do!) with a key attribute and a value attribute and you'd have just about the same thing. But it's ruby and it's rails, so you want a few more features from your settings:
10
+
11
+ - type castings so that 'true' returns true and '42' return 42
12
+ - default values
13
+ - predicate methods for boolean settings
14
+
15
+ ## Usage
16
+
17
+ Settings can be declared on any class—a PORO or an ActiveRecord can declare settings.
18
+
19
+ ```ruby
20
+ class BatchJob
21
+ setting :last_run, :date
22
+ setting :enabled, :boolean, default: false
23
+ setting :percentage, :float
24
+ end
25
+ ```
26
+
27
+ The settings are then available via accessors on the class:
28
+
29
+ ```ruby
30
+ BatchJob.last_run = Date.today
31
+ BatchJob.enabled?
32
+ BatchJob.percentage
33
+ ```
34
+
35
+ ### Types
36
+
37
+ The following types are available to use:
38
+ - big_integer
39
+ - boolean
40
+ - date
41
+ - datetime
42
+ - decimal
43
+ - float
44
+ - integer
45
+ - string
46
+ - time
47
+
48
+ Note: The <tt>time</tt> type does not store milliseconds.
49
+
50
+ ## Installation
51
+
52
+ Add this line to your application's Gemfile:
53
+
54
+ ```ruby
55
+ gem 'run_time_settings'
56
+ ```
57
+
58
+ And then execute:
59
+
60
+ $ bundle install
61
+
62
+ Or install it yourself as:
63
+
64
+ $ gem install run_time_settings
65
+
66
+ ### Migration for `run_time_settings` Table
67
+
68
+ The table must be created before settings can be used. Run the following command to generate a migration for the `run_time_settings` table:
69
+
70
+ rails g migration CreateRunTimeSettings namespace:string key_name:string db_value:string
71
+
72
+ It should generate a migration that looks something like this:
73
+
74
+ ```ruby
75
+ class CreateRunTimeSettings < ActiveRecord::Migration[6.0]
76
+ def change
77
+ create_table :run_time_settings do |t|
78
+ t.string :namespace
79
+ t.string :key_name
80
+ t.string :db_value
81
+ t.timestamps
82
+ end
83
+ end
84
+ end
85
+ ```
86
+
87
+
88
+ ## Development
89
+
90
+ Guard runs both [RuboCop](https://docs.rubocop.org/en/stable/) and [RSpec](https://relishapp.com/rspec). Rubocop is
91
+ configured to auto-correct safe issues.
92
+
93
+ Start [Guard](https://github.com/guard/guard) at the command line: `bundle exec guard`
94
+
95
+ Both can be run manually from the command line:
96
+
97
+ `rubocop`
98
+
99
+ `bundle exec rspec`
100
+
101
+ Run the console with the following:
102
+
103
+ `bundle exec rake console`
104
+
105
+ ### Database
106
+
107
+ The tests (and the console) use an in-memory database, so there is no prior setup required. It gets created each time.
108
+
109
+ ## Contributing
110
+
111
+ Bug reports and pull requests are welcome on GitHub at https://github.com/RockSolt/run_time_settings. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/RockSolt/run_time_settings/blob/master/CODE_OF_CONDUCT.md).
112
+
113
+
114
+ ## License
115
+
116
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
117
+
118
+ ## Code of Conduct
119
+
120
+ Everyone interacting in the RunTimeSettings project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/RockSolt/run_time_settings/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ require 'irb'
10
+ require 'irb/completion'
11
+ require 'run_time_settings'
12
+ require_relative 'spec/database'
13
+ ARGV.clear
14
+ IRB.start
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'run_time_settings/version'
4
+
5
+ require 'run_time_settings/persistent_attributes'
6
+ require 'run_time_settings/settings'
7
+
8
+ module RunTimeSettings
9
+ class Error < StandardError; end
10
+ # Your code goes here...
11
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'run_time_settings/type/time'
5
+
6
+ module RunTimeSettings
7
+ # = Persistent Attributes
8
+ module PersistentAttributes
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ # Specifies a class-level, persistent attribute. The class (not the
13
+ # instance) receives getter and setter methods for the setting.
14
+ # Additionally, boolean settings add predicate methods.
15
+ #
16
+ # +setting_name+ The name of the setting.
17
+ #
18
+ # +type+ A symbol such as +:string+ or +:integer+, or a type object to be
19
+ # used for this attribute. See ActiveModel::Type for list of symbols /
20
+ # delivered types.
21
+ #
22
+ # === Options
23
+ # The following options are accepted:
24
+ #
25
+ # +default+ The default value to use when no value is provided.
26
+ #
27
+ # When using a symbol for +cast_type+, extra options are forwarded to the
28
+ # constructor of the type object.
29
+ #
30
+ # === Examples
31
+ #
32
+ # A BatchJob class declares <tt>setting :last_run, :date</tt>, which will
33
+ # add the following:
34
+ # * <tt>BatchJob.last_run</tt>
35
+ # * <tt>BatchJob.last_run=(date)</tt>
36
+ #
37
+ # A BatchJob class declares <tt>setting :enabled, :boolean, default: false</tt>,
38
+ # which will add the following:
39
+ # * <tt>BatchJob.enabled</tt>
40
+ # * <tt>BatchJob.enabled=(boolean)</tt>
41
+ # * <tt>BatchJob.enabled?</tt>
42
+ def setting(setting_name, type = ActiveModel::Type::Value.new, **options)
43
+ setting_name = setting_name.to_s
44
+ type = RunTimeSettings::Type::Time.new if type == :time
45
+ type = ActiveModel::Type.lookup(type, **options.except(:default)) if type.is_a?(Symbol)
46
+
47
+ RunTimeSettings::Settings.add_setting_type(name, setting_name, type)
48
+ define_setting_accessors(setting_name, options[:default], type.is_a?(ActiveModel::Type::Boolean))
49
+ end
50
+
51
+ private
52
+
53
+ def define_setting_accessors(setting_name, default, boolean)
54
+ define_singleton_method(setting_name) do
55
+ value = RunTimeSettings::Settings.read(name, setting_name)
56
+ value.nil? ? default : value
57
+ end
58
+
59
+ define_singleton_method("#{setting_name}?") { public_send(setting_name) } if boolean
60
+
61
+ define_singleton_method("#{setting_name}=") do |value|
62
+ RunTimeSettings::Settings.write(name, setting_name, value)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+
5
+ module RunTimeSettings
6
+ # Settings
7
+ #
8
+ # Model settings holds namespaced key-value pairs. This acts as the data store for the PersistentAttributes mixin.
9
+ # See RunTimeSettings::PersistentAttributes.settings for information.
10
+ class Settings < ActiveRecord::Base
11
+ self.table_name = :run_time_settings
12
+
13
+ class << self
14
+ def add_setting_type(namespace, key, type)
15
+ setting_types[[namespace, key]] = type
16
+ end
17
+
18
+ def read(namespace, key)
19
+ value = RunTimeSettings::Settings.find_by(namespace: namespace, key_name: key)&.db_value
20
+ type_for(namespace, key).deserialize(value) if value.present?
21
+ end
22
+
23
+ def write(namespace, key, value)
24
+ setting = RunTimeSettings::Settings.find_or_initialize_by(namespace: namespace, key_name: key)
25
+ setting.update!(db_value: type_for(namespace, key).serialize(value))
26
+ end
27
+
28
+ private
29
+
30
+ def type_for(namespace, key)
31
+ setting_types[[namespace, key]]
32
+ end
33
+
34
+ def setting_types
35
+ @setting_types ||= {}
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunTimeSettings
4
+ module Type
5
+ # = Time
6
+ #
7
+ # Custom type caster for Time. Values are converted to an integer representing seconds since epoch for storage in
8
+ # the database, then converted back to Time on the way out.
9
+ #
10
+ # This is only accurate to the second; milliseconds are lost.
11
+ class Time
12
+ # called by read, should return Time instance
13
+ def deserialize(value)
14
+ ::Time.at(value.to_i)
15
+ end
16
+
17
+ # called by write, should convert to unix epoch for storage in database
18
+ def serialize(value)
19
+ value.to_i
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunTimeSettings
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: run_time_settings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Todd Kummer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: guard-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.90.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.90.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.19'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.19'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sqlite3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.4.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.4.2
139
+ description: Easily add and manage application-level settings
140
+ email:
141
+ - todd@rockridgesolutions.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - README.md
147
+ - Rakefile
148
+ - lib/run_time_settings.rb
149
+ - lib/run_time_settings/persistent_attributes.rb
150
+ - lib/run_time_settings/settings.rb
151
+ - lib/run_time_settings/type/time.rb
152
+ - lib/run_time_settings/version.rb
153
+ homepage: https://github.com/RockSolt/run_time_settings
154
+ licenses:
155
+ - MIT
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: 2.3.0
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubygems_version: 3.1.2
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Persistent, run-time application settings
176
+ test_files: []