thecore_settings 1.1.15 → 2.0.5

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +20 -0
  3. data/.devcontainer/devcontainer.json +33 -0
  4. data/.github/workflows/gempush.yml +34 -0
  5. data/.gitignore +542 -0
  6. data/.rakeTasks +7 -0
  7. data/.rspec +1 -0
  8. data/.ruby-version +1 -0
  9. data/.travis.yml +31 -0
  10. data/CHANGELOG.md +81 -0
  11. data/Gemfile +4 -0
  12. data/Gemfile.lock +275 -0
  13. data/LICENSE.txt +22 -0
  14. data/README.md +169 -0
  15. data/README.rdoc +68 -0
  16. data/Rakefile +6 -34
  17. data/app/{assets/config/thecore_settings_manifest.js → models/.keep} +0 -0
  18. data/app/models/thecore_settings/setting.rb +77 -0
  19. data/app/views/.keep +0 -0
  20. data/app/views/rails_admin/main/_setting_value.html.haml +41 -0
  21. data/bin/rails +13 -0
  22. data/config/locales/en.yml +29 -0
  23. data/config/locales/{thecore_settings.it.yml → it.yml} +2 -2
  24. data/config/locales/pt-BR.yml +28 -0
  25. data/config/locales/ru.yml +29 -0
  26. data/db/migrate/20161227101954_create_rails_admin_settings.rb +4 -4
  27. data/gemfiles/mongoid-6.0.gemfile +5 -0
  28. data/gemfiles/mongoid-6.3.gemfile +5 -0
  29. data/lib/generators/thecore_settings/migration_generator.rb +15 -0
  30. data/lib/generators/thecore_settings/templates/db/migrate/20161227101954_create_thecore_settings.rb +29 -0
  31. data/lib/generators/thecore_settings/templates/migration.rb +29 -0
  32. data/lib/thecore_settings/determine_mime_type.rb +193 -0
  33. data/lib/thecore_settings/dumper.rb +12 -0
  34. data/lib/thecore_settings/engine.rb +17 -3
  35. data/lib/thecore_settings/fallback.rb +21 -0
  36. data/lib/thecore_settings/hex_color_validator.rb +11 -0
  37. data/lib/thecore_settings/kinds.rb +34 -0
  38. data/lib/thecore_settings/mongoid.rb +19 -0
  39. data/lib/thecore_settings/namespaced.rb +210 -0
  40. data/lib/thecore_settings/processing.rb +217 -0
  41. data/lib/thecore_settings/rails_admin_config.rb +71 -0
  42. data/lib/thecore_settings/require_helpers.rb +86 -0
  43. data/lib/thecore_settings/settings.rb +95 -0
  44. data/lib/thecore_settings/storage/carrier_wave_uploader.rb +9 -0
  45. data/lib/thecore_settings/storage/shrine_uploader.rb +14 -0
  46. data/lib/thecore_settings/tasks.rb +35 -0
  47. data/lib/thecore_settings/uploads.rb +57 -0
  48. data/lib/thecore_settings/validation.rb +126 -0
  49. data/lib/thecore_settings/version.rb +1 -1
  50. data/lib/thecore_settings.rb +115 -123
  51. data/spec/advanced_usage_spec.rb +11 -0
  52. data/spec/carrierwave_spec.rb +41 -0
  53. data/spec/database_trickery_spec.rb +48 -0
  54. data/spec/defaults_spec.rb +87 -0
  55. data/spec/enabling_spec.rb +29 -0
  56. data/spec/factories/setting.rb +8 -0
  57. data/spec/label_spec.rb +16 -0
  58. data/spec/migration_spec.rb +20 -0
  59. data/spec/model_spec.rb +105 -0
  60. data/spec/namespaced_spec.rb +67 -0
  61. data/spec/paperclip_spec.rb +38 -0
  62. data/spec/settings_spec.rb +75 -0
  63. data/spec/shrine_spec.rb +34 -0
  64. data/spec/spec_helper.rb +85 -0
  65. data/spec/support/1024x768.gif +0 -0
  66. data/spec/support/database_cleaner.rb +10 -0
  67. data/spec/support/defaults.yml +23 -0
  68. data/spec/support/defaults_w_file.yml +19 -0
  69. data/spec/support/mongoid.rb +6 -0
  70. data/spec/support/mongoid.yml +6 -0
  71. data/spec/types_spec.rb +101 -0
  72. data/thecore_settings.gemspec +44 -0
  73. metadata +326 -63
  74. data/app/assets/stylesheets/rich/editor.css +0 -20
  75. data/config/initializers/rails_admin_requirements.rb +0 -22
  76. data/config/initializers/thecore_settings_abilities.rb +0 -22
  77. data/config/initializers/thecore_settings_post_init.rb +0 -9
  78. data/config/routes.rb +0 -3
  79. data/db/migrate/20161227101956_add_app_name.rb +0 -5
  80. data/lib/tasks/thecore_settings_tasks.rake +0 -4
