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.
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