settingsdb-rails 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +43 -0
  3. data/Rakefile +40 -0
  4. data/lib/generators/settingsdb/USAGE +8 -0
  5. data/lib/generators/settingsdb/install_generator.rb +103 -0
  6. data/lib/generators/settingsdb/templates/initializer.rb +3 -0
  7. data/lib/generators/settingsdb/templates/migration.rb +18 -0
  8. data/lib/generators/settingsdb/templates/migration_exists.rb +10 -0
  9. data/lib/settingsdb-rails.rb +6 -0
  10. data/lib/settingsdb/acts_as_setting.rb +48 -0
  11. data/lib/settingsdb/defaults.rb +91 -0
  12. data/lib/settingsdb/generators/generated_attribute.rb +104 -0
  13. data/lib/settingsdb/settings.rb +121 -0
  14. data/lib/settingsdb/version.rb +3 -0
  15. data/test/acts_as_setting_test.rb +90 -0
  16. data/test/dummy/Rakefile +7 -0
  17. data/test/dummy/app/assets/javascripts/application.js +9 -0
  18. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  19. data/test/dummy/app/controllers/application_controller.rb +3 -0
  20. data/test/dummy/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy/app/models/config_a.rb +3 -0
  22. data/test/dummy/app/models/config_b.rb +3 -0
  23. data/test/dummy/app/models/test_setting.rb +3 -0
  24. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +45 -0
  27. data/test/dummy/config/boot.rb +10 -0
  28. data/test/dummy/config/database.yml +25 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +30 -0
  31. data/test/dummy/config/environments/production.rb +60 -0
  32. data/test/dummy/config/environments/test.rb +39 -0
  33. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/test/dummy/config/initializers/inflections.rb +10 -0
  35. data/test/dummy/config/initializers/mime_types.rb +5 -0
  36. data/test/dummy/config/initializers/secret_token.rb +7 -0
  37. data/test/dummy/config/initializers/session_store.rb +8 -0
  38. data/test/dummy/config/initializers/settingsdb.rb +3 -0
  39. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  40. data/test/dummy/config/locales/en.yml +5 -0
  41. data/test/dummy/config/routes.rb +58 -0
  42. data/test/dummy/db/development.sqlite3 +0 -0
  43. data/test/dummy/db/migrate/20120327011030_settingsdb_create_test_settings.rb +12 -0
  44. data/test/dummy/db/migrate/20120327011127_settingsdb_create_config_as.rb +12 -0
  45. data/test/dummy/db/migrate/20120327011134_settingsdb_create_config_bs.rb +12 -0
  46. data/test/dummy/db/schema.rb +43 -0
  47. data/test/dummy/db/test.sqlite3 +0 -0
  48. data/test/dummy/log/development.log +512 -0
  49. data/test/dummy/log/test.log +3749 -0
  50. data/test/dummy/public/404.html +26 -0
  51. data/test/dummy/public/422.html +26 -0
  52. data/test/dummy/public/500.html +26 -0
  53. data/test/dummy/public/favicon.ico +0 -0
  54. data/test/dummy/script/rails +6 -0
  55. data/test/dummy/test/fixtures/config_as.yml +11 -0
  56. data/test/dummy/test/fixtures/config_bs.yml +11 -0
  57. data/test/dummy/test/fixtures/configurations.yml +11 -0
  58. data/test/dummy/test/fixtures/settings.yml +11 -0
  59. data/test/dummy/test/fixtures/test_settings.yml +11 -0
  60. data/test/dummy/test/unit/config_a_test.rb +7 -0
  61. data/test/dummy/test/unit/config_b_test.rb +7 -0
  62. data/test/dummy/test/unit/configuration_test.rb +7 -0
  63. data/test/dummy/test/unit/setting_test.rb +7 -0
  64. data/test/dummy/test/unit/test_setting_test.rb +7 -0
  65. data/test/settingsdb_defaults_test.rb +71 -0
  66. data/test/settingsdb_generator_test.rb +42 -0
  67. data/test/test_helper.rb +10 -0
  68. metadata +215 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,43 @@
