ruby-settings-cached 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba4e9ee304a2b3dad3de28b7991da2385eb5f3e9
4
+ data.tar.gz: 034bf0a2d72024d4ed6762ae83a753bb27ec9c52
5
+ SHA512:
6
+ metadata.gz: 5047728194298915cb178c2a93c7e1bb3d81375a905b152f8399cb87476d2458eb06e50d721850f92888b344d90dda492a508b2e6f66f211d0e4f03bd1e05194
7
+ data.tar.gz: 125508ee57f87528a0c3a3c49be8f0b049c1656983f62b6d2985a448a15d96c58ce02bb1dbfcb404970a9c84b2ce7919f470d0ca974f44725c167ee547d13e50
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # Settings Gem
2
+
3
+ This is improved from [rails-settings](https://github.com/ledermann/rails-settings),
4
+ added caching for all settings. Settings is a plugin that makes managing a table of
5
+ global key, value pairs easy. Think of it like a global Hash stored in your database,
6
+ that uses simple ActiveRecord like methods for manipulation. Keep track of any global
7
+ setting that you dont want to hard code into your rails app. You can store any kind
8
+ of object. Strings, numbers, arrays, or any object.
9
+
10
+ ## Status
11
+
12
+ - [![Gem Version](https://badge.fury.io/rb/rails-settings-cached.png)](https://rubygems.org/gems/rails-settings-cached)
13
+ - [![CI Status](https://travis-ci.org/RobotJiang/ruby-settings-cached.svg)](https://travis-ci.org/RobotJiang/ruby-settings-cached)
14
+
15
+ ## Setup
16
+
17
+ Edit your Gemfile:
18
+
19
+ ```ruby
20
+ # Rails 4+ project or Only ruby project
21
+ gem "ruby-settings-cached", "0.1"
22
+ ```
23
+
24
+ Generate your settings if you use Rails:
25
+
26
+ ```bash
27
+ $ rails g settings <settings_name>
28
+ ```
29
+
30
+ ```
31
+
32
+ Now just put that migration in the database with:
33
+
34
+ ```bash
35
+ rake db:migrate
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ The syntax is easy. First, lets create some settings to keep track of:
41
+
42
+ ```ruby
43
+ Setting.admin_password = 'supersecret'
44
+ Setting.date_format = '%m %d, %Y'
45
+ Setting.cocktails = ['Martini', 'Screwdriver', 'White Russian']
46
+ Setting.foo = 123
47
+ Setting.credentials = { :username => 'tom', :password => 'secret' }
48
+ ```
49
+
50
+ Now lets read them back:
51
+
52
+ ```ruby
53
+ Setting.foo # returns 123
54
+ ```
55
+
56
+ Changing an existing setting is the same as creating a new setting:
57
+
58
+ ```ruby
59
+ Setting.foo = 'super duper bar'
60
+ ```
61
+
62
+ For changing an existing setting which is a Hash, you can merge new values with existing ones:
63
+
64
+ ```ruby
65
+ Setting.merge!(:credentials, :password => 'topsecret')
66
+ Setting.credentials # returns { :username => 'tom', :password => 'topsecret' }
67
+ ```
68
+
69
+ Decide you dont want to track a particular setting anymore?
70
+
71
+ ```ruby
72
+ Setting.destroy :foo
73
+ Setting.foo # returns nil
74
+ ```
75
+
76
+ Want a list of all the settings?
77
+ ```ruby
78
+ # Rails 4.1.x
79
+ Setting.get_all
80
+ # Rails 3.x and 4.0.x
81
+ Setting.all
82
+ # returns {'admin_password' => 'super_secret', 'date_format' => '%m %d, %Y'}
83
+ ```
84
+
85
+ You need name spaces and want a list of settings for a give name space? Just choose your prefered named space delimiter and use `Setting.get_all` (`Settings.all` for # Rails 3.x and 4.0.x) like this:
86
+
87
+ ```ruby
88
+ Setting['preferences.color'] = :blue
89
+ Setting['preferences.size'] = :large
90
+ Setting['license.key'] = 'ABC-DEF'
91
+ # Rails 4.1.x
92
+ Setting.get_all('preferences.')
93
+ # Rails 3.x and 4.0.x
94
+ Setting.all('preferences.')
95
+ # returns { 'preferences.color' => :blue, 'preferences.size' => :large }
96
+ ```
97
+
98
+ Set defaults for certain settings of your app. This will cause the defined settings to return with the
99
+ Specified value even if they are **not in the database**. Make a new file in `config/initializers/default_settings.rb`
100
+ with the following:
101
+
102
+ ```ruby
103
+ Setting.defaults[:some_setting] = 'footastic'
104
+ Setting.where(:var => "some_setting").count
105
+ => 0
106
+ Setting.some_setting
107
+ => "footastic"
108
+ ```
109
+
110
+ Init default value in database, this has indifferent with `Setting.defaults[:some_setting]`, this will **save the value into database**:
111
+
112
+ ```ruby
113
+ Setting.save_default(:some_key, "123")
114
+ Setting.where(:var => "some_key").count
115
+ => 1
116
+ Setting.some_key
117
+ => "123"
118
+ ```
119
+
120
+ Settings may be bound to any existing ActiveRecord object. Define this association like this:
121
+ Notice! is not do caching in this version.
122
+
123
+ ```ruby
124
+ class User < ActiveRecord::Base
125
+ include RailsSettings::Extend
126
+ end
127
+ ```
128
+
129
+ Then you can set/get a setting for a given user instance just by doing this:
130
+
131
+ ```ruby
132
+ user = User.find(123)
133
+ user.settings.color = :red
134
+ user.settings.color # returns :red
135
+ # Rails 4.1.x
136
+ user.settings.get_all
137
+ # Rails 3.x and 4.0.x
138
+ user.settings.all
139
+ # { "color" => :red }
140
+ ```
141
+
142
+ If you want to find users having or not having some settings, there are named scopes for this:
143
+
144
+ ```ruby
145
+ User.with_settings
146
+ # => returns a scope of users having any setting
147
+
148
+ User.with_settings_for('color')
149
+ # => returns a scope of users having a 'color' setting
150
+
151
+ User.without_settings
152
+ # returns a scope of users having no setting at all (means user.settings.get_all == {})
153
+
154
+ User.without_settings('color')
155
+ # returns a scope of users having no 'color' setting (means user.settings.color == nil)
156
+ ```
157
+
158
+ Settings maybe dynamically scoped. For example, if you're using [apartment gem](https://github.com/influitive/apartment) for multitenancy, you may not want tenants to share settings:
159
+
160
+ ```ruby
161
+ class Settings < RailsSettings::CachedSettings
162
+ cache_prefix { Apartment::Tenant.current }
163
+ ...
164
+ end
165
+ ```
166
+
167
+ -----
168
+
169
+ ## How to create a list, form to manage Settings?
170
+
171
+ If you want create an admin interface to editing the Settings, you can try methods in follow:
172
+
173
+ ```ruby
174
+ class SettingsController < ApplicationController
175
+ def index
176
+ # to get all items for render list
177
+ @settings = Setting.unscoped
178
+ end
179
+
180
+ def edit
181
+ @setting = Setting.unscoped.find(params[:id])
182
+ end
183
+ end
184
+ ```
185
+
186
+
187
+ Also you may use [rails-settings-ui](https://github.com/accessd/rails-settings-ui) gem
188
+ for building ready to using interface with validations.
189
+
@@ -0,0 +1,32 @@
1
+ require 'rails/generators/migration'
2
+
3
+ if defined?(Rails)
4
+ class SettingsGenerator < Rails::Generators::NamedBase
5
+ include Rails::Generators::Migration
6
+
7
+ argument :name, type: :string, default: 'my_settings'
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ @@migrations = false
12
+
13
+ def self.next_migration_number(dirname) #:nodoc:
14
+ if ActiveRecord::Base.timestamped_migrations
15
+ if @@migrations
16
+ (current_migration_number(dirname) + 1)
17
+ else
18
+ @@migrations = true
19
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
20
+ end
21
+ else
22
+ format '%.3d', current_migration_number(dirname) + 1
23
+ end
24
+ end
25
+
26
+ def settings
27
+ # generate(:model, name, "--skip-migration")
28
+ template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb"), force: true
29
+ migration_template 'migration.rb', 'db/migrate/create_settings.rb'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ class CreateSettings < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :settings do |t|
4
+ t.string :var, null: false
5
+ t.text :value, null: true
6
+ t.integer :thing_id, null: true
7
+ t.string :thing_type, null: true, limit: 30
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :settings, %i(thing_type thing_id var), unique: true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :settings
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ # RubySettings Model
2
+ class <%= class_name %> < RubySettings::CachedSettings
3
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'ruby-settings/settings'
2
+ require_relative 'ruby-settings/configuration'
3
+ require_relative 'ruby-settings/cached_settings'
4
+ require_relative 'ruby-settings/scoped_settings'
5
+ require_relative 'ruby-settings/extend'
6
+
7
+
@@ -0,0 +1,51 @@
1
+ module RubySettings
2
+ class CachedSettings < Settings
3
+ after_update :rewrite_cache
4
+ after_create :rewrite_cache
5
+ after_destroy :expire_cache
6
+
7
+ include RubySettings::ConfigurationHelpers
8
+
9
+ def rewrite_cache
10
+ cache_store.write(cache_key, value)
11
+ end
12
+
13
+ def expire_cache
14
+ cache_store.delete(cache_key)
15
+ end
16
+
17
+ def cache_key
18
+ self.class.cache_key(var, thing)
19
+ end
20
+
21
+ class << self
22
+ def cache_prefix(&block)
23
+ @cache_prefix = block
24
+ end
25
+
26
+ def cache_key(var_name, scope_object)
27
+ scope = "rails_settings_cached:"
28
+ scope << "#{@cache_prefix.call}:" if @cache_prefix
29
+ scope << "#{scope_object.class.name}-#{scope_object.id}:" if scope_object
30
+ scope << "#{var_name}"
31
+ end
32
+
33
+ def [](var_name)
34
+ value = RubySettings.config.cache_store.fetch(cache_key(var_name, @object)) do
35
+ super(var_name)
36
+ end
37
+
38
+ if value.nil?
39
+ @@defaults[var_name.to_s] if value.nil?
40
+ else
41
+ value
42
+ end
43
+ end
44
+
45
+ def save_default(key, value)
46
+ return false unless self[key].nil?
47
+ self[key] = value
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ module RubySettings
2
+
3
+ class << self
4
+
5
+ attr_accessor :configuration
6
+
7
+ def config
8
+ self.configuration ||= Configuration.new
9
+ end
10
+
11
+ def configure
12
+ yield config if block_given?
13
+ end
14
+
15
+ end
16
+
17
+ class Configuration
18
+ attr_accessor :cache_store
19
+ end
20
+
21
+ module ConfigurationHelpers
22
+ extend ActiveSupport::Concern
23
+
24
+ def cache_store
25
+ @cache_store ||= (RubySettings.config.cache_store || ActiveSupport::Cache::MemoryStore.new)
26
+ end
27
+
28
+ end
29
+ end
30
+
@@ -0,0 +1,34 @@
1
+ module RubySettings
2
+ module Extend
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ scope :with_settings, lambda {
7
+ joins("JOIN settings ON (settings.thing_id = #{table_name}.#{primary_key} AND
8
+ settings.thing_type = '#{base_class.name}')")
9
+ .select("DISTINCT #{table_name}.*")
10
+ }
11
+
12
+ scope :with_settings_for, lambda { |var|
13
+ joins("JOIN settings ON (settings.thing_id = #{table_name}.#{primary_key} AND
14
+ settings.thing_type = '#{base_class.name}') AND settings.var = '#{var}'")
15
+ }
16
+
17
+ scope :without_settings, lambda {
18
+ joins("LEFT JOIN settings ON (settings.thing_id = #{table_name}.#{primary_key} AND
19
+ settings.thing_type = '#{base_class.name}')")
20
+ .where('settings.id IS NULL')
21
+ }
22
+
23
+ scope :without_settings_for, lambda { |var|
24
+ where('settings.id IS NULL')
25
+ .joins("LEFT JOIN settings ON (settings.thing_id = #{table_name}.#{primary_key} AND
26
+ settings.thing_type = '#{base_class.name}') AND settings.var = '#{var}'")
27
+ }
28
+ end
29
+
30
+ def settings
31
+ ScopedSettings.for_thing(self)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ module RubySettings
2
+ class ScopedSettings < CachedSettings
3
+ def self.for_thing(object)
4
+ @object = object
5
+ self
6
+ end
7
+
8
+ def self.thing_scoped
9
+ unscoped.where(thing_type: @object.class.base_class.to_s, thing_id: @object.id)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,110 @@
1
+ module RubySettings
2
+ class Settings < ActiveRecord::Base
3
+ self.table_name = table_name_prefix + 'settings'
4
+
5
+ class SettingNotFound < RuntimeError; end
6
+
7
+ cattr_accessor :defaults
8
+ @@defaults = {}.with_indifferent_access
9
+
10
+ belongs_to :thing, polymorphic: true
11
+
12
+ # Support old plugin
13
+ if defined?(SettingsDefaults::DEFAULTS)
14
+ @@defaults = SettingsDefaults::DEFAULTS.with_indifferent_access
15
+ end
16
+
17
+ # get the value field, YAML decoded
18
+ def value
19
+ YAML.load(self[:value])
20
+ end
21
+
22
+ # set the value field, YAML encoded
23
+ def value=(new_value)
24
+ self[:value] = new_value.to_yaml
25
+ end
26
+
27
+ class << self
28
+ # get or set a variable with the variable as the called method
29
+ def method_missing(method, *args)
30
+ method_name = method.to_s
31
+ super(method, *args)
32
+ rescue NoMethodError
33
+ # set a value for a variable
34
+ if method_name[-1] == '='
35
+ var_name = method_name.sub('=', '')
36
+ value = args.first
37
+ self[var_name] = value
38
+ else
39
+ # retrieve a value
40
+ self[method_name]
41
+ end
42
+ end
43
+
44
+ # destroy the specified settings record
45
+ def destroy(var_name)
46
+ var_name = var_name.to_s
47
+ obj = object(var_name)
48
+ raise SettingNotFound, "Setting variable \"#{var_name}\" not found" if obj.nil?
49
+
50
+ obj.destroy
51
+ true
52
+ end
53
+
54
+ # retrieve all settings as a hash (optionally starting with a given namespace)
55
+ def get_all(starting_with = nil)
56
+ vars = thing_scoped.select('var, value')
57
+ vars = vars.where("var LIKE '#{starting_with}%'") if starting_with
58
+
59
+ result = {}
60
+ vars.each do |record|
61
+ result[record.var] = record.value
62
+ end
63
+ result.merge! @@defaults.slice(*(@@defaults.keys - result.keys))
64
+
65
+ result.with_indifferent_access
66
+ end
67
+
68
+ def where(sql = nil)
69
+ vars = thing_scoped.where(sql) if sql
70
+ vars
71
+ end
72
+
73
+ # get a setting value by [] notation
74
+ def [](var_name)
75
+ object(var_name).try(:value) || @@defaults[var_name.to_s]
76
+ end
77
+
78
+ # set a setting value by [] notation
79
+ def []=(var_name, value)
80
+ var_name = var_name.to_s
81
+
82
+ record = object(var_name) || thing_scoped.new(var: var_name)
83
+ record.value = value
84
+ record.save!
85
+
86
+ value
87
+ end
88
+
89
+ def merge!(var_name, hash_value)
90
+ raise ArgumentError unless hash_value.is_a?(Hash)
91
+
92
+ old_value = self[var_name] || {}
93
+ raise TypeError, "Existing value is not a hash, can't merge!" unless old_value.is_a?(Hash)
94
+
95
+ new_value = old_value.merge(hash_value)
96
+ self[var_name] = new_value if new_value != old_value
97
+
98
+ new_value
99
+ end
100
+
101
+ def object(var_name)
102
+ thing_scoped.where(var: var_name.to_s).first
103
+ end
104
+
105
+ def thing_scoped
106
+ unscoped.where('thing_type is NULL and thing_id is NULL')
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-settings-cached
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Squeegy
8
+ - Georg Ledermann
9
+ - 100hz
10
+ - Jason Lee
11
+ - Raymond Jiang
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2015-12-15 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: activerecord
19
+ requirement: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 4.2.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 4.2.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: activesupport
33
+ requirement: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 4.2.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 4.2.0
45
+ - !ruby/object:Gem::Dependency
46
+ name: rake
47
+ requirement: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ - !ruby/object:Gem::Dependency
60
+ name: rspec
61
+ requirement: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 3.3.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.3.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: rubocop
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ type: :development
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ - !ruby/object:Gem::Dependency
88
+ name: sqlite3
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.10
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.3.10
101
+ description:
102
+ email: huacnlee@gmail.com
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - README.md
108
+ - lib/generators/settings/settings_generator.rb
109
+ - lib/generators/settings/templates/migration.rb
110
+ - lib/generators/settings/templates/model.rb
111
+ - lib/ruby-settings-cached.rb
112
+ - lib/ruby-settings/cached_settings.rb
113
+ - lib/ruby-settings/configuration.rb
114
+ - lib/ruby-settings/extend.rb
115
+ - lib/ruby-settings/scoped_settings.rb
116
+ - lib/ruby-settings/settings.rb
117
+ homepage: https://github.com/RobotJiang/ruby-settings-cached
118
+ licenses: []
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '2.0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.4.5
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: This is improved from rails-settings, added caching. Settings is a plugin
140
+ that makes managing a table of global key, value pairs easy. Think of it like a
141
+ global Hash stored in you database, that uses simple ActiveRecord like methods for
142
+ manipulation. Keep track of any global setting that you dont want to hard code
143
+ into your rails app. You can store any kind of object. Strings, numbers, arrays,
144
+ or any object. Ported to Rails 3!
145
+ test_files: []