rails-properties 3.4.3
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +74 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/README.md +135 -0
- data/Rakefile +6 -0
- data/ci/Gemfile-rails-3-1 +5 -0
- data/ci/Gemfile-rails-3-2 +5 -0
- data/ci/Gemfile-rails-4-0 +6 -0
- data/ci/Gemfile-rails-4-1 +6 -0
- data/ci/Gemfile-rails-4-2 +6 -0
- data/ci/Gemfile-rails-5-0 +5 -0
- data/ci/Gemfile-rails-5-1 +5 -0
- data/ci/Gemfile-rails-5-2 +5 -0
- data/lib/generators/rails_properties/migration/migration_generator.rb +23 -0
- data/lib/generators/rails_properties/migration/templates/migration.rb +21 -0
- data/lib/rails-properties.rb +23 -0
- data/lib/rails-properties/base.rb +48 -0
- data/lib/rails-properties/configuration.rb +32 -0
- data/lib/rails-properties/property_object.rb +84 -0
- data/lib/rails-properties/scopes.rb +34 -0
- data/lib/rails-properties/version.rb +3 -0
- data/rails-properties.gemspec +29 -0
- data/spec/configuration_spec.rb +108 -0
- data/spec/database.yml +3 -0
- data/spec/properties_spec.rb +248 -0
- data/spec/property_object_spec.rb +153 -0
- data/spec/queries_spec.rb +101 -0
- data/spec/scopes_spec.rb +31 -0
- data/spec/serialize_spec.rb +40 -0
- data/spec/spec_helper.rb +111 -0
- data/spec/support/matchers/perform_queries.rb +22 -0
- data/spec/support/query_counter.rb +17 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7be3c5bb1390b030f3cf3b0bd14a0b7227806704
|
4
|
+
data.tar.gz: bf1b88871e3f28897b72a33e305d329ea2041e4e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9f6063f9fd8e8f0b68b8f3bb5b4d1c82cd6551e856d52b05e5a42f45a1a965d25f52942226cb528fb0aef9d50b8ac81af11daf5e5c311fb7ad01997cde81dc5
|
7
|
+
data.tar.gz: 41c8f6000e5ab99749548fac88738720a69c86a83241ef43386e2f2183a706eaa84507c9941dddcaeed5d92d71d7c77cb6aaf5ae58e99449527822cb37026594
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.10
|
6
|
+
- 2.2.9
|
7
|
+
- 2.3.6
|
8
|
+
- 2.4.3
|
9
|
+
- 2.5.0
|
10
|
+
gemfile:
|
11
|
+
- ci/Gemfile-rails-3-1
|
12
|
+
- ci/Gemfile-rails-3-2
|
13
|
+
- ci/Gemfile-rails-4-0
|
14
|
+
- ci/Gemfile-rails-4-1
|
15
|
+
- ci/Gemfile-rails-4-2
|
16
|
+
- ci/Gemfile-rails-5-0
|
17
|
+
- ci/Gemfile-rails-5-1
|
18
|
+
- ci/Gemfile-rails-5-2
|
19
|
+
matrix:
|
20
|
+
include:
|
21
|
+
- rvm: 2.2.9
|
22
|
+
gemfile: ci/Gemfile-rails-4-0
|
23
|
+
env: PROTECTED_ATTRIBUTES=true
|
24
|
+
- rvm: 2.2.9
|
25
|
+
gemfile: ci/Gemfile-rails-4-1
|
26
|
+
env: PROTECTED_ATTRIBUTES=true
|
27
|
+
- rvm: 2.2.9
|
28
|
+
gemfile: ci/Gemfile-rails-4-2
|
29
|
+
env: PROTECTED_ATTRIBUTES=true
|
30
|
+
exclude:
|
31
|
+
- rvm: 1.9.3
|
32
|
+
gemfile: ci/Gemfile-rails-5-0
|
33
|
+
- rvm: 1.9.3
|
34
|
+
gemfile: ci/Gemfile-rails-5-1
|
35
|
+
- rvm: 1.9.3
|
36
|
+
gemfile: ci/Gemfile-rails-5-2
|
37
|
+
- rvm: 2.0.0
|
38
|
+
gemfile: ci/Gemfile-rails-5-0
|
39
|
+
- rvm: 2.0.0
|
40
|
+
gemfile: ci/Gemfile-rails-5-1
|
41
|
+
- rvm: 2.0.0
|
42
|
+
gemfile: ci/Gemfile-rails-5-2
|
43
|
+
- rvm: 2.1.10
|
44
|
+
gemfile: ci/Gemfile-rails-5-0
|
45
|
+
- rvm: 2.1.10
|
46
|
+
gemfile: ci/Gemfile-rails-5-1
|
47
|
+
- rvm: 2.1.10
|
48
|
+
gemfile: ci/Gemfile-rails-5-2
|
49
|
+
- rvm: 2.2.9
|
50
|
+
gemfile: ci/Gemfile-rails-3-1
|
51
|
+
- rvm: 2.3.6
|
52
|
+
gemfile: ci/Gemfile-rails-3-1
|
53
|
+
- rvm: 2.4.3
|
54
|
+
gemfile: ci/Gemfile-rails-3-1
|
55
|
+
- rvm: 2.5.0
|
56
|
+
gemfile: ci/Gemfile-rails-3-1
|
57
|
+
- rvm: 2.2.9
|
58
|
+
gemfile: ci/Gemfile-rails-3-2
|
59
|
+
- rvm: 2.3.6
|
60
|
+
gemfile: ci/Gemfile-rails-3-2
|
61
|
+
- rvm: 2.4.3
|
62
|
+
gemfile: ci/Gemfile-rails-3-2
|
63
|
+
- rvm: 2.4.3
|
64
|
+
gemfile: ci/Gemfile-rails-4-0
|
65
|
+
- rvm: 2.4.3
|
66
|
+
gemfile: ci/Gemfile-rails-4-1
|
67
|
+
- rvm: 2.5.0
|
68
|
+
gemfile: ci/Gemfile-rails-3-2
|
69
|
+
- rvm: 2.5.0
|
70
|
+
gemfile: ci/Gemfile-rails-4-0
|
71
|
+
- rvm: 2.5.0
|
72
|
+
gemfile: ci/Gemfile-rails-4-1
|
73
|
+
before_install: gem update bundler
|
74
|
+
sudo: false
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012-2018 Georg Ledermann
|
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,135 @@
|
|
1
|
+
# Properties for Rails
|
2
|
+
|
3
|
+
Ruby gem to handle properties for ActiveRecord instances by storing them as serialized Hash in a separate database table. Namespaces and defaults included.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
* Ruby 1.9.3 or newer
|
8
|
+
* Rails 3.1 or newer (including Rails 5.2)
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Include the gem in your Gemfile and run `bundle` to install it:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'rails-properties'
|
17
|
+
```
|
18
|
+
|
19
|
+
Generate and run the migration:
|
20
|
+
|
21
|
+
```shell
|
22
|
+
rails g rails_properties:migration
|
23
|
+
rake db:migrate
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Define properties
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class User < ActiveRecord::Base
|
33
|
+
has_properties do |s|
|
34
|
+
s.key :dashboard, :defaults => { :theme => 'blue', :view => 'monthly', :filter => false }
|
35
|
+
s.key :calendar, :defaults => { :scope => 'company'}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
If no defaults are needed, a simplified syntax can be used:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
has_properties :dashboard, :calendar
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Every property is handled by the class `RailsProperties::PropertyObject`. You can use your own class, e.g. for validations:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class Project < ActiveRecord::Base
|
52
|
+
has_properties :info, :class_name => 'ProjectPropertyObject'
|
53
|
+
end
|
54
|
+
|
55
|
+
class ProjectPropertyObject < RailsProperties::PropertyObject
|
56
|
+
validate do
|
57
|
+
unless self.owner_name.present? && self.owner_name.is_a?(String)
|
58
|
+
errors.add(:base, "Owner name is missing")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Set properties
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
user = User.find(1)
|
68
|
+
user.properties(:dashboard).theme = 'black'
|
69
|
+
user.properties(:calendar).scope = 'all'
|
70
|
+
user.properties(:calendar).display = 'daily'
|
71
|
+
user.save! # saves new or changed properties, too
|
72
|
+
```
|
73
|
+
|
74
|
+
or
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
user = User.find(1)
|
78
|
+
user.properties(:dashboard).update_attributes! :theme => 'black'
|
79
|
+
user.properties(:calendar).update_attributes! :scope => 'all', :display => 'daily'
|
80
|
+
```
|
81
|
+
|
82
|
+
|
83
|
+
### Get properties
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
user = User.find(1)
|
87
|
+
user.properties(:dashboard).theme
|
88
|
+
# => 'black
|
89
|
+
|
90
|
+
user.properties(:dashboard).view
|
91
|
+
# => 'monthly' (it's the default)
|
92
|
+
|
93
|
+
user.properties(:calendar).scope
|
94
|
+
# => 'all'
|
95
|
+
```
|
96
|
+
|
97
|
+
### Delete properties
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
user = User.find(1)
|
101
|
+
user.properties(:dashboard).update_attributes! :theme => nil
|
102
|
+
|
103
|
+
user.properties(:dashboard).view = nil
|
104
|
+
user.properties(:dashboard).save!
|
105
|
+
```
|
106
|
+
|
107
|
+
### Using scopes
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
User.with_properties
|
111
|
+
# => all users having any property
|
112
|
+
|
113
|
+
User.without_properties
|
114
|
+
# => all users without having any property
|
115
|
+
|
116
|
+
User.with_properties_for(:calendar)
|
117
|
+
# => all users having a property for 'calender'
|
118
|
+
|
119
|
+
User.without_properties_for(:calendar)
|
120
|
+
# => all users without having properties for 'calendar'
|
121
|
+
```
|
122
|
+
|
123
|
+
### Eager Loading
|
124
|
+
```ruby
|
125
|
+
User.includes(:property_objects)
|
126
|
+
# => Eager load property_objects when querying many users
|
127
|
+
```
|
128
|
+
|
129
|
+
## License
|
130
|
+
|
131
|
+
MIT License
|
132
|
+
|
133
|
+
Copyright (c) 2012-2018 [Georg Ledermann](http://www.georg-ledermann.de)
|
134
|
+
|
135
|
+
This gem is a rename of [rails-settings](https://github.com/ledermann/rails-settings) by [Georg Ledermann](https://github.com/ledermann)
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module RailsProperties
|
5
|
+
class MigrationGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
desc "Generates migration for rails-properties"
|
9
|
+
source_root File.expand_path('../templates', __FILE__)
|
10
|
+
|
11
|
+
def create_migration_file
|
12
|
+
migration_template 'migration.rb', 'db/migrate/rails_properties_migration.rb'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.next_migration_number(dirname)
|
16
|
+
if ActiveRecord::Base.timestamped_migrations
|
17
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
18
|
+
else
|
19
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIGRATION_BASE_CLASS = if ActiveRecord::VERSION::MAJOR >= 5
|
2
|
+
ActiveRecord::Migration[5.0]
|
3
|
+
else
|
4
|
+
ActiveRecord::Migration
|
5
|
+
end
|
6
|
+
|
7
|
+
class RailsPropertiesMigration < MIGRATION_BASE_CLASS
|
8
|
+
def self.up
|
9
|
+
create_table :properties do |t|
|
10
|
+
t.string :var, :null => false
|
11
|
+
t.text :value
|
12
|
+
t.references :target, :null => false, :polymorphic => true
|
13
|
+
t.timestamps :null => true
|
14
|
+
end
|
15
|
+
add_index :properties, [ :target_type, :target_id, :var ], :unique => true
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :properties
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RailsProperties
|
2
|
+
# In Rails 3, attributes can be protected by `attr_accessible` and `attr_protected`
|
3
|
+
# In Rails 4, attributes can be protected by using the gem `protected_attributes`
|
4
|
+
# In Rails 5, protecting attributes is obsolete (there are `StrongParameters` only)
|
5
|
+
def self.can_protect_attributes?
|
6
|
+
(ActiveRecord::VERSION::MAJOR == 3) || defined?(ProtectedAttributes)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rails-properties/property_object'
|
11
|
+
require 'rails-properties/configuration'
|
12
|
+
require 'rails-properties/base'
|
13
|
+
require 'rails-properties/scopes'
|
14
|
+
|
15
|
+
ActiveRecord::Base.class_eval do
|
16
|
+
def self.has_properties(*args, &block)
|
17
|
+
RailsProperties::Configuration.new(*args.unshift(self), &block)
|
18
|
+
|
19
|
+
include RailsProperties::Base
|
20
|
+
extend RailsProperties::Scopes
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RailsProperties
|
2
|
+
module Base
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
has_many :property_objects,
|
6
|
+
:as => :target,
|
7
|
+
:autosave => true,
|
8
|
+
:dependent => :delete_all,
|
9
|
+
:class_name => self.property_object_class_name
|
10
|
+
|
11
|
+
def properties(var)
|
12
|
+
raise ArgumentError unless var.is_a?(Symbol)
|
13
|
+
raise ArgumentError.new("Unknown key: #{var}") unless self.class.default_properties[var]
|
14
|
+
|
15
|
+
if RailsProperties.can_protect_attributes?
|
16
|
+
property_objects.detect { |s| s.var == var.to_s } || property_objects.build({ :var => var.to_s }, :without_protection => true)
|
17
|
+
else
|
18
|
+
property_objects.detect { |s| s.var == var.to_s } || property_objects.build(:var => var.to_s, :target => self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def properties=(value)
|
23
|
+
if value.nil?
|
24
|
+
property_objects.each(&:mark_for_destruction)
|
25
|
+
else
|
26
|
+
raise ArgumentError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def properties?(var=nil)
|
31
|
+
if var.nil?
|
32
|
+
property_objects.any? { |property_object| !property_object.marked_for_destruction? && property_object.value.present? }
|
33
|
+
else
|
34
|
+
properties(var).value.present?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_properties_hash
|
39
|
+
properties_hash = self.class.default_properties.dup
|
40
|
+
properties_hash.each do |var, vals|
|
41
|
+
properties_hash[var] = properties_hash[var].merge(properties(var.to_sym).value)
|
42
|
+
end
|
43
|
+
properties_hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RailsProperties
|
2
|
+
class Configuration
|
3
|
+
def initialize(*args, &block)
|
4
|
+
options = args.extract_options!
|
5
|
+
klass = args.shift
|
6
|
+
keys = args
|
7
|
+
|
8
|
+
raise ArgumentError unless klass
|
9
|
+
|
10
|
+
@klass = klass
|
11
|
+
@klass.class_attribute :default_properties, :property_object_class_name
|
12
|
+
@klass.default_properties = {}
|
13
|
+
@klass.property_object_class_name = options[:class_name] || 'RailsProperties::PropertyObject'
|
14
|
+
|
15
|
+
if block_given?
|
16
|
+
yield(self)
|
17
|
+
else
|
18
|
+
keys.each do |k|
|
19
|
+
key(k)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
raise ArgumentError.new('has_properties: No keys defined') if @klass.default_properties.blank?
|
24
|
+
end
|
25
|
+
|
26
|
+
def key(name, options={})
|
27
|
+
raise ArgumentError.new("has_properties: Symbol expected, but got a #{name.class}") unless name.is_a?(Symbol)
|
28
|
+
raise ArgumentError.new("has_properties: Option :defaults expected, but got #{options.keys.join(', ')}") unless options.blank? || (options.keys == [:defaults])
|
29
|
+
@klass.default_properties[name] = (options[:defaults] || {}).stringify_keys.freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|