spreeference 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +26 -0
  3. data/README.md +233 -0
  4. data/Rakefile +26 -0
  5. data/app/models/spreeference/app_configuration.rb +9 -0
  6. data/app/models/spreeference/application_record.rb +16 -0
  7. data/app/models/spreeference/preference.rb +6 -0
  8. data/config/routes.rb +2 -0
  9. data/db/migrate/20170217094656_create_spreeference_preferences.rb +10 -0
  10. data/lib/generators/spreeference/install_generator.rb +17 -0
  11. data/lib/generators/templates/app_configuration.rb +6 -0
  12. data/lib/generators/templates/spreeference.rb +12 -0
  13. data/lib/spreeference.rb +16 -0
  14. data/lib/spreeference/configuration.rb +48 -0
  15. data/lib/spreeference/engine.rb +28 -0
  16. data/lib/spreeference/environment.rb +12 -0
  17. data/lib/spreeference/environment_extension.rb +23 -0
  18. data/lib/spreeference/preferable.rb +103 -0
  19. data/lib/spreeference/preferable_methods.rb +52 -0
  20. data/lib/spreeference/scoped_store.rb +34 -0
  21. data/lib/spreeference/store.rb +93 -0
  22. data/lib/spreeference/version.rb +3 -0
  23. data/lib/tasks/spreeference_tasks.rake +4 -0
  24. data/spec/dummy/Rakefile +6 -0
  25. data/spec/dummy/app/assets/config/manifest.js +3 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  28. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  29. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  31. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  32. data/spec/dummy/app/jobs/application_job.rb +2 -0
  33. data/spec/dummy/app/models/app_configuration.rb +6 -0
  34. data/spec/dummy/app/models/application_record.rb +3 -0
  35. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  36. data/spec/dummy/bin/bundle +3 -0
  37. data/spec/dummy/bin/rails +4 -0
  38. data/spec/dummy/bin/rake +4 -0
  39. data/spec/dummy/bin/setup +34 -0
  40. data/spec/dummy/bin/update +29 -0
  41. data/spec/dummy/config.ru +5 -0
  42. data/spec/dummy/config/application.rb +23 -0
  43. data/spec/dummy/config/boot.rb +5 -0
  44. data/spec/dummy/config/cable.yml +9 -0
  45. data/spec/dummy/config/database.yml +25 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +42 -0
  48. data/spec/dummy/config/environments/production.rb +73 -0
  49. data/spec/dummy/config/environments/test.rb +36 -0
  50. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  57. data/spec/dummy/config/initializers/session_store.rb +3 -0
  58. data/spec/dummy/config/initializers/spreeference.rb +13 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +23 -0
  61. data/spec/dummy/config/puma.rb +47 -0
  62. data/spec/dummy/config/routes.rb +3 -0
  63. data/spec/dummy/config/secrets.yml +22 -0
  64. data/spec/dummy/config/spring.rb +6 -0
  65. data/spec/dummy/db/development.sqlite3 +0 -0
  66. data/spec/dummy/db/schema.rb +23 -0
  67. data/spec/dummy/db/test.sqlite3 +0 -0
  68. data/spec/dummy/log/development.log +69 -0
  69. data/spec/dummy/log/test.log +1995 -0
  70. data/spec/dummy/public/404.html +67 -0
  71. data/spec/dummy/public/422.html +67 -0
  72. data/spec/dummy/public/500.html +66 -0
  73. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/spec/dummy/public/apple-touch-icon.png +0 -0
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/lib/spreeference/configuration_spec.rb +27 -0
  77. data/spec/lib/spreeference/preferable_spec.rb +344 -0
  78. data/spec/lib/spreeference/scoped_store_spec.rb +58 -0
  79. data/spec/lib/spreeference/store_spec.rb +46 -0
  80. data/spec/models/spreeference/preference_spec.rb +80 -0
  81. data/spec/rails_helper.rb +31 -0
  82. data/spec/spec_helper.rb +19 -0
  83. 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,12 @@
1
+ module Spreeference
2
+ class Environment
3
+ include EnvironmentExtension
4
+
5
+ attr_accessor :preferences
6
+
7
+ def initialize
8
+ @preferences = Spreeference::AppConfiguration.new
9
+ end
10
+
11
+ end
12
+ 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
@@ -0,0 +1,3 @@
1
+ module Spreeference
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :spreeference do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../stylesheets .css
@@ -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
+ */
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,6 @@
1
+ class AppConfiguration < Spreeference::Configuration
2
+ # preference :checkbox, :boolean, default: true
3
+ # preference :input, :string, default:'changeme'
4
+ # preference :number, :integer, default: 123
5
+ # preference :list, :array, default:[]
6
+ end