@@ -0,0 +1,9 @@
1
+ module ThecoreSettings
2
+ module Uploads
3
+ class CarrierWaveUploader < CarrierWave::Uploader::Base
4
+ def extension_white_list
5
+ %w(jpg jpeg gif png tiff psd ai txt rtf doc docx xls xlsx ppt pptx odt odx zip rar 7z pdf)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module ThecoreSettings
2
+ module Uploads
3
+ class ShrineUploader < Shrine
4
+ plugin :determine_mime_type
5
+ plugin :validation_helpers
6
+ plugin :mongoid if ThecoreSettings.mongoid?
7
+ Attacher.validate do
8
+ validate_mime_type_inclusion %w[image/jpeg image/gif image/png]
9
+ validate_max_size 2.megabytes
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ # require this file to load the tasks
2
+ require 'rake'
3
+
4
+ # Require sitemap_generator at runtime. If we don't do this the ActionView helpers are included
5
+ # before the Rails environment can be loaded by other Rake tasks, which causes problems
6
+ # for those tasks when rendering using ActionView.
7
+ namespace :settings do
8
+ # Require sitemap_generator only. When installed as a plugin the require will fail, so in
9
+ # that case, load the environment first.
10
+ task :require do
11
+ Rake::Task['environment'].invoke
12
+ end
13
+
14
+ desc "Dump settings to config/settings.yml; use rake settings:dump[production] to create env-specific template"
15
+ task :dump, [:as_env] => ['settings:require'] do |t, args|
16
+ if args.empty? || args[:as_env].blank?
17
+ path = Settings.root_file_path.join('config/settings.yml')
18
+ else
19
+ path = Settings.root_file_path.join("config/settings.#{args[:as_env]}.yml")
20
+ end
21
+ ThecoreSettings::Dumper.dump(path)
22
+ puts "dumped settings to #{path}"
23
+ end
24
+
25
+ desc "Load settings from config/settings.yml without overwriting current values"
26
+ task :load => ['settings:require'] do
27
+ Settings.apply_defaults!(Rails.root.join("config/settings.#{Rails.env.to_s}.yml"), true)
28
+ Settings.apply_defaults!(Rails.root.join('config/settings.yml'), true)
29
+ end
30
+
31
+ desc "Delete all settings"
32
+ task :delete => ['settings:require'] do
33
+ Settings.destroy_all!
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ module ThecoreSettings
2
+ module Uploads
3
+ autoload :CarrierWaveUploader, "thecore_settings/storage/carrier_wave_uploader"
4
+ autoload :ShrineUploader, "thecore_settings/storage/shrine_uploader"
5
+
6
+ def self.paperclip_options
7
+ if defined?(Rails)
8
+ {}
9
+ else
10
+ {path: "#{File.dirname(__FILE__)}/../../uploads/:filename", url: '/uploads/:filename'}
11
+ end
12
+ end
13
+
14
+ def self.included(base)
15
+ # carrierwave
16
+ if base.respond_to?(:mount_uploader)
17
+ # puts "[thecore_settings] CarrierWave detected"
18
+ # base.field(:file, type: String)
19
+ base.mount_uploader(:file, ThecoreSettings::Uploads::CarrierWaveUploader)
20
+ Settings.file_uploads_supported = true
21
+ Settings.file_uploads_engine = :carrierwave
22
+ # mongoid-paperclip
23
+ elsif ThecoreSettings.mongoid? && ::Mongoid.const_defined?('Paperclip')
24
+ base.send(:include, ::Mongoid::Paperclip)
25
+ # puts "[thecore_settings] PaperClip detected"
26
+ base.field(:file, type: String)
27
+ base.has_mongoid_attached_file(:file, self.paperclip_options)
28
+ if base.respond_to?(:do_not_validate_attachment_file_type)
29
+ base.do_not_validate_attachment_file_type :file
30
+ end
31
+
32
+ Settings.file_uploads_supported = true
33
+ Settings.file_uploads_engine = :paperclip
34
+ elsif ThecoreSettings.active_record? && defined?(Paperclip)
35
+ base.has_attached_file(:file, self.paperclip_options)
36
+ if base.respond_to?(:do_not_validate_attachment_file_type)
37
+ base.do_not_validate_attachment_file_type :file
38
+ end
39
+ Settings.file_uploads_supported = true
40
+ Settings.file_uploads_engine = :paperclip
41
+ elsif ThecoreSettings.active_record? && defined?(Shrine)
42
+ Settings.file_uploads_supported = true
43
+ Settings.file_uploads_engine = :shrine
44
+ base.send(:include, ShrineUploader::Attachment(:file))
45
+ elsif ThecoreSettings.mongoid? && ::Mongoid.const_defined?('Shrine')
46
+ base.send(:include, ::Mongoid::Document)
47
+ base.send(:include, ShrineUploader::Attachment(:file))
48
+ base.field(:file_data, type: String)
49
+ Settings.file_uploads_supported = true
50
+ Settings.file_uploads_engine = :shrine
51
+ else
52
+ # puts "[thecore_settings] Uploads disabled"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,126 @@
1
+ module ThecoreSettings
2
+ module Validation
3
+ class << self
4
+ def included(base)
5
+ base.before_validation do
6
+ self.raw = default_serializable_value if raw.blank?
7
+ end
8
+ base.before_validation :preprocess_value, if: :preprocessed_kind?
9
+ base.validates_uniqueness_of :key, scope: :ns
10
+ base.validates_inclusion_of :kind, in: ThecoreSettings.kinds
11
+ base.validates_numericality_of :raw, if: :integer_kind?
12
+ base.validates_numericality_of :raw, if: :float_kind?
13
+
14
+ add_validators(base)
15
+ end
16
+
17
+ def add_validators(base)
18
+ add_color_validator(base)
19
+ add_file_validator(base)
20
+ add_email_validator(base)
21
+ add_url_validator(base)
22
+ add_phone_validator(base)
23
+ add_geo_validator(base)
24
+ add_yaml_validator(base)
25
+ add_json_validator(base)
26
+ end
27
+
28
+ def add_color_validator(base)
29
+ base.validates_with(ThecoreSettings::HexColorValidator, attributes: :raw, if: :color_kind?)
30
+ end
31
+
32
+ def add_file_validator(base)
33
+ base.validate if: :file_kind? do
34
+ unless Settings.file_uploads_supported
35
+ raise '[thecore_settings] File kind requires either CarrierWave or Paperclip or Shrine. Check that thecore_settings is below them in Gemfile'
36
+ end
37
+ end
38
+ end
39
+
40
+ def add_email_validator(base)
41
+ base.validate if: :email_kind? do
42
+ require_validates_email_format_of do
43
+ errors.add(:raw, I18n.t('admin.settings.email_invalid')) unless raw.blank? || ValidatesEmailFormatOf.validate_email_format(raw).nil?
44
+ end
45
+ end
46
+ end
47
+
48
+ def add_url_validator(base)
49
+ base.before_validation if: :url_kind? do
50
+ require_addressable do
51
+ self.raw = Addressable::URI.heuristic_parse(self.raw) unless self.raw.blank?
52
+ end
53
+ end
54
+
55
+ base.before_validation if: :domain_kind? do
56
+ require_addressable do
57
+ self.raw = Addressable::URI.heuristic_parse(self.raw).host unless self.raw.blank?
58
+ end
59
+ end
60
+ end
61
+
62
+ def add_phone_validator(base)
63
+ base.validate if: :phone_kind? do
64
+ require_russian_phone do
65
+ errors.add(:raw, I18n.t('admin.settings.phone_invalid')) unless raw.blank? || RussianPhone::Number.new(raw).valid?
66
+ end
67
+ end
68
+
69
+ base.validate if: :phones_kind? do
70
+ require_russian_phone do
71
+ unless raw.blank?
72
+ invalid_phones = raw.gsub("\r", '').split("\n").inject([]) do |memo, value|
73
+ memo << value unless RussianPhone::Number.new(value).valid?
74
+ memo
75
+ end
76
+ errors.add(:raw, I18n.t('admin.settings.phones_invalid', phones: invalid_phones * ', ')) unless invalid_phones.empty?
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ def add_geo_validator(base)
84
+ base.validate if: :address_kind? do
85
+ require_geocoder do
86
+ # just raise error if we are trying to use address kind without geocoder
87
+ end
88
+ end
89
+ if Object.const_defined?('Geocoder')
90
+ if ThecoreSettings.mongoid?
91
+ base.field(:coordinates, type: Array)
92
+ base.send(:include, Geocoder::Model::Mongoid)
93
+ end
94
+ base.geocoded_by(:raw)
95
+ base.after_validation(:geocode, if: :address_kind?)
96
+ end
97
+ end
98
+
99
+ def add_yaml_validator(base)
100
+ base.validate if: :yaml_kind? do
101
+ require_safe_yaml do
102
+ unless raw.blank?
103
+ begin
104
+ YAML.safe_load(raw)
105
+ rescue Psych::SyntaxError => e
106
+ errors.add(:raw, I18n.t('admin.settings.yaml_invalid'))
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def add_json_validator(base)
114
+ base.validate if: :json_kind? do
115
+ unless raw.blank?
116
+ begin
117
+ JSON.load(raw)
118
+ rescue JSON::ParserError => e
119
+ errors.add(:raw, I18n.t('admin.settings.json_invalid'))
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -1,3 +1,3 @@
1
1
  module ThecoreSettings
