setting_accessors 0.0.1

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +254 -0
  8. data/Rakefile +2 -0
  9. data/lib/generators/setting_accessors/install_generator.rb +38 -0
  10. data/lib/generators/setting_accessors/templates/migration.rb.erb +12 -0
  11. data/lib/generators/setting_accessors/templates/model.rb.erb +47 -0
  12. data/lib/generators/setting_accessors/templates/settings.yml +24 -0
  13. data/lib/setting_accessors.rb +26 -0
  14. data/lib/setting_accessors/accessor.rb +148 -0
  15. data/lib/setting_accessors/converter.rb +67 -0
  16. data/lib/setting_accessors/integration.rb +79 -0
  17. data/lib/setting_accessors/integration_validator.rb +15 -0
  18. data/lib/setting_accessors/internal.rb +108 -0
  19. data/lib/setting_accessors/setting_scaffold.rb +252 -0
  20. data/lib/setting_accessors/validator.rb +144 -0
  21. data/lib/setting_accessors/version.rb +3 -0
  22. data/lib/tasks/setting_accessors_tasks.rake +4 -0
  23. data/setting_accessors.gemspec +30 -0
  24. data/test/dummy/README.rdoc +28 -0
  25. data/test/dummy/Rakefile +6 -0
  26. data/test/dummy/app/assets/images/.keep +0 -0
  27. data/test/dummy/app/assets/javascripts/application.js +13 -0
  28. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  29. data/test/dummy/app/controllers/application_controller.rb +5 -0
  30. data/test/dummy/app/controllers/concerns/.keep +0 -0
  31. data/test/dummy/app/helpers/application_helper.rb +2 -0
  32. data/test/dummy/app/mailers/.keep +0 -0
  33. data/test/dummy/app/models/.keep +0 -0
  34. data/test/dummy/app/models/concerns/.keep +0 -0
  35. data/test/dummy/app/models/setting.rb +59 -0
  36. data/test/dummy/app/models/user.rb +15 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/bin/bundle +3 -0
  39. data/test/dummy/bin/rails +4 -0
  40. data/test/dummy/bin/rake +4 -0
  41. data/test/dummy/config.ru +4 -0
  42. data/test/dummy/config/application.rb +25 -0
  43. data/test/dummy/config/boot.rb +5 -0
  44. data/test/dummy/config/database.yml +25 -0
  45. data/test/dummy/config/environment.rb +5 -0
  46. data/test/dummy/config/environments/development.rb +37 -0
  47. data/test/dummy/config/environments/production.rb +83 -0
  48. data/test/dummy/config/environments/test.rb +34 -0
  49. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  51. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/test/dummy/config/initializers/inflections.rb +16 -0
  53. data/test/dummy/config/initializers/mime_types.rb +4 -0
  54. data/test/dummy/config/initializers/session_store.rb +3 -0
  55. data/test/dummy/config/initializers/setting_accessors.rb +1 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +23 -0
  58. data/test/dummy/config/routes.rb +56 -0
  59. data/test/dummy/config/secrets.yml +22 -0
  60. data/test/dummy/config/settings.yml +23 -0
  61. data/test/dummy/db/migrate/20150102112106_create_users.rb +9 -0
  62. data/test/dummy/db/migrate/20150102115329_create_settings.rb +12 -0
  63. data/test/dummy/db/schema.rb +32 -0
  64. data/test/dummy/db/test.sqlite3 +0 -0
  65. data/test/dummy/lib/assets/.keep +0 -0
  66. data/test/dummy/log/.keep +0 -0
  67. data/test/dummy/public/404.html +67 -0
  68. data/test/dummy/public/422.html +67 -0
  69. data/test/dummy/public/500.html +66 -0
  70. data/test/dummy/public/favicon.ico +0 -0
  71. data/test/dummy/test/models/setting_test.rb +131 -0
  72. data/test/dummy/test/models/user_test.rb +5 -0
  73. data/test/generators/install_generator_test.rb +15 -0
  74. data/test/setting_accessors_test.rb +4 -0
  75. data/test/test_helper.rb +23 -0
  76. metadata +270 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5556d06d931218cf2828932793eec26f482bcbf1
