setting_accessors 0.3.0 → 1.0.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +10 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +131 -0
  5. data/.travis.yml +11 -7
  6. data/Appraisals +17 -0
  7. data/CHANGELOG.md +38 -1
  8. data/Gemfile +2 -0
  9. data/README.md +64 -124
  10. data/Rakefile +5 -27
  11. data/bin/console +15 -0
  12. data/bin/setup +10 -0
  13. data/gemfiles/rails_4.1.gemfile +7 -0
  14. data/gemfiles/rails_4.2.gemfile +7 -0
  15. data/gemfiles/rails_4.2.gemfile.lock +162 -0
  16. data/gemfiles/rails_5.0.gemfile +7 -0
  17. data/gemfiles/rails_5.0.gemfile.lock +169 -0
  18. data/gemfiles/rails_5.1.gemfile +7 -0
  19. data/gemfiles/rails_5.1.gemfile.lock +169 -0
  20. data/gemfiles/rails_5.2.gemfile +7 -0
  21. data/gemfiles/rails_5.2.gemfile.lock +177 -0
  22. data/lib/generators/setting_accessors/install_generator.rb +11 -9
  23. data/lib/generators/setting_accessors/templates/model.rb.erb +0 -24
  24. data/lib/setting_accessors.rb +14 -5
  25. data/lib/setting_accessors/accessor_generator.rb +66 -0
  26. data/lib/setting_accessors/converters/base.rb +24 -0
  27. data/lib/setting_accessors/converters/boolean_converter.rb +51 -0
  28. data/lib/setting_accessors/converters/integer_converter.rb +21 -0
  29. data/lib/setting_accessors/converters/polymorphic_converter.rb +11 -0
  30. data/lib/setting_accessors/converters/string_converter.rb +11 -0
  31. data/lib/setting_accessors/helpers.rb +28 -0
  32. data/lib/setting_accessors/integration.rb +83 -97
  33. data/lib/setting_accessors/internal.rb +37 -64
  34. data/lib/setting_accessors/setting_scaffold.rb +147 -214
  35. data/lib/setting_accessors/setting_set.rb +168 -0
  36. data/lib/setting_accessors/version.rb +3 -1
  37. data/lib/tasks/setting_accessors_tasks.rake +2 -0
  38. data/setting_accessors.gemspec +27 -19
  39. metadata +117 -143
  40. data/.codeclimate.yml +0 -66
  41. data/lib/setting_accessors/accessor.rb +0 -189
  42. data/lib/setting_accessors/converter.rb +0 -71
  43. data/lib/setting_accessors/integration_validator.rb +0 -15
  44. data/lib/setting_accessors/validator.rb +0 -144
  45. data/test/dummy/README.rdoc +0 -28
  46. data/test/dummy/Rakefile +0 -6
  47. data/test/dummy/app/assets/images/.keep +0 -0
  48. data/test/dummy/app/assets/javascripts/application.js +0 -13
  49. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  50. data/test/dummy/app/controllers/application_controller.rb +0 -5
  51. data/test/dummy/app/controllers/concerns/.keep +0 -0
  52. data/test/dummy/app/helpers/application_helper.rb +0 -2
  53. data/test/dummy/app/mailers/.keep +0 -0
  54. data/test/dummy/app/models/.keep +0 -0
  55. data/test/dummy/app/models/concerns/.keep +0 -0
  56. data/test/dummy/app/models/post.rb +0 -2
  57. data/test/dummy/app/models/setting.rb +0 -59
  58. data/test/dummy/app/models/user.rb +0 -19
  59. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  60. data/test/dummy/bin/bundle +0 -3
  61. data/test/dummy/bin/rails +0 -4
  62. data/test/dummy/bin/rake +0 -4
  63. data/test/dummy/config.ru +0 -4
  64. data/test/dummy/config/application.rb +0 -25
  65. data/test/dummy/config/boot.rb +0 -5
  66. data/test/dummy/config/database.yml +0 -25
  67. data/test/dummy/config/environment.rb +0 -5
  68. data/test/dummy/config/environments/development.rb +0 -37
  69. data/test/dummy/config/environments/production.rb +0 -83
  70. data/test/dummy/config/environments/test.rb +0 -34
  71. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  72. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  73. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  74. data/test/dummy/config/initializers/inflections.rb +0 -16
  75. data/test/dummy/config/initializers/mime_types.rb +0 -4
  76. data/test/dummy/config/initializers/session_store.rb +0 -3
  77. data/test/dummy/config/initializers/setting_accessors.rb +0 -1
  78. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  79. data/test/dummy/config/locales/en.yml +0 -23
  80. data/test/dummy/config/routes.rb +0 -56
  81. data/test/dummy/config/secrets.yml +0 -22
  82. data/test/dummy/config/settings.yml +0 -23
  83. data/test/dummy/db/migrate/20150102112106_create_users.rb +0 -9
  84. data/test/dummy/db/migrate/20150102115329_create_settings.rb +0 -12
  85. data/test/dummy/db/migrate/20150723114600_create_posts.rb +0 -11
  86. data/test/dummy/db/schema.rb +0 -40
  87. data/test/dummy/db/test.sqlite3 +0 -0
  88. data/test/dummy/lib/assets/.keep +0 -0
  89. data/test/dummy/public/404.html +0 -67
  90. data/test/dummy/public/422.html +0 -67
  91. data/test/dummy/public/500.html +0 -66
  92. data/test/dummy/public/favicon.ico +0 -0
  93. data/test/dummy/test/fixtures/posts.yml +0 -11
  94. data/test/dummy/test/models/post_test.rb +0 -19
  95. data/test/dummy/test/models/setting_test.rb +0 -143
  96. data/test/dummy/test/models/user_test.rb +0 -154
  97. data/test/generators/install_generator_test.rb +0 -15
  98. data/test/setting_accessors_test.rb +0 -4
  99. data/test/test_helper.rb +0 -31
