ack_rails_admin_settings 1.1.4

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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +17 -0
  7. data/CHANGELOG.md +53 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +192 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +133 -0
  12. data/Rakefile +9 -0
  13. data/app/models/rails_admin_settings/setting.rb +78 -0
  14. data/app/views/rails_admin/main/_setting_value.html.haml +104 -0
  15. data/config/locales/en.yml +28 -0
  16. data/config/locales/ru.yml +28 -0
  17. data/gemfiles/mongoid-3.1.gemfile +5 -0
  18. data/gemfiles/mongoid-4.0.gemfile +5 -0
  19. data/lib/ack_rails_admin_settings.rb +1 -0
  20. data/lib/generators/rails_admin_settings/migration_generator.rb +15 -0
  21. data/lib/generators/rails_admin_settings/templates/migration.rb +26 -0
  22. data/lib/rails_admin_settings/dumper.rb +12 -0
  23. data/lib/rails_admin_settings/engine.rb +17 -0
  24. data/lib/rails_admin_settings/fallback.rb +21 -0
  25. data/lib/rails_admin_settings/hex_color_validator.rb +11 -0
  26. data/lib/rails_admin_settings/kinds.rb +30 -0
  27. data/lib/rails_admin_settings/mongoid.rb +31 -0
  28. data/lib/rails_admin_settings/namespaced.rb +218 -0
  29. data/lib/rails_admin_settings/processing.rb +159 -0
  30. data/lib/rails_admin_settings/rails_admin_config.rb +74 -0
  31. data/lib/rails_admin_settings/require_helpers.rb +65 -0
  32. data/lib/rails_admin_settings/settings.rb +113 -0
  33. data/lib/rails_admin_settings/storage/carrierwave.rb +9 -0
  34. data/lib/rails_admin_settings/tasks.rb +35 -0
  35. data/lib/rails_admin_settings/uploads.rb +45 -0
  36. data/lib/rails_admin_settings/validation.rb +84 -0
  37. data/lib/rails_admin_settings/version.rb +3 -0
  38. data/lib/rails_admin_settings.rb +81 -0
  39. data/rails_admin_settings.gemspec +40 -0
  40. data/release.sh +6 -0
  41. data/spec/advanced_usage_spec.rb +11 -0
  42. data/spec/carrierwave_spec.rb +41 -0
  43. data/spec/database_trickery_spec.rb +48 -0
  44. data/spec/defaults_spec.rb +87 -0
  45. data/spec/enabling_spec.rb +29 -0
  46. data/spec/factories/setting.rb +8 -0
  47. data/spec/label_spec.rb +18 -0
  48. data/spec/migration_spec.rb +15 -0
  49. data/spec/model_spec.rb +45 -0
  50. data/spec/namespaced_spec.rb +67 -0
  51. data/spec/paperclip_spec.rb +39 -0
  52. data/spec/settings_spec.rb +75 -0
  53. data/spec/spec_helper.rb +42 -0
  54. data/spec/support/1024x768.gif +0 -0
  55. data/spec/support/database_cleaner.rb +10 -0
  56. data/spec/support/defaults.yml +23 -0
  57. data/spec/support/defaults_w_file.yml +19 -0
  58. data/spec/support/mongoid.rb +6 -0
  59. data/spec/support/mongoid.yml +6 -0
  60. data/spec/types_spec.rb +88 -0
  61. metadata +383 -0