4
+ data.tar.gz: 8b6a4a5541cf10d19544331c988498540c92d16d
5
+ SHA512:
6
+ metadata.gz: 70cd7230d3b760a246697282b44d0f132aa9fe5cb92e2a26eaae37c24dfb28bbdc29d647a36e85190e9287d3ff90bf0bd9aad6d9ec860bfe8f43b194a69f5a47
7
+ data.tar.gz: 89cc704ede8fc29558a4443be76ab2b80d74f7b9c5419003c2fe2d800036b92e78302e40b02a5b830e72eca63a7b84da88929e5dc11128ff0dbe88fafa397f8c
data/.gitignore ADDED
@@ -0,0 +1,29 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+
24
+ #Rubymine
25
+ .idea
26
+
27
+ #Dummy Application
28
+ test/dummy/log
29
+ test/dummy/db/development.sqlite3
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ setting_accessors
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in setting_accessors.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Stefan Exner
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,254 @@
1
+ SettingAccessors
2
+ ================
3
+
4
+ Sometimes it's handy to keep track of various settings or attributes in ActiveRecord instances and persist them through various requests (and sessions). An example would be to store a items-per-page value for multiple tables in the application per user or probably also set default sorting directions.
5
+
6
+ The only problem is, that it would be necessary to either keep hundreds of columns in the corresponding table or create a serialized attribute which would grow over time and generally be hard to manage.
7
+
8
+ This gem consists of a global key-value-store, allowing to use both global and record bound settings
9
+ and ActiveRecord integration to add virtual columns to models without having to change the database layout.
10
+
11
+ Installation
12
+ ------------
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'setting_accessors'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```
29
+ $ gem install setting_accessors
30
+ ```
31
+
32
+ The gem requires a few additional files to work properly:
33
+
34
+ - A migration to create the settings table
35
+ - A setting model
36
+ - An initializer
37
+ - A settings config file (config/settings.yml)
38
+
39
+ All of them can be generated using the provided generator:
40
+
41
+ ```
42
+ $ rails g setting_accessors:install MODEL_NAME
43
+ ```
44
+
45
+ If no model name is given, the default one (`Setting`) is used.
46
+
47
+ Usage as a global key-value-store (globally defined and anonymous settings)
48
+ -----
49
+
50
+ In the following, the model name will be assumed as `Setting`, though you may choose its name freely using the provided generator (see above).
51
+
52
+ Settings can either be global or assigned to an instance of ActiveRecord::Base. They consist of a name which is unique either in the global scope or within the instance and a value which can be everything that's serializable through YAML.
53
+
54
+ The easiest way to use the settings model is as a global key-value-store without validations and typecasting.
55
+
56
+ Values can be easily set and retrieved by using their names as class method names on `Setting` itself:
57
+
58
+ ```ruby
59
+ Setting.the_meaning_of_life = 42
60
+ Setting.the_meaning_of_life
61
+ #=> 42
62
+ ```
63
+
64
+ If the name contains characters which are not allowed due to ruby naming rules, you can also use the methods`Setting[KEY]` resp. `Setting.get(KEY)` to get a setting's value and `Setting[KEY] = VALUE` resp.`Setting.create_or_update(KEY, VALUE)`.
65
+
66
+ Therefore, the following getter methods are equivalent:
67
+
68
+ ```ruby
69
+ Setting.meaning_of_life
70
+ Setting[:meaning_of_life]
71
+ Setting.get(:meaning_of_life)
72
+ ```
73
+
74
+ For the corresponding setters:
75
+
76
+ ```ruby
77
+ Setting.meaning_of_life = 42
78
+ Setting[:meaning_of_life] = 42
79
+ Setting.create_or_update(:meaning_of_life, 42)
80
+ ```
81
+
82
+ ### Globally defined settings with types and validations
83
+
84
+ As stated above, the initializer will generate a file called `settings.yml` in your application's `config` directory. This file is used to define global settings, an entry consists of:
85
+
86
+ - The setting's name
87
+ - The setting's type (optional)
88
+ - Validations to be performed when the setting is saved (optional)
89
+ - A default value (optional)
90
+
91
+ An example would be a simple string setting:
92
+
93
+ ```yaml
94
+ a_string:
95
+ type: string
96
+ default: "I am a string!"
97
+ validations:
98
+ presence: true
99
+ ```
100
+
101
+ If a setting is defined with a type, automatic type conversion will happen, similar to what ActiveRecord does:
102
+
103
+ ```ruby
104
+ Setting.a_string = 42
105
+ #=> "42"
106
+ ```
107
+
108
+ The default value is used by the functions `Setting.get_or_default` and `Setting.create_default_setting` and a fallback option for assigned settings (see below).
109
+
110
+ The built-in validations currently support, this might be extended in the future.
111
+
112
+ | Base Validation | Options |
113
+ |:----------------|:---------------------------|
114
+ | `presence` | `allow_nil`, `allow_blank` |
115
+ | `numericality` | `only_integer` |
116
+ | `boolean` |   |
117
+
118
+
119
+ ### Assigned Records
120
+
121
+ Both globally defined settings and "anonymous" settings can also be assigned to
122
+ a instances of `ActiveRecord::Base` without having to define them in the model first.
123
+
124
+ An example would be the above mentioned saving of "items per page" values for
125
+ various views: Let's say we have an events view and want to save how many
126
+ rows/items each user would like to display.
127
+
128
+ As there might be many views requiring this functionality, it wouldn't make
129
+ sense to define global settings for each of them. Instead, we would use anonymous
130
+ settings with a generated key based on the current view:
131
+
132
+ ```ruby
133
+ def items_per_page
134
+ key = [controller_path, action_name].join('_')
135
+ default_value = 30
136
+ Setting.get(key, current_user) || default_value
137
+ end
138
+ ```
139
+
140
+ ActiveRecord Integration
141
+ ------------------------
142
+
143
+ The gem adds the method `setting_accessor` to each class which inherits from `ActiveRecord::Base`.
144
+
145
+ `setting_accessor` takes a setting name and a set of options to customize the accessor's behaviour:
146
+
147
+ ```ruby
148
+ class MyModel < ActiveRecord::Base
149
+ setting_accessor :a_string, :fallback => :default
150
+ end
151
+ ```
152
+
153
+ This automatically adds most of the helper methods to the model instances which are available for database columns:
154
+
155
+ ```ruby
156
+ my_model = MyModel.new
157
+
158
+ #Setter
159
+ my_model.a_string = 1234
160
+
161
+ #Getter
162
+ my_model.a_string
163
+ #=> "1234"
164
+
165
+ #Value before type cast
166
+ my_model.a_string_before_type_cast
167
+ #=> 1234
168
+
169
+ #Old value
170
+ my_model.a_string_was
171
+ #=> "I am a string!" (fallback value, see below)
172
+
173
+ #Check if the value was changed
174
+ my_model.a_string_changed?
175
+ #=> true
176
+ ```
177
+
178
+ The setting records are only persisted if all settings *and* the main record were valid.
179
+
180
+ ### Integration of globally defined settings
181
+
182
+ If a setting with the given name is defined in `config/settings.yml`, validations and type settings are automatically fetched from it, in this case, only the `:fallback` option is allowed.
183
+
184
+ As of now, this means that custom validations are also not supported for globally defined settings, this may be changed in the future.
185
+
186
+ ### Class-wise definition of settings
187
+
188
+ If a setting is defined using `setting_accessor` which is not part of `config/settings.yml`, it will only be available in this class. It however may be defined in multiple classes independently.
189
+
190
+ Defining a class setting accepts the same options as the config file:
191
+
192
+ ```ruby
193
+ class MyModel < ActiveRecord::Base
194
+ setting_accessor :my_setting, :type => :boolean, :default => false
195
+ end
196
+ ```
197
+
198
+ ### Options
199
+
200
+ ```ruby
201
+ :type => :string | :integer | :boolean | :polymorphic
202
+ ```
203
+
204
+ `:type` defines the setting's data type. If no type is given, `:polymorhpic`
205
+ is used automatically.
206
+
207
+ For every other type, the setting's value is automatically converted accordingly
208
+ upon setting it, mimicking ActiveRecord's behaviour regarding database columns.
209
+ Please note that `:polymorphic` values are saved as is, meaning that you can
210
+ store everything that's serializable.
211
+
212
+ More types will be added in the future, most likely all database types
213
+ ActiveRecord can handle as well.
214
+
215
+ ```ruby
216
+ :default => "I am a string!"
217
+ ```
218
+
219
+ `:default` sets the setting's default value. It can be retrieved either by
220
+ calling `Setting.get_or_default(...)` or defining a `:fallback` on a class setting.
221
+
222
+ ```ruby
223
+ :fallback => :global | :default | Object
224
+ ```
225
+
226
+ The `:fallback` option specifies which value should be returned in case the
227
+ setting did not receive a value yet:
228
+
229
+ - `:global` will try to retrieve a global setting (`Setting.NAME`) with the same
230
+ name as the accessor and return `nil` if it isn't defined.
231
+ - `:default` will return the default value set up either in the accessor method
232
+ or `config/settings.yml` for globally defined settings.
233
+ - Every other value will be returned as is
234
+
235
+
236
+ ```ruby
237
+ :validations => {:presence => true}
238
+ :validations => {:numericality => {:only_integer => true}}
239
+ :validations => {:custom => [lambda {|setting| setting.errors.add(:not_42) if setting.value != 42}]}
240
+ :validations => {:custom => [:must_be_42]}
241
+ ```
242
+
243
+ There are some built-in validations (see above), but you may also define custom
244
+ validations either by passing in anonymous functions or symbols representing
245
+ class methods either in the setting class or the assigned model.
246
+
247
+ Contributing
248
+ ------------
249
+
250
+ 1. Fork it ( https://github.com/stex/setting_accessors/fork )
251
+ 2. Create your feature branch (`git checkout -b my-new-feature`\)
252
+ 3. Commit your changes (`git commit -am 'Add some feature'`\)
253
+ 4. Push to the branch (`git push origin my-new-feature`\)
254
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,38 @@
1
+ module SettingAccessors
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ argument :model_name, :type => :string, :default => 'Setting'
9
+
10
+ def self.next_migration_number(path)
11
+ if @prev_migration_nr
12
+ @prev_migration_nr += 1
13
+ else
14
+ @prev_migration_nr = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
15
+ end
16
+ @prev_migration_nr.to_s
17
+ end
18
+
19
+ desc 'Installs everything necessary'
20
+ def create_install
21
+ template 'settings.yml', 'config/settings.yml'
22
+ template 'model.rb.erb', "app/models/#{model_name.classify.underscore}.rb"
23
+ migration_template 'migration.rb.erb', "db/migrate/create_#{model_name.classify.underscore.pluralize}.rb"
24
+
25
+ initializer 'setting_accessors.rb', <<INIT
26
+ SettingAccessors.configuration do |config|
27
+
28
+ #The model your application is using for settings.
29
+ #If you created it using the SettingAccessors generator, the
30
+ #model name below should already be correct.
31
+ config.setting_class = #{model_name}
32
+
33
+ end
34
+ INIT
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ class Create<%= model_name.camelize.pluralize %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= model_name.underscore.pluralize %> do |t|
4
+ t.belongs_to :assignable, :polymorphic => true
5
+
6
+ t.string :name
7
+ t.text :value
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,47 @@
1
+ #
2
+ # This model handles the management of system wide or record specific settings
3
+ #
4
+ # @attr [String] name
5
+ # The setting's name
6
+ #
7
+ # @attr [Object] value
8
+ # The setting's value. May be anything that can be serialized through YAML
9
+ #
10
+ # You can access global settings just like a normal class method,
11
+ # please have a look at #method_missing for more information.
12
+ #
13
+ # If not absolutely necessary, please **do not** create settings yourself
14
+ # through Setting.new, instead use #create_or_update instead.
15
+ #
16
+ # There are also some usage examples in the corresponding test.
17
+ #
18
+
19
+ class <%= model_name.camelize %> < ActiveRecord::Base
20
+ belongs_to :assignable, :polymorphic => true
21
+
22
+ include SettingAccessors::SettingScaffold
23
+
24
+ #
25
+ # Makes accessing settings a little easier.
26
+ # Examples:
27
+ #
28
+ # #Loading **the value** of a global setting named "my_setting"
29
+ # Setting.my_setting
30
+ #
31
+ # #Setting **the value** of a global setting named "my_setting"
32
+ # Setting.my_setting = [1,2,3,4,5]
33
+ #
34
+ # #Loading **the value** of an assigned setting named "cool_setting"
35
+ # #+some_cool_user+ is here an instance of ActiveRecord::Base
36
+ # Setting.cool_setting(some_cool_user)
37
+ #
38
+ def self.method_missing(method, *args)
39
+ super(method, *args) if args.size > 1
40
+ method_name = method.to_s
41
+ if method_name.last == '='
42
+ self.create_or_update(method_name[0..method_name.length-2], args.first, nil, true)
43
+ else
44
+ self.get(method_name, args.first)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # This file specifies all settings used in the application
3
+ # (be it record specific or global).
4
+ #
5
+ # The keys are the setting names, the values are a hash
6
+ # containing validation options, type and default value
7
+ #
8
+ # Examples:
9
+ #
10
+ # a_string:
11
+ # type: string
12
+ # default: "I am a string!"
13
+ # validations:
14
+ # presence: true
15
+ #
16
+ # a_number:
17
+ # type: integer
18
+ # default: "I am a Number!"
19
+ #
20
+ # a_boolean:
21
+ # type: boolean
22
+ # default: true
23
+ # validations:
24
+ # boolean: true