2
- VERSION = '1.1.15'.freeze
2
+ VERSION = "#{`git describe --tags $(git rev-list --tags --max-count=1)`.chomp}"
3
3
  end
@@ -1,127 +1,119 @@
1
- require "thecore_settings/engine"
2
- #require "rich"
3
- require "russian_phone"
4
- require "sanitize"
5
- require "safe_yaml"
6
- require "validates_email_format_of"
7
- require "geocoder"
8
- require "paperclip"
9
- require "addressable"
10
- require "rails_admin_settings"
1
+ require "thecore_settings/version"
11
2
 
12
3
  module ThecoreSettings
13
- # Your code goes here...
14
- SafeYAML::OPTIONS[:default_mode] = :safe
15
- SafeYAML::OPTIONS[:deserialize_symbols] = false
16
- #
17
- # if Object.const_defined?("Rich")
18
- # Rich.setup do |config|
19
- #
20
- # # == CKEditor configuration
21
- # #
22
- # # Rich ships with what I hope are sensible defaults.
23
- # # You may want to override these.
24
- # #
25
- # # For example, the elements available in the formats
26
- # # dropdown are defined like this:
27
- # # config.editor[:format_tags] = "h3;p;pre"
28
- # #
29
- # # By default, Rich visualizes what type of element
30
- # # you are editing. To disable this:
31
- # # config.editor[:startupOutlineBlocks] = false
32
- #
33
- #
34
- # # == Image styles
35
- # #
36
- # # Rich uses paperclip for image processing. You can
37
- # # define the styles you would like to use here. You
38
- # # can use the standard syntax allowed by paperclip.
39
- # # See: https://github.com/thoughtbot/paperclip/wiki/Thumbnail-Generation
40
- # #
41
- # # When you change these after uploading some files,
42
- # # remember to re-generate your styles by running:
43
- # # rake rich:refresh_assets
44
- # config.image_styles = {
45
- # :thumb => "100x100#"
46
- # }
47
- #
48
- # # == Convert options
49
- # #
50
- # # You can pass additional commands to ImageMagick to set image quality,
51
- # # apply a blur, and other fancy tricks.
52
- # #
53
- # # Example (this will make your image look terrible):
54
- # # config.convert_options = {
55
- # # :large => '-quality 1'
56
- # # }
57
- #
58
- # # == Allowed styles (in file manager)
59
- # #
60
- # # Of the styles specified above, which should be user
61
- # # selectable in the file manager?
62
- # #
63
- # # Example:
64
- # # config.allowed_styles = [ :large, :thumb ]
65
- # #
66
- # # Default:
67
- # # config.allowed_styles = :all
68
- #
69
- # # == Default Style
70
- # #
71
- # # The style to insert by default. In addition to the
72
- # # styles defined above you can also use :original to get
73
- # # the unprocessed file. Make sure this style exists.
74
- # config.default_style = :thumb
75
- #
76
- # # == Upload non-image files
77
- # #
78
- # # Setting this option to true will add a second Rich filebrowser icon to
79
- # # the editor toolbar. In this filebrowser you can upload non-image files.
80
- # # Inserting these files into your editor will result in a direct (A) link.
81
- # #
82
- # # Default:
83
- # # config.allow_document_uploads = false
84
- #
85
- # # == Set allowed filetypes for non-image files
86
- # #
87
- # # If you want, you can restrict the types of documents that users can upload.
88
- # # Default behavior is to allow any kind of file to be uploaded. You can set
89
- # # the accepted types by providing an array of mimetypes to check against.
90
- # # Note that for this to have any effect, you first need to enable document
91
- # # uploads using the setting above.
92
- # #
93
- # # Default, allow any file to be uploaded:
94
- # # config.allowed_document_types = :all
95
- # #
96
- # # Example, only allow PDF uploads:
97
- # # config.allowed_document_types = ['application/pdf']
98
- #
99
- # # == Asset insertion
100
- # #
101
- # # Set this to true to keep the filebrowser open after inserting an asset.
102
- # # Also configurable per-use from within the filebrowser.
103
- # #
104
- # # Default:
105
- # # config.insert_many = false
106
- #
107
- # # == User Authentication
108
- # #
109
- # # When defined, Rich will automatically call this method
110
- # # in a before filter to ensure that the user is logged in.
111
- # #
112
- # # If you do not change this value from the default, anyone
113
- # # will be able to see your images, and upload files.
114
- # #
115
- # # Example for Devise with an AdminUser model:
116
- # # config.authentication_method = :authenticate_admin_user!
117
- # #
118
- # # Default (NOT recommended in production environments):
119
- # # config.authentication_method = :none
120
- # config.authentication_method = :authenticate_user!
121
- #
122
- # end
123
- #
124
- # Rich.insert
125
- # end
4
+ if defined?(Rails) && defined?(Rails::Html) && defined?(Rails::Html::WhiteListSanitizer)
5
+ @@scrubber = Rails::Html::WhiteListSanitizer.new
6
+ end
7
+ cattr_accessor :scrubber
126
8
 
