spreeference 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.
- checksums.yaml +7 -0
- data/LICENSE.md +26 -0
- data/README.md +233 -0
- data/Rakefile +26 -0
- data/app/models/spreeference/app_configuration.rb +9 -0
- data/app/models/spreeference/application_record.rb +16 -0
- data/app/models/spreeference/preference.rb +6 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20170217094656_create_spreeference_preferences.rb +10 -0
- data/lib/generators/spreeference/install_generator.rb +17 -0
- data/lib/generators/templates/app_configuration.rb +6 -0
- data/lib/generators/templates/spreeference.rb +12 -0
- data/lib/spreeference.rb +16 -0
- data/lib/spreeference/configuration.rb +48 -0
- data/lib/spreeference/engine.rb +28 -0
- data/lib/spreeference/environment.rb +12 -0
- data/lib/spreeference/environment_extension.rb +23 -0
- data/lib/spreeference/preferable.rb +103 -0
- data/lib/spreeference/preferable_methods.rb +52 -0
- data/lib/spreeference/scoped_store.rb +34 -0
- data/lib/spreeference/store.rb +93 -0
- data/lib/spreeference/version.rb +3 -0
- data/lib/tasks/spreeference_tasks.rake +4 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/models/app_configuration.rb +6 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +13 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +34 -0
- data/spec/dummy/bin/update +29 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +42 -0
- data/spec/dummy/config/environments/production.rb +73 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/spreeference.rb +13 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/puma.rb +47 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +69 -0
- data/spec/dummy/log/test.log +1995 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/spreeference/configuration_spec.rb +27 -0
- data/spec/lib/spreeference/preferable_spec.rb +344 -0
- data/spec/lib/spreeference/scoped_store_spec.rb +58 -0
- data/spec/lib/spreeference/store_spec.rb +46 -0
- data/spec/models/spreeference/preference_spec.rb +80 -0
- data/spec/rails_helper.rb +31 -0
- data/spec/spec_helper.rb +19 -0
- metadata +227 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Spreeference
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
|
4
|
+
isolate_namespace Spreeference
|
5
|
+
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec, fixture: false
|
8
|
+
g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
9
|
+
g.assets false
|
10
|
+
g.helper false
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer 'spreeference.environment', before: :load_config_initializers do |app|
|
14
|
+
app.config.spreeference = Spreeference::Environment.new
|
15
|
+
Spreeference::Config = app.config.spreeference.preferences
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds migrations to host application
|
19
|
+
initializer :append_migrations do |app|
|
20
|
+
unless app.root.to_s.match root.to_s
|
21
|
+
config.paths["db/migrate"].expanded.each do |path|
|
22
|
+
app.config.paths["db/migrate"] << path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Spreeference
|
2
|
+
module EnvironmentExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def add_class(name)
|
6
|
+
self.instance_variable_set "@#{name}", Set.new
|
7
|
+
create_method( "#{name}=".to_sym ) { |val|
|
8
|
+
instance_variable_set( "@" + name, val)
|
9
|
+
}
|
10
|
+
|
11
|
+
create_method(name.to_sym) do
|
12
|
+
instance_variable_get( "@" + name )
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def create_method(name, &block)
|
19
|
+
self.class.send(:define_method, name, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Spreeference
|
2
|
+
module Preferable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
extend Spreeference::PreferableMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_preference(name)
|
10
|
+
has_preference! name
|
11
|
+
send self.class.preference_getter_method(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_preference(name, value)
|
15
|
+
has_preference! name
|
16
|
+
send self.class.preference_setter_method(name), value
|
17
|
+
end
|
18
|
+
|
19
|
+
def preference_type(name)
|
20
|
+
has_preference! name
|
21
|
+
send self.class.preference_type_getter_method(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def preference_default(name)
|
25
|
+
has_preference! name
|
26
|
+
send self.class.preference_default_getter_method(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_preference!(name)
|
30
|
+
raise NoMethodError.new "#{name} preference not defined" unless has_preference? name
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_preference?(name)
|
34
|
+
respond_to? self.class.preference_getter_method(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def defined_preferences
|
38
|
+
methods.grep(/\Apreferred_.*=\Z/).map do |pref_method|
|
39
|
+
pref_method.to_s.gsub(/\Apreferred_|=\Z/, '').to_sym
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_preferences
|
44
|
+
Hash[
|
45
|
+
defined_preferences.map do |preference|
|
46
|
+
[preference, preference_default(preference)]
|
47
|
+
end
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear_preferences
|
52
|
+
preferences.keys.each {|pref| preferences.delete pref}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def convert_preference_value(value, type)
|
58
|
+
case type
|
59
|
+
when :string, :text
|
60
|
+
value.to_s
|
61
|
+
when :password
|
62
|
+
value.to_s
|
63
|
+
when :decimal
|
64
|
+
BigDecimal.new(value.to_s)
|
65
|
+
when :integer
|
66
|
+
value.to_i
|
67
|
+
when :boolean
|
68
|
+
if value.is_a?(FalseClass) ||
|
69
|
+
value.nil? ||
|
70
|
+
value == 0 ||
|
71
|
+
value =~ /^(f|false|0)$/i ||
|
72
|
+
(value.respond_to? :empty? and value.empty?)
|
73
|
+
false
|
74
|
+
else
|
75
|
+
true
|
76
|
+
end
|
77
|
+
when :array
|
78
|
+
value.is_a?(Array) ? value : Array.wrap(value)
|
79
|
+
when :hash
|
80
|
+
case value.class.to_s
|
81
|
+
when "Hash"
|
82
|
+
value
|
83
|
+
when "String"
|
84
|
+
# only works with hashes whose keys are strings
|
85
|
+
JSON.parse value.gsub('=>', ':')
|
86
|
+
when "Array"
|
87
|
+
begin
|
88
|
+
value.try(:to_h)
|
89
|
+
rescue TypeError
|
90
|
+
Hash[*value]
|
91
|
+
rescue ArgumentError
|
92
|
+
raise 'An even count is required when passing an array to be converted to a hash'
|
93
|
+
end
|
94
|
+
else
|
95
|
+
value.class.ancestors.include?(Hash) ? value : {}
|
96
|
+
end
|
97
|
+
else
|
98
|
+
value
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Spreeference
|
2
|
+
module PreferableMethods
|
3
|
+
|
4
|
+
def preference(name, type, *args)
|
5
|
+
options = args.extract_options!
|
6
|
+
options.assert_valid_keys(:default)
|
7
|
+
default = options[:default]
|
8
|
+
default = ->{ options[:default] } unless default.is_a?(Proc)
|
9
|
+
|
10
|
+
# cache_key will be nil for new objects, then if we check if there
|
11
|
+
# is a pending preference before going to default
|
12
|
+
define_method preference_getter_method(name) do
|
13
|
+
preferences.fetch(name) do
|
14
|
+
default.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method preference_setter_method(name) do |value|
|
19
|
+
value = convert_preference_value(value, type)
|
20
|
+
preferences[name] = value
|
21
|
+
|
22
|
+
# If this is an activerecord object, we need to inform
|
23
|
+
# ActiveRecord::Dirty that this value has changed, since this is an
|
24
|
+
# in-place update to the preferences hash.
|
25
|
+
preferences_will_change! if respond_to?(:preferences_will_change!)
|
26
|
+
end
|
27
|
+
|
28
|
+
define_method preference_default_getter_method(name), &default
|
29
|
+
|
30
|
+
define_method preference_type_getter_method(name) do
|
31
|
+
type
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def preference_getter_method(name)
|
36
|
+
"preferred_#{name}".to_sym
|
37
|
+
end
|
38
|
+
|
39
|
+
def preference_setter_method(name)
|
40
|
+
"preferred_#{name}=".to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
def preference_default_getter_method(name)
|
44
|
+
"preferred_#{name}_default".to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
def preference_type_getter_method(name)
|
48
|
+
"preferred_#{name}_type".to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Spreeference
|
2
|
+
class ScopedStore
|
3
|
+
def initialize prefix, suffix=nil
|
4
|
+
@prefix = prefix
|
5
|
+
@suffix = suffix
|
6
|
+
end
|
7
|
+
|
8
|
+
def store
|
9
|
+
Spreeference::Store.instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch key, &block
|
13
|
+
store.fetch(key_for(key), &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def []= key, value
|
17
|
+
store[key_for(key)] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete key
|
21
|
+
store.delete(key_for(key))
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def key_for key
|
26
|
+
[rails_cache_id, @prefix, key, @suffix].compact.join('/')
|
27
|
+
end
|
28
|
+
|
29
|
+
def rails_cache_id
|
30
|
+
ENV['RAILS_CACHE_ID']
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Spreeference
|
4
|
+
class StoreInstance
|
5
|
+
attr_accessor :persistence
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@cache = Rails.cache
|
9
|
+
@persistence = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def set(key, value)
|
13
|
+
@cache.write(key, value)
|
14
|
+
persist(key, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :[]=, :set
|
18
|
+
|
19
|
+
def exist?(key)
|
20
|
+
@cache.exist?(key) ||
|
21
|
+
should_persist? && Spreeference::Preference.where(key: key).exists?
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(key)
|
25
|
+
# return the retrieved value, if it's in the cache
|
26
|
+
# use unless nil? incase the value is actually boolean false
|
27
|
+
#
|
28
|
+
unless (val = @cache.read(key)).nil?
|
29
|
+
return val
|
30
|
+
end
|
31
|
+
|
32
|
+
if should_persist?
|
33
|
+
# If it's not in the cache, maybe it's in the database, but
|
34
|
+
# has been cleared from the cache
|
35
|
+
|
36
|
+
# does it exist in the database?
|
37
|
+
if preference = Spreeference::Preference.find_by_key(key)
|
38
|
+
# it does exist
|
39
|
+
val = preference.value
|
40
|
+
else
|
41
|
+
# use the fallback value
|
42
|
+
val = yield
|
43
|
+
end
|
44
|
+
|
45
|
+
# Cache either the value from the db or the fallback value.
|
46
|
+
# This avoids hitting the db with subsequent queries.
|
47
|
+
@cache.write(key, val)
|
48
|
+
|
49
|
+
return val
|
50
|
+
else
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :fetch, :get
|
56
|
+
|
57
|
+
def delete(key)
|
58
|
+
@cache.delete(key)
|
59
|
+
destroy(key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear_cache
|
63
|
+
@cache.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def persist(cache_key, value)
|
69
|
+
return unless should_persist?
|
70
|
+
|
71
|
+
preference = Spreeference::Preference.where(key: cache_key).first_or_initialize
|
72
|
+
preference.value = value
|
73
|
+
preference.save
|
74
|
+
end
|
75
|
+
|
76
|
+
def destroy(cache_key)
|
77
|
+
return unless should_persist?
|
78
|
+
|
79
|
+
preference = Spreeference::Preference.find_by_key(cache_key)
|
80
|
+
preference.destroy if preference
|
81
|
+
end
|
82
|
+
|
83
|
+
def should_persist?
|
84
|
+
@persistence and Spreeference::Preference.table_exists?
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class Store < StoreInstance
|
90
|
+
include Singleton
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|