data/.codeclimate.yml DELETED
@@ -1,66 +0,0 @@
1
- ---
2
- engines:
3
- rubocop:
4
- enabled: true
5
- eslint:
6
- enabled: true
7
- csslint:
8
- enabled: true
9
- ratings:
10
- paths:
11
- - "**.rb"
12
- - "**.js"
13
- - "**.jsx"
14
- - "**.css"
15
- exclude_paths:
16
- - test/**/*
17
- # This is a sample .codeclimate.yml configured for Engine analysis on Code
18
- # Climate Platform. For an overview of the Code Climate Platform, see here:
19
- # http://docs.codeclimate.com/article/300-the-codeclimate-platform
20
-
21
- # Under the engines key, you can configure which engines will analyze your repo.
22
- # Each key is an engine name. For each value, you need to specify enabled: true
23
- # to enable the engine as well as any other engines-specific configuration.
24
-
25
- # For more details, see here:
26
- # http://docs.codeclimate.com/article/289-configuring-your-repository-via-codeclimate-yml#platform
27
-
28
- # For a list of all available engines, see here:
29
- # http://docs.codeclimate.com/article/296-engines-available-engines
30
-
31
- engines:
32
- # to turn on an engine, add it here and set enabled to `true`
33
- # to turn off an engine, set enabled to `false` or remove it
34
- rubocop:
35
- enabled: true
36
- golint:
37
- enabled: true
38
- gofmt:
39
- enabled: true
40
- eslint:
41
- enabled: true
42
- csslint:
43
- enabled: true
44
-
45
- # Engines can analyze files and report issues on them, but you can separately
46
- # decide which files will receive ratings based on those issues. This is
47
- # specified by path patterns under the ratings key.
48
-
49
- # For more details see here:
50
- # http://docs.codeclimate.com/article/289-configuring-your-repository-via-codeclimate-yml#platform
51
-
52
- # Note: If the ratings key is not specified, this will result in a 0.0 GPA on your dashboard.
53
-
54
- # ratings:
55
- # paths:
56
- # - app/**
57
- # - lib/**
58
- # - "**.rb"
59
- # - "**.go"
60
-
61
- # You can globally exclude files from being analyzed by any engine using the
62
- # exclude_paths key.
63
-
64
- #exclude_paths:
65
- #- spec/**/*
66
- #- vendor/**/*
@@ -1,189 +0,0 @@
1
- #
2
- # Helper class to make accessing record specific settings easier
3
- #
4
- class SettingAccessors::Accessor
5
-
6
- def initialize(record)
7
- @record = record
8
- @temp_settings = {}
9
- end
10
-
11
- #
12
- # Tries to retrieve the given setting's value from the temp settings
13
- # (already read/written values in this instance). If the setting hasn't been
14
- # used before, its value is retrieved from the database.
15
- #
16
- # If a setting hasn't been read by this record (instance) before, its value
17
- # is stored in the local read set.
18
- #
19
- # TODO: See if this causes problems with read settings not being updated by external changes.
20
- # User1: Read Setting X
21
- # User2: Update Setting X
22
- # User1: Read Setting X -> Gets old value from temp settings.
23
- # This shouldn't be too dangerous as the system state will be refreshed with every request though.
24
- #
25
- def [](key)
26
- return @temp_settings[key.to_sym] if has_key?(key)
27
- value = SettingAccessors.setting_class.get(key, @record)
28
- @temp_settings[key.to_sym] = value unless value.nil?
29
- value
30
- end
31
-
32
- #
33
- # Tries to fetch a setting value using the provided key and #[].
34
- # It will only return the +default+ value if there is
35
- # - no temporary setting with the given key AND
36
- # - no already persisted setting (see #[])
37
- #
38
- def fetch(key, default = nil)
39
- result = self[key]
40
- return default if result.nil? && !has_key?(key)
41
- result
42
- end
43
-
44
- #
45
- # Like #fetch, but it will store the default value as a temporary setting
46
- # if no actual setting value could be found. This is useful to further work
47
- # with default setting values.
48
- # The default value is cloned (using #dup to avoid copying object states) before
49
- # it is assigned. This will not work for singleton instances like true, false, etc.
50
- #
51
- def fetch_and_store(key, default = nil)
52
- result = self[key]
53
- if result.nil? && !has_key?(key)
54
- self[key] = default.duplicable? ? default.dup : default
55
- else
56
- result
57
- end
58
- end
59
-
60
- def has_key?(key)
61
- @temp_settings.has_key?(key.to_sym)
62
- end
63
-
64
- #
65
- # Writes a setting's value
66
- #
67
- def []=(key, val)
68
- set_value_before_type_cast(key, val)
69
- @temp_settings[key.to_sym] = SettingAccessors::Internal.converter(value_type(key)).convert(val)
70
- end
71
-
72
- #
73
- # Tries to find a setting for this record.
74
- # If none is found, will return the default setting value
75
- # specified in the setting config file.
76
- #
77
- def get_or_default(key)
78
- fetch_and_store(key, SettingAccessors.setting_class.get_or_default(key, @record))
79
- end
80
-
81
- #
82
- # Tries to find a setting for this record first.
83
- # If none is found, tries to find a global setting with the same name
84
- #
85
- def get_or_global(key)
86
- fetch_and_store(key, SettingAccessors.setting_class.get(key))
87
- end
88
-
89
- #
90
- # Tries to find a setting for this record first,
91
- # if none is found, it will return the given value instead.
92
- #
93
- def get_or_value(key, value)
94
- fetch_and_store(key, value)
95
- end
96
-
97
- def get_with_fallback(key, fallback = nil)
98
- return self[key] if fallback.nil?
99
-
100
- case fallback.to_s
101
- when 'default' then get_or_default(key)
102
- when 'global' then get_or_global(key)
103
- else get_or_value(key, fallback)
104
- end
105
- end
106
-
107
- #
108
- # @return [String] the setting's value type in the +@record+'s context
109
- #
110
- def value_type(key)
111
- SettingAccessors::Internal.setting_value_type(key, @record)
112
- end
113
-
114
- #----------------------------------------------------------------
115
- # ActiveRecord Helper Methods Emulation
116
- #----------------------------------------------------------------
117
-
118
- def value_was(key, fallback = nil)
119
- return SettingAccessors.setting_class.get(key, @record) if fallback.nil?
120
-
121
- case fallback.to_s
122
- when 'default' then SettingAccessors.setting_class.get_or_default(key, @record)
123
- when 'global' then SettingAccessors.setting_class.get(key)
124
- else fallback
125
- end
126
- end
127
-
128
- def value_changed?(key)
129
- self[key] != value_was(key)
130
- end
131
-
132
- def value_before_type_cast(key)
133
- SettingAccessors::Internal.lookup_nested_hash(@values_before_type_casts, key.to_s) || self[key]
134
- end
135
-
136
- protected
137
-
138
- #
139
- # Keeps a record of the originally set value for a setting before it was
140
- # automatically converted.
141
- #
142
- def set_value_before_type_cast(key, value)
143
- @values_before_type_casts ||= {}
144
- @values_before_type_casts[key.to_s] = value
145
- end
146
-
147
- #
148
- # Validates the new setting values.
149
- # If there is an accessor for the setting, the errors will be
150
- # directly forwarded to it, otherwise to :base
151
- #
152
- # Please do not call this method directly, use the IntegrationValidator
153
- # class instead, e.g.
154
- #
155
- # validates_with SettingAccessors::IntegrationValidator
156
- #
157
- def validate!
158
- @temp_settings.each do |key, value|
159
- validation_errors = SettingAccessors.setting_class.validation_errors(key, value, @record)
160
- validation_errors.each do |message|
161
- if @record.respond_to?("#{key}=")
162
- @record.errors.add(key, message)
163
- else
164
- @record.errors.add :base, :invalid_setting, :name => key, :message => message
165
- end
166
- end
167
- end
168
- end
169
-
170
- #
171
- # Saves the new setting values into the database
172
- # Please note that there is no check if the values changed their
173
- # in the meantime.
174
- #
175
- # Also, this method expects that the settings were validated
176
- # before using #validate! and will therefore not perform
177
- # validations itself.
178
- #
179
- def persist!
180
- @temp_settings.each do |key, value|
181
- Setting.create_or_update(key, value, @record)
182
- end
183
- flush!
184
- end
185
-
186
- def flush!
187
- @temp_settings = {}
188
- end
189
- end
@@ -1,71 +0,0 @@
1
- #
2
- # This class hopefully will hopefully one day mimic ActiveRecord's
3
- # attribute assigning methods, meaning that a conversion to the column type
4
- # is done as soon as a new value is assigned by the programmer.
5
- #
6
- # If the value cannot be parsed in the required type, +nil+ is assigned.
7
- # Please make sure that you specify the correct validations in settings.yml
8
- # or assigned model to avoid this.
9
- #
10
- # Currently supported types:
11
- # - Fixnum
12
- # - String
13
- # - Boolean
14
- #
15
- # If the type is 'polymorphic', it is not converted at all.
16
- #
17
- class SettingAccessors::Converter
18
-
19
- def initialize(value_type)
20
- @value_type = value_type
21
- end
22
-
23
- #
24
- # Converts the setting's value to the correct type
25
- #
26
- def convert(new_value)
27
- #If the value is set to be polymorphic, we don't have to convert anything.
28
- return new_value if @value_type.to_s == 'polymorphic'
29
-
30
- #ActiveRecord only converts non-nil values to their database type
31
- #during assignment
32
- return new_value if new_value.nil?
33
-
34
- parse_method = :"parse_#{@value_type}"
35
-
36
- if private_methods.include?(parse_method)
37
- send(parse_method, new_value)
38
- else
39
- Rails.logger.warn("Invalid Setting type: #{@value_type}")
40
- new_value
41
- end
42
- end
43
-
44
- private
45
-
46
- def parse_boolean(value)
47
- case value
48
- when TrueClass, FalseClass
49
- value
50
- when String
51
- return true if %w[true 1].include?(value.downcase)
52
- return false if %w[false 0].include?(value.downcase)
53
- nil
54
- when Fixnum
55
- return true if value == 1
56
- return false if value.zero?
57
- nil
58
- else
59
- nil
60
- end
61
- end
62
-
63
- def parse_integer(value)
64
- value.to_i
65
- end
66
-
67
- def parse_string(value)
68
- value.to_s
69
- end
70
-
71
- end
@@ -1,15 +0,0 @@
1
- #
2
- # This class handles model validations for assigned records, e.g.
3
- # if the settings are accessed using the Accessor class in this module.
4
- # Only the new temp values are validated using the setting config.
5
- #
6
- # The main work is still done in the Accessor class, so we don't have
7
- # to access its instance variables here, this class acts as a wrapper
8
- # for Rails' validation chain
9
- #
10
-
11
- class SettingAccessors::IntegrationValidator < ActiveModel::Validator
12
- def validate(record)
13
- record.settings.send(:validate!)
14
- end
15
- end
@@ -1,144 +0,0 @@
1
- class SettingAccessors::Validator < ActiveModel::Validator
2
-
3
- def validate(record)
4
- record.send(:validations).each do |key, requirement|
5
- if key.to_s == 'custom'
6
- Array(requirement).each do |validation|
7
- run_custom_validation(record, validation)
8
- end
9
- elsif built_in_validation?(key)
10
- send("validate_#{key}", record, requirement)
11
- else
12
- raise ArgumentError.new("The invalid validation '#{key}' was given in model '#{defining_model(record).to_s}'")
13
- end
14
- end
15
- end
16
-
17
- private
18
-
19
- def defining_model(record)
20
- if SettingAccessors::Internal.globally_defined_setting?(record.name) || !record.assignable
21
- SettingAccessors.setting_class
22
- else
23
- record.assignable.class
24
- end
25
- end
26
-
27
- #
28
- # Runs a custom validation method
29
- # The method may either be a Proc or an instance method in +record+.+class+
30
- #
31
- def run_custom_validation(record, proc)
32
- case proc
33
- when Proc
34
- proc.call(record)
35
- when Symbol
36
- if defining_model(record).respond_to?(proc)
37
- defining_model(record).send(proc)
38
- else
39
- raise ArgumentError.new "The method '#{proc}' was set up as validation method in model '#{defining_model(record).name}', but doesn't exist."
40
- end
41
- else
42
- raise ArgumentError.new "An invalid validations method was given ('#{proc}')"
43
- end
44
- end
45
-
46
- #
47
- # @return [TrueClass, FalseClass] +true+ if the given validation
48
- # is a built-in one.
49
- #
50
- def built_in_validation?(validation_name)
51
- private_methods.include?("validate_#{validation_name}".to_sym)
52
- end
53
-
54
- #
55
- # Validates that the setting's value is given
56
- # accepts :allow_blank and :allow_nil as options
57
- #
58
- def validate_presence(record, requirement)
59
- return true unless requirement
60
-
61
- if requirement.is_a?(Hash)
62
- if requirement['allow_blank'] && !record.value.nil? ||
63
- requirement['allow_nil'] && record.value.nil? ||
64
- record.value.present?
65
- true
66
- else
67
- add_error record, :blank
68
- false
69
- end
70
- else
71
- add_error_if record.value.nil? || record.value == '', record, :blank
72
- end
73
- end
74
-
75
- #
76
- # Validates numericality of the setting's value based on the options given
77
- # in settings.yml
78
- #
79
- def validate_numericality(record, options)
80
- #Test if the value is Numeric in any way (float or int)
81
- add_error_if(!parse_value_as_numeric(record.value), record, :not_a_number) && return
82
-
83
- #If the validation was set to check for integer values, do that as well
84
- add_error_if(options['only_integer'] && !parse_value_as_fixnum(record.value), record, :not_an_integer)
85
- end
86
-
87
- #
88
- # Validates whether the given value is a valid boolean
89
- #
90
- def validate_boolean(record, requirement)
91
- add_error_if(requirement && parse_value_as_boolean(record.value).nil?, record, :not_a_boolean)
92
- end
93
-
94
- #----------------------------------------------------------------
95
- # Helper Methods
96
- #----------------------------------------------------------------
97
-
98
- def add_error(record, validation, options = {})
99
- record.errors.add :value, validation, options
100
- end
101
-
102
- def add_error_if(cond, record, validation, options = {})
103
- add_error(record, validation, options) if cond
104
- cond
105
- end
106
-
107
- #
108
- # Borrowed from Rails' numericality validator
109
- # @return [Float, NilClass] the given String value as a float or nil
110
- # if the value was not a valid Numeric
111
- #
112
- def parse_value_as_numeric(raw_value)
113
- Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/
114
- rescue ArgumentError, TypeError
115
- nil
116
- end
117
-
118
- #
119
- # Borrowed from Rails' numericality validator
120
- #
121
- # @return [Fixnum, NilClass] the given String value as an int or nil
122
- #
123
- def parse_value_as_fixnum(raw_value)
124
- raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
125
- end
126
-
127
- #
128
- # Tries to parse the given value as a boolean value
129
- #
130
- # @return [TrueClass, FalseClass, NilClass]
131
- #
132
- def parse_value_as_boolean(raw_value)
133
- case raw_value
134
- when TrueClass, FalseClass
135
- raw_value
136
- when String
137
- return true if %w[true 1].include?(raw_value.downcase)
138
- return false if %w[false 0].include?(raw_value.downcase)
139
- nil
140
- else
141
- nil
142
- end
143
- end
144
- end