golden-setting 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +198 -0
  6. data/Rakefile +1 -0
  7. data/app/assets/javascripts/golden/setting/settings.js.coffee +8 -0
  8. data/app/assets/stylesheets/golden/setting/settings.css.sass +0 -0
  9. data/app/controllers/golden/setting/settings_controller.rb +65 -0
  10. data/app/helpers/golden/setting/settings_helper.rb +52 -0
  11. data/app/models/concerns/golden/setting/resourced.rb +48 -0
  12. data/app/views/golden/setting/settings/_batch_form.html.erb +24 -0
  13. data/app/views/golden/setting/settings/_form.html.erb +25 -0
  14. data/app/views/golden/setting/settings/_setting.html.erb +18 -0
  15. data/app/views/golden/setting/settings/_table.html.erb +34 -0
  16. data/app/views/golden/setting/settings/edit.html.erb +30 -0
  17. data/app/views/golden/setting/settings/index.html.erb +33 -0
  18. data/app/views/golden/setting/settings/list.html.erb +19 -0
  19. data/app/views/golden/setting/settings/new.html.erb +27 -0
  20. data/app/views/golden/setting/settings/show.html.erb +24 -0
  21. data/config/locales/settings.en.yml +52 -0
  22. data/config/locales/settings.zh-TW.yml +52 -0
  23. data/config/routes.rb +8 -0
  24. data/golden-setting.gemspec +28 -0
  25. data/lib/generators/golden/setting/USAGE +18 -0
  26. data/lib/generators/golden/setting/install_generator.rb +86 -0
  27. data/lib/generators/golden/setting/templates/create_golden_settings.rb +17 -0
  28. data/lib/generators/golden/setting/templates/golden_setting.rb +36 -0
  29. data/lib/generators/golden/setting/templates/setting.rb +3 -0
  30. data/lib/generators/golden/setting/templates/settings.en.yml +15 -0
  31. data/lib/generators/golden/setting/templates/settings.zh-TW.yml +15 -0
  32. data/lib/golden/setting/action.rb +58 -0
  33. data/lib/golden/setting/base.rb +47 -0
  34. data/lib/golden/setting/cache.rb +55 -0
  35. data/lib/golden/setting/engine.rb +16 -0
  36. data/lib/golden/setting/error.rb +38 -0
  37. data/lib/golden/setting/form_option.rb +69 -0
  38. data/lib/golden/setting/group.rb +17 -0
  39. data/lib/golden/setting/name.rb +30 -0
  40. data/lib/golden/setting/resource.rb +44 -0
  41. data/lib/golden/setting/value.rb +67 -0
  42. data/lib/golden/setting/version.rb +5 -0
  43. data/lib/golden/setting.rb +54 -0
  44. data/spec/base_spec.rb +125 -0
  45. data/spec/cache_spec.rb +40 -0
  46. data/spec/resource_spec.rb +72 -0
  47. data/spec/spec_helper.rb +32 -0
  48. data/spec/support/models.rb +15 -0
  49. data/spec/support/schema.rb +26 -0
  50. metadata +194 -0