1
+ = SettingsDB-Rails
2
+
3
+ SettingsDB provides an easy to use mechanism for keeping application settings
4
+ in your database. It also provides a namespacing mechanism to keep settings
5
+ from different areas separate, as well as a defaults option.
6
+
7
+ == Getting Started
8
+
9
+ Add settingsdb-rails to your Gemfile:
10
+
11
+ gem 'settingsdb-rails'
12
+
13
+ To install the model, migration, and defaults initializer:
14
+
15
+ rails generate settingsdb:install
16
+ rake db:migrate
17
+
18
+ This will create a `settings` model, migration, and initializer file.
19
+ Now just reference settings in your code:
20
+
21
+ <% title Setting[:site_title] %>
22
+
23
+ To create a new setting just set its value:
24
+
25
+ Setting[:site_title] = "My Awesome Site!"
26
+
27
+ Non-qualified key operations use the `:default` namespace, to set a key
28
+ in a particular namespace:
29
+
30
+ Setting[:myplugin, :site_title] = "My Awesome Plugin Site!"
31
+
32
+ To set application defaults, use `SettingsDB::Defaults`:
33
+
34
+ SettingsDB::Defaults[:site_title] = 'Untitled'
35
+ SettingsDB::Defaults[:myplugin, :site_title] = 'Untitled Plugin'
36
+
37
+ Or use a block:
38
+
39
+ SettingsDB::Defaults.config do |c|
40
+ c[:site_title] = 'Untitled'
41
+ c[:myplugin, :site_title] = 'Untitled Plugin'
42
+ end
43
+
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'SettingsDB-Rails'
18
+ rdoc.main = 'README.rdoc'
19
+ rdoc.options << '--line-numbers'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ rdoc.rdoc_files.exclude('lib/settingsdb-rails.rb', 'lib/settingsdb/version.rb')
23
+ rdoc.rdoc_files.exclude('lib/generators/**/*')
24
+ end
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate settingsdb Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,103 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record/migration'
4
+ require 'active_record'
5
+ require 'settingsdb/generators/generated_attribute'
6
+
7
+ module SettingsDB::Generators # :nodoc: all
8
+ class InstallGenerator < Rails::Generators::NamedBase
9
+ include Rails::Generators::Migration
10
+ extend ActiveRecord::Generators::Migration
11
+
12
+ namespace "settingsdb:install"
13
+ argument :name, :type => :string, :default => 'settings'
14
+ argument :attributes, :type => :array, :default => [], :banner => "field:type[[:index][:default]] ..."
15
+ source_root File.expand_path('../templates', __FILE__)
16
+
17
+
18
+ desc "Creates a SettingsDB initializer, Model, and Migration"
19
+ class_option :model, :desc => 'Generate the model', :type => :boolean, :default => true
20
+ class_option :migration, :desc => 'Generate the migration', :type => :boolean, :default => true
21
+
22
+ def add_settingsdb_model
23
+ return if !options.model? || model_exists?
24
+ invoke 'active_record:model', [name.singularize], :migration => false
25
+ end
26
+
27
+ def inject_settingsdb_content
28
+ return if !options.model?
29
+ inject_into_class(model_path, model_class, " acts_as_setting\n")
30
+ end
31
+
32
+ def add_settingsdb_migration
33
+ return if !options.migration?
34
+ if migration_exists?
35
+ migration_template 'migration_exists.rb', "db/migrate/settingsdb_add_settings_columns_to_#{table_name}"
36
+ else
37
+ migration_template 'migration.rb', "db/migrate/settingsdb_create_#{table_name}"
38
+ end
39
+ end
40
+
41
+ def add_settingsdb_initializer
42
+ return if initializer_exists?
43
+ template 'initializer.rb', 'config/initializers/settingsdb.rb'
44
+ end
45
+
46
+ protected
47
+ # override Rails::Generators::NamedBase#parse_attributes! to customize attribute parsing
48
+ def parse_attributes!
49
+ self.attributes = (attributes || []).map do |key_value|
50
+ name, type, index, default = key_value.split(/:/)
51
+ opts = {}
52
+ if default
53
+ opts[:default] = default
54
+ end
55
+ if index
56
+ index_type, constraint = index.split(/,/)
57
+ if constraint == 'not_null'
58
+ opts[:null] = false
59
+ end
60
+ end
61
+ create_attribute(name, type, index_type, opts)
62
+ end
63
+ end
64
+
65
+ def create_attribute(*args)
66
+ if Rails.version[0,3].to_f >= 3.2
67
+ Rails::Generators::GeneratedAttribute.new(*args)
68
+ else
69
+ SettingsDB::Generators::GeneratedAttribute.new(*args)
70
+ end
71
+ end
72
+
73
+ # Helper methods shamelessly copied from Devise
74
+ def file_name
75
+ name.underscore
76
+ end
77
+
78
+ def model_class
79
+ class_name.singularize
80
+ end
81
+
82
+ def model_path
83
+ @model_path ||= File.join('app', 'models', class_path, "#{file_name.singularize}.rb")
84
+ end
85
+
86
+ def migration_path
87
+ @migration_path ||= File.join("db", "migrate")
88
+ end
89
+
90
+ def model_exists?
91
+ File.exists?(File.join(destination_root, model_path))
92
+ end
93
+
94
+ def migration_exists?
95
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/settingsdb_create_#{table_name}.rb$/).first
96
+ end
97
+
98
+ def initializer_exists?
99
+ File.exists?(File.join(destination_root, File.join(%w(config initializers settingsdb.rb))))
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ SettingsDB::Defaults.config do |config|
2
+ #config[:setting] = :value
3
+ end
@@ -0,0 +1,18 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ t.string :namespace, :null => false, :default => 'default'
5
+ t.string :name, :null => false
6
+ t.text :value
7
+ <% attributes.each do |attr| -%>
8
+ t.<%= attr.type %> :<%= attr.name %><%= attr.inject_options %>
9
+ <% end -%>
10
+ end
11
+
12
+ add_index :<%= table_name %>, [:namespace, :name], :unique => true
13
+ add_index :<%= table_name %>, :name
14
+ <% attributes.reject {|attr| !attr.has_index?}.each do |attr| -%>
15
+ add_index :<%= table_name %>, :<%= attr.name %><%= attr.inject_index_options %>
16
+ <% end -%>
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ add_column :<%= table_name %>, :namespace, :string, :null => false, :default => 'default'
4
+ add_column :<%= table_name %>, :name, :string, :null => false
5
+ add_column :<%= table_name %>, :value, :text
6
+
7
+ add_index :<%= table_name %>, [:namespace, :name], :unique => true
8
+ add_index :<%= table_name %>, :name
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module SettingsDB # :nodoc:
2
+ end
3
+
4
+ require 'settingsdb/defaults'
5
+ require 'settingsdb/settings'
6
+ require 'settingsdb/acts_as_setting'
@@ -0,0 +1,48 @@
1
+ module SettingsDB::ActsAsSetting # :nodoc:
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ end
6
+
7
+ module ClassMethods
8
+ ##
9
+ # This method causes the model to import the SettingsDB behavior
10
+ # A SettingsDB enabled modle requires 3 fields: name, namespace, and value
11
+ # This method also takes options to override the default names of these fields.
12
+ # === Options
13
+ # *:setting_namespace_field*:: Override namespace field-name (default: +:namespace+)
14
+ # *:setting_name_field*:: Override name field-name (default: +:name+)
15
+ # *:setting_value_field*:: Override value field-name (default: +:value+)
16
+ #
17
+ # === Examples
18
+ #
19
+ # To use the default field names in your model, no options are needed:
20
+ #
21
+ # class Setting < ActiveRecord::Base
22
+ # acts_as_setting
23
+ # end
24
+ #
25
+ # If your model needs to rename a setting field's name pass them as options
26
+ # to +acts_as_settings+. Here the +Configuration+ model keeps the namespace
27
+ # value in the +:scope+ field, and the setting name value in the +:key+ field:
28
+ #
29
+ # class Configuration < ActiveRecord::Base
30
+ # acts_as_setting :setting_namespace_field => :scope, :setting_name_field => :key
31
+ # end
32
+ #
33
+ def acts_as_setting(options = {})
34
+ cattr_accessor :setting_namespace_field, :setting_name_field, :setting_value_field
35
+ self.setting_name_field = (options[:setting_name_field] || :name).to_sym
36
+ self.setting_namespace_field = (options[:setting_namespace_field] || :namespace).to_sym
37
+ self.setting_value_field = (options[:setting_value_field] || :value).to_sym
38
+ class_eval(<<-BLOCK, __FILE__, __LINE__ + 1)
39
+ include SettingsDB::Settings
40
+ serialize :#{setting_value_field}
41
+ before_destroy :remove_from_cache
42
+
43
+ BLOCK
44
+ end
45
+ end
46
+ end
47
+
48
+ ActiveRecord::Base.send :include, SettingsDB::ActsAsSetting
@@ -0,0 +1,91 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ class SettingsDB::Defaults
3
+
4
+ ##
5
+ # :call-seq:
6
+ # Defaults[:key] -> value (from :default namespace)
7
+ # Defaults[:p1, :key] -> value (from :p1 namespace)
8
+ #
9
+ # Returns the value for the index. If namespace is given
10
+ # returns the value for the index in that namespace.
11
+ # When no namespace is given, the :default namespace is used.
12
+ def self.[](namespace = :default, index)
13
+ @@defaults[namespace][index] if @@defaults[namespace]
14
+ end
15
+
16
+ ##
17
+ # :call-seq:
18
+ # Defaults[:key] = value -> 'value' (in :default namespace)
19
+ # Defaults[:p1, :key] = value -> 'value' (in :p1 namespace)
20
+ #
21
+ # Sets the value for the index.
22
+ # If a namespace is given then the index is set in that namespace
23
+ # else it is set in the default namespace.
24
+ def self.[]=(namespace = :default, index, value)
25
+ if @@defaults[namespace] && @@defaults[namespace].has_key?(index)
26
+ @@defaults[namespace][index] = value
27
+ else
28
+ @@defaults[namespace] = { index => value }
29
+ value
30
+ end
31
+ end
32
+
33
+ ##
34
+ # :call-seq:
35
+ # Defaults.config -> hash
36
+ # Defaults.config {|config| ...}
37
+ #
38
+ # If given a block yields self. If no block is given
39
+ # the hash of default values is returned.
40
+ # === Example
41
+ # Defaults.config do |conf|
42
+ # conf[:key1] = value1
43
+ # conf[:ns1, :key1] = value2
44
+ # end
45
+ #
46
+ # If no block is given the default hash is returned and
47
+ # can be manipulated directly:
48
+ #
49
+ # default_hash = Defaults.config
50
+ # default_hash[:default][:key1] = value1
51
+ # default_hash[:ns1] = { :key1 => value2 }
52
+ def self.config # :yields: config
53
+ if block_given?
54
+ yield self
55
+ end
56
+ self
57
+ end
58
+
59
+ ##
60
+ # :call-seq:
61
+ # reset! -> Defaults
62
+ #
63
+ # Deletes all the default settings.
64
+ def self.reset!
65
+ @@defaults = { :default => {} }.with_indifferent_access
66
+ self
67
+ end
68
+
69
+ ##
70
+ # :call-seq:
71
+ # defaults! -> hash
72
+ #
73
+ # Returns the actual reference to the internal defaults hash.
74
+ # USE THIS WITH CARE!
75
+ def self.defaults!
76
+ @@defaults
77
+ end
78
+
79
+ ##
80
+ # :call-seq:
81
+ # defaults -> hash
82
+ #
83
+ # Returns a deep copy of the defaults hash, modifying this will
84
+ # have no impact on Defaults[] results.
85
+ def self.defaults
86
+ # Do a deep copy
87
+ Marshal.load(Marshal.dump(@@defaults))
88
+ end
89
+
90
+ reset!
91
+ end
@@ -0,0 +1,104 @@
1
+ # This was copied from rails 3.2 to extend the support to earlier rails versions
2
+ require 'active_support/time'
3
+ require 'active_support/core_ext/object/inclusion'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ module SettingsDB::Generators # :nodoc: all
7
+ class GeneratedAttribute
8
+ attr_accessor :name, :type
9
+ attr_reader :attr_options
10
+
11
+ class << self
12
+ def parse(column_definition)
13
+ name, type, has_index = column_definition.split(':')
14
+
15
+ # if user provided "name:index" instead of "name:string:index"
16
+ # type should be set blank so GeneratedAttribute's constructor
17
+ # could set it to :string
18
+ has_index, type = type, nil if %w(index uniq).include?(type)
19
+
20
+ type, attr_options = *parse_type_and_options(type)
21
+ new(name, type, has_index, attr_options)
22
+ end
23
+
24
+ private
25
+
26
+ # parse possible attribute options like :limit for string/text/binary/integer or :precision/:scale for decimals
27
+ # when declaring options curly brackets should be used
28
+ def parse_type_and_options(type)
29
+ case type
30
+ when /(string|text|binary|integer){(\d+)}/
31
+ return $1, :limit => $2.to_i
32
+ when /decimal{(\d+),(\d+)}/
33
+ return :decimal, :precision => $1.to_i, :scale => $2.to_i
34
+ else
35
+ return type, {}
36
+ end
37
+ end
38
+ end
39
+
40
+ def initialize(name, type=nil, index_type=false, attr_options={})
41
+ @name = name
42
+ @type = (type.presence || :string).to_sym
43
+ @has_index = %w(index uniq).include?(index_type)
44
+ @has_uniq_index = %w(uniq).include?(index_type)
45
+ @attr_options = attr_options
46
+ end
47
+
48
+ def field_type
49
+ @field_type ||= case type
50
+ when :integer then :number_field
51
+ when :float, :decimal then :text_field
52
+ when :time then :time_select
53
+ when :datetime, :timestamp then :datetime_select
54
+ when :date then :date_select
55
+ when :text then :text_area
56
+ when :boolean then :check_box
57
+ else :text_field
58
+ end
59
+ end
60
+
61
+ def default
62
+ @default ||= case type
63
+ when :integer then 1
64
+ when :float then 1.5
65
+ when :decimal then "9.99"
66
+ when :datetime, :timestamp, :time then Time.now.to_s(:db)
67
+ when :date then Date.today.to_s(:db)
68
+ when :string then name == "type" ? "" : "MyString"
69
+ when :text then "MyText"
70
+ when :boolean then false
71
+ when :references, :belongs_to then nil
72
+ else ""
73
+ end
74
+ end
75
+
76
+ def human_name
77
+ name.to_s.humanize
78
+ end
79
+
80
+ def index_name
81
+ reference? ? "#{name}_id" : name
82
+ end
83
+
84
+ def reference?
85
+ self.type.in?([:references, :belongs_to])
86
+ end
87
+
88
+ def has_index?
89
+ @has_index
90
+ end
91
+
92
+ def has_uniq_index?
93
+ @has_uniq_index
94
+ end
95
+
96
+ def inject_options
97
+ @attr_options.blank? ? '' : ", #{@attr_options.to_s.gsub(/[{}]/, '')}"
98
+ end
99
+
100
+ def inject_index_options
101
+ has_uniq_index? ? ", :unique => true" : ''
102
+ end
103
+ end
104
+ end