9
+ class PersistenceException < Exception
10
+ end
11
+
12
+ autoload :Mongoid, "thecore_settings/mongoid"
13
+ autoload :Fallback, "thecore_settings/fallback"
14
+ autoload :Namespaced, "thecore_settings/namespaced"
15
+ autoload :Processing, "thecore_settings/processing"
16
+ autoload :Validation, "thecore_settings/validation"
17
+ autoload :RequireHelpers, "thecore_settings/require_helpers"
18
+ autoload :RailsAdminConfig, "thecore_settings/rails_admin_config"
19
+ autoload :Uploads, "thecore_settings/uploads"
20
+ autoload :HexColorValidator, "thecore_settings/hex_color_validator"
21
+ autoload :Dumper, "thecore_settings/dumper"
22
+
23
+ class << self
24
+ def orm
25
+ if defined?(::Mongoid)
26
+ :mongoid
27
+ else
28
+ :active_record
29
+ end
30
+ end
31
+
32
+ def mongoid?
33
+ orm == :mongoid
34
+ end
35
+
36
+ def active_record?
37
+ orm == :active_record
38
+ end
39
+
40
+ def apply_defaults!(file, verbose = false)
41
+ if File.file?(file)
42
+ puts "[settings] Loading from #{file}" if verbose
43
+ if defined?(Psych) && Psych.respond_to?(:safe_load)
44
+ yaml = Psych.safe_load(File.read(file))
45
+ else
46
+ yaml = YAML.load(File.read(file), safe: true)
47
+ end
48
+ yaml.each_pair do |namespace, vals|
49
+ process_defaults(namespace, vals, verbose)
50
+ end
51
+ end
52
+ end
53
+
54
+ def process_defaults(namespace, vals, verbose = false)
55
+ vals.symbolize_keys!
56
+ n = Settings.ns(namespace)
57
+ vals.each_pair do |key, val|
58
+ val.symbolize_keys!
59
+ if !val[:kind].nil? && (val[:kind] == 'file' || val[:kind] == 'image')
60
+ unless Settings.file_uploads_supported
61
+ raise PersistenceException, "Fatal: setting #{key} is #{val[:type]} but file upload engine is not detected"
62
+ end
63
+ value = File.open(Settings.root_file_path.join(val.delete(:value)))
64
+ else
65
+ value = val.delete(:value)
66
+ end
67
+ puts "#{key} - default '#{value}' current '#{Settings.get(key).raw}'" if verbose
68
+ n.set(key, value, val.merge(overwrite: false))
69
+ end
70
+ n.unload!
71
+ end
72
+
73
+ def migrate!
74
+ if ThecoreSettings.mongoid?
75
+ ThecoreSettings::Setting.where(:ns.exists => false).update_all(ns: 'main')
76
+ ThecoreSettings::Setting.all.each do |s|
77
+ s.kind = s.read_attribute(:type) if !s.read_attribute(:type).blank? && s.kind != s.read_attribute(:type)
78
+ s.save! if s.changed?
79
+ s.unset(:type)
80
+ end
81
+ else
82
+ if Settings.table_exists?
83
+ ThecoreSettings::Setting.where("ns IS NULL").update_all(ns: 'main')
84
+ end
85
+ end
86
+ end
87
+
88
+ def track_history!
89
+ return false unless Settings.table_exists?
90
+
91
+ if mongoid?
92
+ if ::Mongoid.const_defined?('History')
93
+ ThecoreSettings::Setting.send(:include, ::Mongoid::History::Trackable)
94
+ ThecoreSettings::Setting.send(:track_history, {track_create: true, track_destroy: true})
95
+ else
96
+ puts "[thecore_settings] WARN unable to track_history: Mongoid::History not loaded!"
97
+ end
98
+ if ::Mongoid.const_defined?('Userstamp')
99
+ ThecoreSettings::Setting.send(:include, ::Mongoid::Userstamp)
100
+ else
101
+ puts "[thecore_settings] WARN unable to track_history: Mongoid::Userstamp not loaded!"
102
+ end
103
+ elsif active_record?
104
+ if defined?(PaperTrail) && PaperTrail::Version.table_exists?
105
+ ThecoreSettings::Setting.send(:has_paper_trail)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ require "thecore_settings/kinds"
113
+ require "thecore_settings/settings"
114
+
115
+ if Object.const_defined?('Rails')
116
+ require "thecore_settings/engine"
117
+ else
118
+ require File.dirname(__FILE__) + '/../app/models/thecore_settings/setting.rb'
127
119
  end