@@ -0,0 +1,52 @@
1
+ en:
2
+ golden:
3
+ setting:
4
+ field_types:
5
+ string: 'String'
6
+ boolean: 'Boolean'
7
+ select: 'Select'
8
+ radio_buttons: 'Radio Buttons'
9
+ check_boxes: 'Check Boxes'
10
+ settings:
11
+ edit:
12
+ title: 'Editing setting'
13
+ index:
14
+ title: 'Settings'
15
+ list: 'List'
16
+ new:
17
+ title: 'New setting'
18
+ show:
19
+ title: 'Setting - %{name}'
20
+ list:
21
+ title: 'Listing settings'
22
+ index: 'Settings'
23
+ batch_form:
24
+ submit: 'Save'
25
+
26
+ activerecord:
27
+ models:
28
+ golden/setting/base: 'Setting'
29
+ attributes:
30
+ golden/setting/base:
31
+ group: 'Group'
32
+ name: 'Name'
33
+ value: 'Value'
34
+ resource: 'Resource belonged'
35
+ form_options: 'Form options'
36
+ field_type: 'Field type'
37
+ field_values: 'Field values'
38
+
39
+ simple_form:
40
+ labels:
41
+ setting:
42
+ hints:
43
+ setting:
44
+ field_values: 'Please define an array of values in Ruby.'
45
+ placeholders:
46
+ setting:
47
+
48
+ helpers:
49
+ submit:
50
+ setting:
51
+ create: 'Create %{model}'
52
+ update: 'Update %{model}'
@@ -0,0 +1,52 @@
1
+ zh-TW:
2
+ golden:
3
+ setting:
4
+ field_types:
5
+ string: '字串'
6
+ boolean: '布林值'
7
+ select: '下拉選單'
8
+ radio_buttons: '單選按鈕'
9
+ check_boxes: '多選按鈕'
10
+ settings:
11
+ edit:
12
+ title: '編輯設定'
13
+ index:
14
+ title: '設定管理'
15
+ list: '設定列表'
16
+ new:
17
+ title: '新增設定'
18
+ show:
19
+ title: '設定 - %{name}'
20
+ list:
21
+ title: '設定列表'
22
+ index: '設定管理'
23
+ batch_form:
24
+ submit: '儲存'
25
+
26
+ activerecord:
27
+ models:
28
+ golden/setting/base: '設定'
29
+ attributes:
30
+ golden/setting/base:
31
+ group: '類別'
32
+ name: '名稱'
33
+ value: '設定值'
34
+ resource: '專屬對象'
35
+ form_options: '表單格式'
36
+ field_type: '欄位型式'
37
+ field_values: '欄位值選項'
38
+
39
+ simple_form:
40
+ labels:
41
+ setting:
42
+ hints:
43
+ setting:
44
+ field_values: '請以 Ruby 語法定義一個欄位值陣列。'
45
+ placeholders:
46
+ setting:
47
+
48
+ helpers:
49
+ submit:
50
+ setting:
51
+ create: '建立%{model}'
52
+ update: '確認'
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Golden::Setting::Engine.routes.draw do
2
+ resources :settings do
3
+ collection do
4
+ get :list
5
+ put :batch_update
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'golden/setting/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'golden-setting'
8
+ spec.version = Golden::Setting::VERSION
9
+ spec.authors = ['Tse-Ching Ho']
10
+ spec.email = ['tsechingho@gmail.com']
11
+ spec.description = %q{Golden Setting persists settings in database and keep alive in cache.}
12
+ spec.summary = %q{persists settings in database and keep alive in cache.}
13
+ spec.homepage = 'https://github.com/goldenio/golden-setting'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activerecord', '>= 3.2.13', '< 5'
22
+ spec.add_dependency 'railties', '>= 3.2.13', '< 5'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec', '>= 2.13.0'
27
+ spec.add_development_dependency 'sqlite3'
28
+ end
@@ -0,0 +1,18 @@
1
+ Description:
2
+ It will generate model and migration for use.
3
+
4
+ Example:
5
+ rails generate golden:setting:install setting
6
+
7
+ This will create/modify:
8
+ db/migrate/20130516050309_create_golden_settings.rb
9
+ app/models/setting.rb
10
+ app/assets/javascripts/application.js
11
+ app/assets/stylesheets/application.css
12
+ config/initializers/golden_setting.rb
13
+ config/application.rb
14
+ config/locales/golden/settings.en.yml
15
+ config/locales/golden/settings.zh-TW.yml
16
+ config/routes.rb
17
+ app/controllers/application_controller.rb
18
+ app/views/layouts/application.html.erb
@@ -0,0 +1,86 @@
1
+ require 'active_record/migration'
2
+
3
+ class Golden::Setting::InstallGenerator < Rails::Generators::NamedBase
4
+ include Rails::Generators::Migration
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ # argument :name, type: :string, default: 'setting'
9
+
10
+ def self.next_migration_number dirname
11
+ next_migration_number = current_migration_number(dirname) + 1
12
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
13
+ end
14
+
15
+ def generate_migration
16
+ file = 'create_golden_settings.rb'
17
+ #migration_template file, File.join('db/migrate', class_path, file)
18
+ end
19
+
20
+ def generate_model
21
+ # generate :model, name, '--skip-migration'
22
+ file = 'setting.rb'
23
+ log :model, file
24
+ template file, File.join('app/models', class_path, "#{file_name}.rb"), verbose: false
25
+ end
26
+
27
+ def inject_javascript
28
+ file = 'app/assets/javascripts/application.js'
29
+ log :javascripts, 'application.js'
30
+ sentinel = "//= require_tree .\n"
31
+ insert_into_file file, before: sentinel, verbose: false do
32
+ "//= require golden/setting/settings\n"
33
+ end
34
+ end
35
+
36
+ def inject_stylesheet
37
+ file = 'app/assets/stylesheets/application.css'
38
+ log :stylesheets, 'application.css'
39
+ sentinel = " *= require_self\n"
40
+ insert_into_file file, before: sentinel, verbose: false do
41
+ " *= require golden/setting/settings\n"
42
+ end
43
+ end
44
+
45
+ def generate_initializer
46
+ file = 'golden_setting.rb'
47
+ log :initializer, file
48
+ template file, File.join('config/initializers', class_path, file), verbose: false
49
+ end
50
+
51
+ def add_locales_path
52
+ file = 'config/application.rb'
53
+ sentinel = "# config.i18n.default_locale = :de\n"
54
+ insert_into_file file, after: sentinel do
55
+ " config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]\n"
56
+ end
57
+ end
58
+
59
+ def generate_locales
60
+ files = %w{settings.en.yml settings.zh-TW.yml}
61
+ files.each do |file|
62
+ log :locales, "golden/#{file}"
63
+ template file, File.join('config/locales/golden', class_path, file), verbose: false
64
+ end
65
+ end
66
+
67
+ def inject_routes
68
+ route "mount Golden::Setting::Engine, at: '/'\n"
69
+ end
70
+
71
+ def inject_controller
72
+ file = 'app/controllers/application_controller.rb'
73
+ sentinel = "protect_from_forgery with: :exception\n"
74
+ insert_into_file file, after: sentinel do
75
+ " alias_method :authenticate_session!, :authenticate_user!\n"
76
+ end
77
+ end
78
+
79
+ def inject_layout
80
+ file = 'app/views/layouts/application.html.erb'
81
+ sentinel = "<%= yield %>\n"
82
+ gsub_file file, "<%= yield %>\n" do |match|
83
+ %Q{<div class="<%= controller_name %>"><%= yield %></div>\n}
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,17 @@
1
+ class CreateGoldenSettings < ActiveRecord::Migration
2
+ def change
3
+ create_table :golden_settings do |t|
4
+ t.string :group
5
+ t.string :name, null: false
6
+ t.text :value
7
+ t.string :form_options
8
+ t.references :resource, polymorphic: true
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :golden_settings,
13
+ [:group, :name, :resource_id, :resource_type],
14
+ unique: true,
15
+ name: 'index_golden_settings_on_group_and_name_and_resource'
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ Golden::Setting.configure do |config|
2
+ # Define setting class.
3
+ #config.setting_class = 'Setting'
4
+
5
+ # Define database table.
6
+ #config.table_name = 'golden_settings'
7
+
8
+ # Add extra field types with array of name and value.
9
+ # Name will be used as translation key.
10
+ # Value is a symbol name of field types used by `simple_form`.
11
+ #config.field_types += [
12
+ # ['string', :string]
13
+ #]
14
+
15
+ # Add avaiable groups with array of name and value.
16
+ # Name will be used as translation key.
17
+ # Default is a blank array. You SHOULD give one at least
18
+ config.groups += [
19
+ ['site', 'site']
20
+ ]
21
+
22
+ # Define default group with name of groups.
23
+ # Default is ''.
24
+ config.default_group = 'site'
25
+
26
+ # Don't show groups by given method.
27
+ #config.skip_groups_method = nil
28
+
29
+ # Don't show listed settings.
30
+ #config.skip_settings += %W{
31
+ #}
32
+
33
+ # Define default tab with name of groups.
34
+ # Default is ''.
35
+ config.default_tab = 'site'
36
+ end
@@ -0,0 +1,3 @@
1
+ class <%= class_name %> < Golden::Setting::Base
2
+ include Golden::Setting::Cache
3
+ end
@@ -0,0 +1,15 @@
1
+ en:
2
+ golden:
3
+ setting:
4
+ groups:
5
+ site: 'Site'
6
+ names:
7
+ site_title: 'Site Title'
8
+
9
+ # simple_form:
10
+ # labels:
11
+ # setting:
12
+ # hints:
13
+ # setting:
14
+ # placeholders:
15
+ # setting:
@@ -0,0 +1,15 @@
1
+ zh-TW:
2
+ golden:
3
+ setting:
4
+ groups:
5
+ site: '網站'
6
+ names:
7
+ site_title: '名稱'
8
+
9
+ # simple_form:
10
+ # labels:
11
+ # setting:
12
+ # hints:
13
+ # setting:
14
+ # placeholders:
15
+ # setting:
@@ -0,0 +1,58 @@
1
+ module Golden
2
+ module Setting
3
+ module Action
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ end
7
+
8
+ module ClassMethods
9
+ # Setting.add 'theme', 'name', 'golden', :select, %w(golden white)
10
+ def add group, name, value, field_type = nil, field_values = nil
11
+ object(name) ||
12
+ new_object(name).tap do |s|
13
+ s.group = group
14
+ s.value = value
15
+ s.field_type = field_type if field_type
16
+ s.field_values = field_values if field_values
17
+ s.save!
18
+ end
19
+ end
20
+
21
+ def destroy name
22
+ raise Error::SettingNotFound, name unless self[name]
23
+ object(name).destroy
24
+ end
25
+
26
+ def select_object
27
+ raise Error::SettingNoMethod, :select_object unless defined? super
28
+ super
29
+ end
30
+
31
+ def list starting_with = nil
32
+ result = {}
33
+ records = select_object
34
+ if starting_with
35
+ records = records.where('name LIKE ?', "#{starting_with}%")
36
+ end
37
+ records.each do |record|
38
+ result[record.name] = record.value
39
+ end
40
+ result.with_indifferent_access
41
+ end
42
+
43
+ def merge! name, value
44
+ raise Error::SettingNotFound, name unless self[name]
45
+ raise Error::SettingNotHash, value unless value.is_a? Hash
46
+
47
+ old_value = self[name] || {}
48
+ raise Error::SettingNotHash, old_value unless old_value.is_a? Hash
49
+
50
+ new_value = old_value.merge(value)
51
+ self[name] = new_value if new_value != old_value
52
+
53
+ new_value
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ module Golden
2
+ module Setting
3
+ class Base < ActiveRecord::Base
4
+ include Error
5
+ include Group
6
+ include Name
7
+ include Value
8
+ include FormOption
9
+ include Action
10
+
11
+ self.table_name = Golden::Setting.table_name
12
+
13
+ class << self
14
+ def without_resource
15
+ where(resource_type: nil, resource_id: nil)
16
+ end
17
+
18
+ def named name
19
+ without_resource.where(name: name)
20
+ end
21
+
22
+ def select_object fields = %w{name value}
23
+ without_resource.select(fields)
24
+ end
25
+
26
+ def method_missing method, *args
27
+ super method, *args
28
+ rescue NoMethodError
29
+ name = method.to_s
30
+ if name =~ /=$/
31
+ name.gsub!('=', '')
32
+ self[name] = args.first
33
+ elsif name =~ /\?$/
34
+ name.gsub!('?', '')
35
+ self.is_true? name
36
+ else
37
+ self[name]
38
+ end
39
+ end
40
+ end
41
+
42
+ # def initialize attributes = nil, options = {}
43
+ # super attributes, options
44
+ # end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ module Golden
2
+ module Setting
3
+ module Cache
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ after_create :rewrite_cache
7
+ after_update :rewrite_cache
8
+ after_destroy :delete_cache
9
+ end
10
+
11
+ module ClassMethods
12
+ def fetch_cache name
13
+ if block_given?
14
+ ::Rails.cache.fetch("setting:#{name}") { yield }
15
+ else
16
+ ::Rails.cache.fetch "setting:#{name}"
17
+ end
18
+ end
19
+
20
+ def rewrite_cache name, value
21
+ ::Rails.cache.write "setting:#{name}", value
22
+ end
23
+
24
+ def delete_cache name
25
+ ::Rails.cache.delete "setting:#{name}"
26
+ end
27
+
28
+ def [] name
29
+ obj = fetch_cache(name) { super(name) }
30
+ obj || defaults[name.to_sym]
31
+ end
32
+
33
+ def []= name, value
34
+ if self[name] != value
35
+ super name, value
36
+ end
37
+ end
38
+
39
+ def is_true? name
40
+ fetch_cache("#{name}?") { super(name) }
41
+ end
42
+ end
43
+
44
+ def rewrite_cache
45
+ self.class.rewrite_cache name, value
46
+ self.class.rewrite_cache "#{name}?", self.class.base_class.is_true?(name)
47
+ end
48
+
49
+ def delete_cache
50
+ self.class.delete_cache name
51
+ self.class.delete_cache "#{name}?"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,16 @@
1
+ module Golden
2
+ module Setting
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Golden::Setting
5
+ engine_name :golden_setting
6
+
7
+ config.autoload_paths += [
8
+ root.join('app', 'models', 'concerns').to_s
9
+ ]
10
+
11
+ config.after_initialize do
12
+ Golden::Setting::Base.table_name = Golden::Setting.table_name
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module Golden
2
+ module Setting
3
+ module Error
4
+ class SettingNotFound < RuntimeError
5
+ def initialize name, msg = nil
6
+ @name = name
7
+ super msg
8
+ end
9
+
10
+ def to_s
11
+ "Setting name '#{@name}' not found!"
12
+ end
13
+ end
14
+
15
+ class SettingNoMethod < NoMethodError
16
+ def initialize method, msg = nil
17
+ @method = method
18
+ super msg
19
+ end
20
+
21
+ def to_s
22
+ raise "Please define method '#{@method}' first!"
23
+ end
24
+ end
25
+
26
+ class SettingNotHash < TypeError
27
+ def initialize value, msg = nil
28
+ @value = value
29
+ super msg
30
+ end
31
+
32
+ def to_s
33
+ "Setting value '#{@value}' is not a hash!"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,69 @@
1
+ module Golden
2
+ module Setting
3
+ module FormOption
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ cattr_accessor :default_form_options
7
+ @@default_form_options = {
8
+ type: :string,
9
+ collection: []
10
+ }
11
+ serialize :form_options
12
+ attr_accessible :field_type, :field_values
13
+ end
14
+
15
+ module ClassMethods
16
+ def is_true? name
17
+ record = object(name)
18
+ raise Error::SettingNotFound, name unless record
19
+
20
+ case record.field_type
21
+ when :boolean
22
+ self[name] == '1'
23
+ else
24
+ self[name].present?
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize attributes = nil, options = {}
30
+ super attributes, options
31
+ self.form_options
32
+ end
33
+
34
+ def form_options
35
+ case self[:form_options]
36
+ when String
37
+ YAML::load self[:form_options]
38
+ when Hash
39
+ super
40
+ else
41
+ self[:form_options] = default_form_options.dup
42
+ end
43
+ end
44
+
45
+ def field_type
46
+ form_options[:type]
47
+ end
48
+
49
+ def field_type= new_value
50
+ return unless new_value
51
+ self.form_options[:type] = new_value.to_sym
52
+ end
53
+
54
+ def field_values
55
+ case form_options[:collection]
56
+ when String
57
+ eval form_options[:collection]
58
+ else
59
+ form_options[:collection]
60
+ end
61
+ end
62
+
63
+ def field_values= new_value
64
+ return unless new_value
65
+ self.form_options[:collection] = eval(new_value)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,17 @@
1
+ module Golden
2
+ module Setting
3
+ module Group
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ attr_accessible :group
7
+ # validates :group, presence: true
8
+ end
9
+
10
+ module ClassMethods
11
+ def grouped name
12
+ where(group: name)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module Golden
2
+ module Setting
3
+ module Name
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ attr_accessible :name
7
+ validates :name, presence: true
8
+ end
9
+
10
+ module ClassMethods
11
+ def named name
12
+ raise Error::SettingNoMethod, :named unless defined? super
13
+ super
14
+ end
15
+
16
+ def object name
17
+ named(name).first
18
+ end
19
+
20
+ def new_object name
21
+ named(name).new
22
+ end
23
+
24
+ def first_or_new_object name
25
+ named(name).first_or_initialize
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end