golden-setting 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 (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