@@ -0,0 +1,28 @@
1
+ ru:
2
+ admin:
3
+ settings:
4
+ label: 'Настройки'
5
+ no_ckeditor_detected: CKEditor не обнаружен — отображаю настройку как текстовое поле
6
+ phone_invalid: Неверный телефонный номер
7
+ phones_invalid: "Неверный телефонный номер: %{phones}"
8
+ email_invalid: Неверный email
9
+ yaml_invalid: Неверный синтаксис YAML
10
+ color_invalid: Неверный цвет
11
+ mongoid: &mongoid
12
+ models:
13
+ rails_admin_settings/setting: Настройки
14
+ attributes:
15
+ rails_admin_settings/setting:
16
+ c_at: Создано
17
+ u_at: Обновлено
18
+ enabled: Включено
19
+ ns: Группа
20
+ name: Название
21
+ key: Ключ
22
+ raw: Значение
23
+ type: Тип
24
+ kind: Тип
25
+ label: Подпись
26
+ activerecord:
27
+ <<: *mongoid
28
+
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", github: "mongoid/mongoid", branch: "3.1.0-stable"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", "~> 4.0.0.beta1"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1 @@
1
+ require "rails_admin_settings"
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/active_record'
3
+
4
+ module RailsAdminSettings
5
+ class MigrationGenerator < Rails::Generators::Base
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.expand_path('../templates', __FILE__)
8
+
9
+ desc 'RocketCMS migration generator'
10
+ def install
11
+ migration_template "migration.rb", "db/migrate/create_rails_admin_settings.rb"
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,26 @@
1
+ class CreateRailsAdminSettings < ActiveRecord::Migration
2
+ def change
3
+ create_table :rails_admin_settings do |t|
4
+ t.boolean :enabled, default: true
5
+ t.string :kind, null: false, default: 'string'
6
+ t.string :ns, default: 'main'
7
+ t.string :key, null: false
8
+ if Object.const_defined?('Geocoder')
9
+ t.float :latitude
10
+ t.float :longitude
11
+ end
12
+ t.text :raw
13
+ t.string :label
14
+ if defined?(Paperclip)
15
+ t.attachment :file
16
+ elsif defined?(CarrierWave)
17
+ t.string :file
18
+ end
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :rails_admin_settings, :key
23
+ add_index :rails_admin_settings, [:ns, :key], unique: true
24
+ end
25
+ end
26
+
@@ -0,0 +1,12 @@
1
+ module RailsAdminSettings
2
+ module Dumper
3
+ def self.dump(path)
4
+ ns = {}
5
+ RailsAdminSettings::Setting.each do |s|
6
+ ns[s.ns] = {} if ns[s.ns].nil?
7
+ ns[s.ns][s.key] = s.as_yaml
8
+ end
9
+ File.write(path, ns.to_yaml)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ module RailsAdminSettings
2
+ class Engine < ::Rails::Engine
3
+ rake_tasks do
4
+ require File.expand_path('../tasks', __FILE__)
5
+ end
6
+
7
+ initializer 'RailsAdminSettings Install after_filter' do |app|
8
+ require File.dirname(__FILE__) + '/../../app/models/rails_admin_settings/setting.rb'
9
+
10
+ if defined?(ActionController) and defined?(ActionController::Base)
11
+ ActionController::Base.class_eval do
12
+ after_filter { Settings.unload! }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module RailsAdminSettings
2
+ # we are inheriting from BasicObject so we don't get a bunch of methods from
3
+ # Kernel or Object
4
+ class Fallback < BasicObject
5
+ def initialize(ns, fb)
6
+ @ns = ns
7
+ @fb = fb
8
+ end
9
+
10
+ def inspect
11
+ "#<RailsAdminSettings::Fallback ns: #{@ns.inspect}, fb: #{@fb.inspect}>".freeze
12
+ end
13
+
14
+ def method_missing(*args)
15
+ @ns.ns_mutex.synchronize do
16
+ @ns.fallback = @fb
17
+ @ns.__send__(*args)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module RailsAdminSettings
2
+ class HexColorValidator < ActiveModel::EachValidator
3
+ def validate_each(record, attribute, value)
4
+ record.errors[attribute] << (options[:message] || I18n.t('admin.settings.color_invalid')) unless value.blank? || self.class.matches?(value)
5
+ end
6
+ def self.matches?(value)
7
+ return false unless value
8
+ /^(?:[0-9a-f]{3})(?:[0-9a-f]{3})?$/i.match(value).nil? ? false : true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module RailsAdminSettings
2
+ def self.kinds
3
+ [
4
+ 'string',
5
+ 'text',
6
+ 'integer',
7
+ 'boolean',
8
+ 'html',
9
+ 'code',
10
+ 'sanitized',
11
+ 'yaml',
12
+ 'phone',
13
+ 'phones',
14
+ 'email',
15
+ 'address',
16
+ 'file',
17
+ 'image',
18
+ 'url',
19
+ 'domain',
20
+ 'color',
21
+
22
+ 'js',
23
+ 'css'
24
+ ].freeze
25
+ end
26
+
27
+ def self.types
28
+ self.kinds
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module RailsAdminSettings
2
+ module Mongoid
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ include ::Mongoid::Document
6
+ include ::Mongoid::Timestamps::Short
7
+
8
+ store_in collection: "rails_admin_settings".freeze
9
+ field :enabled, type: ::Mongoid::VERSION.to_i < 4 ? Boolean : ::Mongoid::Boolean, default: true
10
+ field :kind, type: String, default: RailsAdminSettings.types.first
11
+ field :ns, type: String, default: 'main'
12
+ field :key, type: String
13
+ field :raw, type: String
14
+ field :label, type: String
15
+ index({ns: 1, key: 1}, {unique: true, sparse: true})
16
+
17
+ field :cache_keys_str, type: String, default: ""
18
+ def cache_keys
19
+ cache_keys_str.split(" ").map { |k| k.strip }.reject { |k| k.blank? }
20
+ end
21
+
22
+ after_save :clear_cache
23
+ def clear_cache
24
+ cache_keys.each do |k|
25
+ Rails.cache.delete(k)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,218 @@
1
+ module RailsAdminSettings
2
+ # we are inheriting from BasicObject so we don't get a bunch of methods from
3
+ # Kernel or Object
4
+ class Namespaced < BasicObject
5
+ attr_accessor :settings, :fallback
6
+ attr_reader :loaded, :mutex, :ns_mutex, :name
7
+
8
+ def initialize(name)
9
+ self.settings = {}
10
+ @mutex = ::Mutex.new
11
+ @ns_mutex = ::Mutex.new
12
+ @loaded = false
13
+ @locked = false
14
+ @name = name
15
+ end
16
+
17
+ def nil?
18
+ false
19
+ end
20
+ def inspect
21
+ "#<RailsAdminSettings::Namespaced name: #{@name.inspect}, fallback: #{@fallback.inspect}, loaded: #{@loaded}>".freeze
22
+ end
23
+ def pretty_inspect
24
+ inspect
25
+ end
26
+
27
+ def load!
28
+ mutex.synchronize do
29
+ return if loaded
30
+ @loaded = true
31
+ @settings = {}
32
+ ::RailsAdminSettings::Setting.ns(@name).each do |setting|
33
+ @settings[setting.key] = setting
34
+ end
35
+ end
36
+ end
37
+
38
+ def unload!
39
+ mutex.synchronize do
40
+ @loaded = false
41
+ @settings = {}
42
+ end
43
+ end
44
+
45
+ # returns setting object
46
+ def get(key, options = {})
47
+ load!
48
+ key = key.to_s
49
+ mutex.synchronize do
50
+ @locked = true
51
+ v = @settings[key]
52
+ if v.nil?
53
+ unless @fallback.nil? || @fallback == @name
54
+ v = ::Settings.ns(@fallback).getnc(key)
55
+ end
56
+ if v.nil?
57
+ v = set(key, options[:default], options)
58
+ end
59
+ end
60
+ @locked = false
61
+ v
62
+ end
63
+ end
64
+
65
+ # returns setting object
66
+ def getnc(key)
67
+ load!
68
+ mutex.synchronize do
69
+ self.settings[key]
70
+ end
71
+ end
72
+
73
+ def set(key, value = nil, options = {})
74
+ load! unless @locked
75
+ key = key.to_s
76
+ options.symbolize_keys!
77
+
78
+ if !options[:type].nil? && options[:type] == 'yaml' && !value.nil?
79
+ if value.class.name != 'String'
80
+ value = value.to_yaml
81
+ end
82
+ end
83
+
84
+ unless options[:cache_keys].nil?
85
+ if options[:cache_keys].class.name == 'String'
86
+ options[:cache_keys_str] = options.delete(:cache_keys)
87
+ elsif options[:cache_keys].class.name == 'Array'
88
+ options[:cache_keys_str] = options.delete(:cache_keys).map { |k| k.to_s }.join(" ")
89
+ else
90
+ options[:cache_keys_str] = options.delete(:cache_keys).to_s
91
+ end
92
+ end
93
+
94
+ options.merge!(value: value)
95
+ if @locked
96
+ write_to_database(key, options)
97
+ else
98
+ mutex.synchronize do
99
+ write_to_database(key, options)
100
+ end
101
+ end
102
+ end
103
+
104
+ def enabled?(key, options = {})
105
+ get(key, options).enabled?
106
+ end
107
+
108
+ def []=(key, value)
109
+ set(key, value)
110
+ end
111
+ def [](key)
112
+ get(key)
113
+ end
114
+
115
+ def destroy!(key)
116
+ load!
117
+ key = key.to_s
118
+ mutex.synchronize do
119
+ ::RailsAdminSettings::Setting.where(ns: @name, key: key).destroy_all
120
+ @settings.delete(key)
121
+ end
122
+ end
123
+
124
+ def destroy_all!
125
+ mutex.synchronize do
126
+ ::RailsAdminSettings::Setting.where(ns: @name).destroy_all
127
+ @loaded = false
128
+ @settings = {}
129
+ end
130
+ end
131
+
132
+ # returns processed setting value
133
+ def method_missing(key, *args)
134
+ key = key.to_s
135
+ if key.end_with?('_enabled?')
136
+ key = key[0..-10]
137
+ v = get(key)
138
+ if v.nil?
139
+ set(key, '').enabled
140
+ else
141
+ v.enabled
142
+ end
143
+ elsif key.end_with?('_enabled=')
144
+ key = key[0..-10]
145
+ v = get(key)
146
+ if ::RailsAdminSettings.mongoid?
147
+ if ::Mongoid::VERSION >= "4.0.0"
148
+ v.set(enabled: args.first)
149
+ else
150
+ v.set("enabled", args.first)
151
+ end
152
+ else
153
+ v.enabled = args.first
154
+ v.save!
155
+ end
156
+ v.enabled
157
+ elsif key.end_with?('=')
158
+ key = key[0..-2]
159
+ options = args[1] || {}
160
+ value = args.first
161
+ set(key, value, options).val
162
+ else
163
+ v = get(key, args.first || {})
164
+ if v.nil?
165
+ ''
166
+ else
167
+ v.val
168
+ end
169
+ end
170
+ end
171
+
172
+ def write_to_database(key, options)
173
+ is_file = !options[:kind].nil? && (options[:kind] == 'image' || options[:kind] == 'file')
174
+ if is_file
175
+ options[:raw] = ''
176
+ file = options[:value]
177
+ else
178
+ options[:raw] = options[:value]
179
+ end
180
+
181
+ options.delete(:value)
182
+ options.delete(:default)
183
+ options[:ns] = @name
184
+
185
+ if @settings[key].nil?
186
+ options.delete(:overwrite)
187
+ v = ::RailsAdminSettings::Setting.create(options.merge(key: key))
188
+ if !v.persisted?
189
+ if v.errors[:key].any?
190
+ v = ::RailsAdminSettings::Setting.where(key: key).first
191
+ if v.nil?
192
+ ::Kernel.raise ::RailsAdminSettings::PersistenceException, 'Fatal: error in key and not in DB'
193
+ end
194
+ else
195
+ ::Kernel.raise ::RailsAdminSettings::PersistenceException, v.errors.full_messages.join(',')
196
+ end
197
+ end
198
+ @settings[key] = v
199
+ else
200
+ opts = options.dup
201
+ if options[:overwrite] == false && !@settings[key].value.blank?
202
+ opts.delete(:raw)
203
+ opts.delete(:value)
204
+ opts.delete(:enabled)
205
+ end
206
+ opts.delete(:overwrite)
207
+ @settings[key].update_attributes!(opts)
208
+ end
209
+ if is_file
210
+ if options[:overwrite] != false || !@settings[key].file?
211
+ @settings[key].file = file
212
+ @settings[key].save!
213
+ end
214
+ end
215
+ @settings[key]
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,159 @@
1
+ module RailsAdminSettings
2
+ module Processing
3
+ RailsAdminSettings.kinds.each do |dkind|
4
+ define_method "#{dkind}_kind?" do
5
+ dkind == kind
6
+ end
7
+ define_method "#{dkind}_type?" do
8
+ dkind == kind
9
+ end
10
+ end
11
+
12
+ def text_kind?
13
+ (RailsAdminSettings.kinds - ['phone', 'phones', 'integer', 'yaml', 'boolean']).include? kind
14
+ end
15
+
16
+ def upload_kind?
17
+ ['file', 'image'].include? kind
18
+ end
19
+
20
+ def html_kind?
21
+ ['html', 'code', 'sanitized'].include? kind
22
+ end
23
+ alias_method :text_type?, :text_kind?
24
+ alias_method :upload_type?, :upload_kind?
25
+ alias_method :html_type?, :html_kind?
26
+
27
+ def value
28
+ if upload_kind?
29
+ if file?
30
+ file.url
31
+ else
32
+ nil
33
+ end
34
+ elsif raw.blank? || disabled?
35
+ default_value
36
+ else
37
+ processed_value
38
+ end
39
+ end
40
+
41
+ def blank?
42
+ if file_kind?
43
+ file.url.nil?
44
+ elsif raw.blank? || disabled?
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def to_s
52
+ if yaml_kind? || phone_kind? || integer_kind?
53
+ raw
54
+ else
55
+ value
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def sanitize_value
62
+ require_sanitize do
63
+ self.raw = Sanitize.clean(value, Sanitize::Config::RELAXED)
64
+ end
65
+ end
66
+
67
+ def default_value
68
+ if html_kind?
69
+ ''.html_safe
70
+ elsif text_kind?
71
+ ''
72
+ elsif integer_kind?
73
+ 0
74
+ elsif yaml_kind?
75
+ elsif boolean_kind?
76
+ false
77
+ elsif yaml_type?
78
+ nil
79
+ elsif phone_kind?
80
+ require_russian_phone do
81
+ RussianPhone::Number.new('')
82
+ end
83
+ elsif phones_kind?
84
+ []
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ def default_serializable_value
91
+ if phones_kind?
92
+ ''
93
+ elsif boolean_type?
94
+ 'false'
95
+ else
96
+ default_value
97
+ end
98
+ end
99
+
100
+ def process_text
101
+ text = raw.dup
102
+ text.gsub!('{{year}}', Time.now.strftime('%Y'))
103
+ text.gsub! /\{\{year\|([\d]{4})\}\}/ do
104
+ if Time.now.strftime('%Y') == $1
105
+ $1
106
+ else
107
+ "#{$1}-#{Time.now.strftime('%Y')}"
108
+ end
109
+ end
110
+ text = text.html_safe if html_kind?
111
+ text
112
+ end
113
+
114
+ def load_phone
115
+ require_russian_phone do
116
+ RussianPhone::Number.new(raw)
117
+ end
118
+ end
119
+
120
+ def load_phones
121
+ require_russian_phone do
122
+ raw.gsub("\r", '').split("\n").map{|i| RussianPhone::Number.new(i)}
123
+ end
124
+ end
125
+
126
+ def load_yaml
127
+ require_safe_yaml do
128
+ YAML.safe_load(raw)
129
+ end
130
+ end
131
+
132
+ def processed_value
133
+ if text_kind?
134
+ process_text
135
+ elsif integer_kind?
136
+ raw.to_i
137
+ elsif yaml_kind?
138
+ load_yaml
139
+ elsif boolean_kind?
140
+ raw == 'true'
141
+ elsif phone_kind?
142
+ load_phone
143
+ elsif phones_kind?
144
+ load_phones
145
+ elsif file_kind?
146
+ file.url
147
+ else
148
+ puts "[rails_admin_settings] Unknown field kind: #{kind}".freeze
149
+ nil
150
+ end
151
+ end
152
+
153
+ def self.included(base)
154
+ base.class_eval do
155
+ alias_method :val, :value
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,74 @@
1
+ module RailsAdminSettings
2
+
3
+ module RailsAdminConfig
4
+
5
+ def self.included(base)
6
+ if base.respond_to?(:rails_admin)
7
+ base.rails_admin do
8
+ navigation_label I18n.t('admin.settings.label')
9
+
10
+ list do
11
+ if Object.const_defined?('RailsAdminToggleable')
12
+ field :enabled, :toggle
13
+ else
14
+ field :enabled
15
+ end
16
+ field :kind
17
+ field :ns
18
+ field :name
19
+ field :raw do
20
+ pretty_value do
21
+ if bindings[:object].file_kind?
22
+ "<a href='#{CGI::escapeHTML(bindings[:object].file.url)}'>#{CGI::escapeHTML(bindings[:object].to_path)}</a>".html_safe.freeze
23
+ elsif bindings[:object].image_kind?
24
+ "<a href='#{CGI::escapeHTML(bindings[:object].file.url)}'><img src='#{CGI::escapeHTML(bindings[:object].file.url)}' /></a>".html_safe.freeze
25
+ else
26
+ value
27
+ end
28
+ end
29
+ end
30
+ if ::Settings.table_exists?
31
+ nss = ::RailsAdminSettings::Setting.pluck(:ns).uniq.map { |c| "ns_#{c.gsub('-', '_')}".to_sym }
32
+ scopes([nil] + nss)
33
+ end
34
+ end
35
+
36
+ edit do
37
+ field :enabled
38
+ field :label do
39
+ read_only true
40
+ help false
41
+ end
42
+ field :kind do
43
+ read_only true
44
+ help false
45
+ end
46
+ field :raw do
47
+ partial "setting_value".freeze
48
+ visible do
49
+ !bindings[:object].upload_kind?
50
+ end
51
+ end
52
+ if Settings.file_uploads_supported
53
+ field :file, Settings.file_uploads_engine do
54
+ visible do
55
+ bindings[:object].upload_kind?
56
+ end
57
+ end
58
+ end
59
+ field :cache_keys_str, :text do
60
+ visible do
61
+ render_object = (bindings[:controller] || bindings[:view])
62
+ render_object and render_object.current_user.admin?
63
+ end
64
+ end
65
+ end
66
+ end
67
+ else
68
+ puts "[rails_admin_settings] Problem: model does not respond to rails_admin: this should not happen"
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end