dynabute 0.0.13 → 0.0.15
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 +5 -5
- data/README.md +37 -13
- data/lib/dynabute/dynabutable.rb +91 -12
- data/lib/dynabute/field.rb +1 -10
- data/lib/dynabute/option.rb +1 -1
- data/lib/dynabute/util.rb +1 -1
- data/lib/dynabute/values/base.rb +1 -1
- data/lib/dynabute/values/select_value.rb +3 -2
- data/lib/dynabute/version.rb +1 -1
- metadata +11 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e2f0aa518cab55eb4e4f2530660132ab9719b5ecb751f556d338662fb0668157
|
4
|
+
data.tar.gz: 3890334d7ff87796ec5c8e9f6bda8e219377a082de93854ea14930384eace26b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95015958c27c3de3af3d286440ec71d463bd0e517099eb1be4128f347c75e2bc22dff339e3e09ffe5062f29271a17b041a9cb5ba29f70433842232c58642eb3d
|
7
|
+
data.tar.gz: cb46b6dca7dc04ce5362b85313d1d8665cc4273fe6ea78b0622332ffe29ebe393ad40d2df34af179d53992218082233ecf9ff416906b066898c712e1a6b7d0af
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Dynabute
|
2
|
-
Dynamically add attributes on Relational Database backed ActiveRecord, without hash serialization and bullshits
|
2
|
+
Dynamically add attributes on Relational Database backed ActiveRecord, without hash serialization and bullshits.
|
3
|
+
|
4
|
+
Try messing with a [working demo](https://dynabute-demo.herokuapp.com).
|
3
5
|
|
4
6
|
## Usage
|
5
7
|
|
@@ -12,17 +14,17 @@ end
|
|
12
14
|
|
13
15
|
then add some field definitions
|
14
16
|
```ruby
|
15
|
-
User.dynabutes
|
16
|
-
User.dynabutes
|
17
|
-
User.dynabutes
|
17
|
+
User.dynabutes.create( name: 'age', value_type: 'integer' )
|
18
|
+
User.dynabutes.create( name: 'skinny', value_type: 'boolean' )
|
19
|
+
User.dynabutes.create( name: 'personalities', value_type: 'string', has_many: true )
|
18
20
|
```
|
19
21
|
|
20
22
|
now set value
|
21
23
|
```ruby
|
22
24
|
user = User.create
|
23
25
|
|
24
|
-
user.
|
25
|
-
# =>
|
26
|
+
user.set_dynabute_value( name: 'age', value: 35 ).save
|
27
|
+
# => true
|
26
28
|
```
|
27
29
|
|
28
30
|
check the value
|
@@ -50,13 +52,13 @@ user.update(
|
|
50
52
|
)
|
51
53
|
```
|
52
54
|
|
53
|
-
`select` value_type is also available
|
55
|
+
`select` value_type is also available
|
54
56
|
```ruby
|
55
|
-
User.dynabutes
|
56
|
-
User.dynabutes
|
57
|
+
User.dynabutes.create( name: 'gender', value_type: 'select', options_attributes: [ { label: 'male' }, { label: 'female' } ] )
|
58
|
+
User.dynabutes.create( name: 'hobbies', has_many: true, value_type: 'select', options_attributes: [ { label: 'running' }, { label: 'swimming' }, { label: 'hiking' } ] )
|
57
59
|
```
|
58
60
|
|
59
|
-
list the available options for a field
|
61
|
+
list the available options for a field
|
60
62
|
```ruby
|
61
63
|
User.dynabutes.find_by(name: gender).options
|
62
64
|
# => [#<Dynabute::Option:0x007ff53e2e1f90 id: 1, field_id: 4, label: "male">,
|
@@ -74,7 +76,7 @@ user.update(dynabute_values_attributes: [
|
|
74
76
|
{ name: 'hobbies', value: hobbies[1].id }
|
75
77
|
])
|
76
78
|
```
|
77
|
-
|
79
|
+
|
78
80
|
check out the selected options
|
79
81
|
```ruby
|
80
82
|
user.dynabute_value(name: 'gender').option
|
@@ -84,7 +86,16 @@ user.dynabute_value(name: 'hobbies').map(&:option)
|
|
84
86
|
# => [#<Dynabute::Option:0x007fb26c4467d8 id: 5, field_id: 5, label: "running">,
|
85
87
|
# #<Dynabute::Option:0x007fb26c446238 id: 6, field_id: 5, label: "swimming">]
|
86
88
|
```
|
87
|
-
|
89
|
+
|
90
|
+
remove value
|
91
|
+
|
92
|
+
```
|
93
|
+
user.remove_dynabute_value(name: 'age')
|
94
|
+
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: 35>
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
|
88
99
|
## Installation
|
89
100
|
Add this line to your application's Gemfile:
|
90
101
|
|
@@ -98,9 +109,22 @@ $ bundle install
|
|
98
109
|
$ rails generate dynabute:install
|
99
110
|
$ rake db:migrate
|
100
111
|
```
|
112
|
+
## Contributors
|
113
|
+
|
114
|
+
[<img src="https://github.com/CrAsH1101.png" width="60px;"/><br /><sub><a href="https://github.com/CrAsH1101">CrAsH1101</a></sub>](https://github.com/CrAsH1101)
|
101
115
|
|
102
116
|
## Contributing
|
103
|
-
|
117
|
+
|
118
|
+
#### Rspec: set test environment
|
119
|
+
```bash
|
120
|
+
$ bundle install
|
121
|
+
$ cd spec/dummy/
|
122
|
+
$ RAILS_ENV=test bundle exec rake db:create
|
123
|
+
$ RAILS_ENV=test bundle exec rake db:migrate
|
124
|
+
$ cd ../../
|
125
|
+
$ bundle exec rspec
|
126
|
+
```
|
127
|
+
|
104
128
|
|
105
129
|
## License
|
106
130
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/dynabute/dynabutable.rb
CHANGED
@@ -4,6 +4,7 @@ require 'dynabute/nested_attributes'
|
|
4
4
|
|
5
5
|
module Dynabute
|
6
6
|
module Dynabutable
|
7
|
+
class ValueNotFound < StandardError; end
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
included do
|
@@ -11,7 +12,7 @@ module Dynabute
|
|
11
12
|
include NestedAttributes::API
|
12
13
|
|
13
14
|
(Dynabute::Field::TYPES).each do |t|
|
14
|
-
has_many Util.value_relation_name(t), class_name: Util.value_class_name(t), as: :dynabutable
|
15
|
+
has_many Util.value_relation_name(t), class_name: Util.value_class_name(t), as: :dynabutable, inverse_of: 'dynabutable', dependent: :delete_all
|
15
16
|
accepts_nested_attributes_for Util.value_relation_name(t), reject_if: proc{ |param| param[:value].blank? }, allow_destroy: true
|
16
17
|
end
|
17
18
|
|
@@ -36,18 +37,73 @@ module Dynabute
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def dynabute_value(name: nil, field_id: nil, field: nil)
|
39
|
-
|
40
|
-
|
41
|
-
if
|
42
|
-
|
40
|
+
field_obj = find_field(name, field_id, field)
|
41
|
+
field_values = send(Util.value_relation_name(field_obj.value_type))
|
42
|
+
field_value = if field_obj.has_many
|
43
|
+
field_values.select{ |v| v.field_id == field_obj.id }
|
43
44
|
else
|
44
|
-
|
45
|
+
field_values.detect{ |v| v.field_id == field_obj.id }
|
45
46
|
end
|
47
|
+
field_value
|
46
48
|
end
|
47
49
|
|
48
50
|
def build_dynabute_value(name: nil, field_id: nil, field: nil, **rest)
|
49
|
-
|
50
|
-
send(Util.value_relation_name(
|
51
|
+
field_obj = find_field(name, field_id, field)
|
52
|
+
send(Util.value_relation_name(field_obj.value_type)).build(field_id: field_obj.id, **rest)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns value attribute for specified field.
|
56
|
+
# If field can have multiple values for single target model object,
|
57
|
+
# an array of values is returned, unless specific value_id is provided.
|
58
|
+
def get_dynabute_value(name: nil, field_id: nil, field: nil, value_id: nil)
|
59
|
+
field_obj = find_field(name, field_id, field)
|
60
|
+
value_obj = dynabute_value(field: field_obj)
|
61
|
+
return unless value_obj
|
62
|
+
if field_obj.has_many && value_id
|
63
|
+
value_obj = value_obj.detect{|v| v.id == value_id}
|
64
|
+
end
|
65
|
+
value_obj.is_a?(Array) ? value_obj.map(&:value) : value_obj&.value
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the value in the target model object nested attribute structure.
|
69
|
+
# If field can have multiple values for single target model object,
|
70
|
+
# a new value will be added, unless specific value_id is provided.
|
71
|
+
#
|
72
|
+
# This method does not store changes in the database. "save" method should
|
73
|
+
# be called on target model to store all changes, or individually on every
|
74
|
+
# value record returned by this method.
|
75
|
+
def set_dynabute_value(name: nil, field_id: nil, field: nil, value: nil, value_id: nil)
|
76
|
+
field_obj = find_field(name, field_id, field)
|
77
|
+
value_obj = dynabute_value(field: field_obj)
|
78
|
+
if field_obj.has_many
|
79
|
+
if value_id
|
80
|
+
value_obj = value_obj.detect{|v| v.id == value_id} if value_obj
|
81
|
+
fail ValueNotFound unless value_obj
|
82
|
+
else
|
83
|
+
value_obj = build_dynabute_value(field: field_obj)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
value_obj ||= build_dynabute_value(field: field_obj)
|
87
|
+
end
|
88
|
+
value_obj.value = value
|
89
|
+
value_obj
|
90
|
+
end
|
91
|
+
|
92
|
+
# Removes value from database and keeps dynabute relations up-to-date.
|
93
|
+
# If field can have multiple values for single target model object,
|
94
|
+
# all values will be removed, unless specific value_id is provided.
|
95
|
+
#
|
96
|
+
# This method stores changes in the database
|
97
|
+
def remove_dynabute_value(name: nil, field_id: nil, field: nil, value_id: nil)
|
98
|
+
field_obj = find_field(name, field_id, field)
|
99
|
+
value_obj = dynabute_value(field: field_obj)
|
100
|
+
if value_obj && field_obj.has_many && value_id
|
101
|
+
value_obj = value_obj.detect{|v| v.id == value_id}
|
102
|
+
end
|
103
|
+
if value_obj
|
104
|
+
result_obj = send(Util.value_relation_name(field_obj.value_type)).destroy(value_obj)
|
105
|
+
value_obj.is_a?(Array) ? result_obj : result_obj.first
|
106
|
+
end
|
51
107
|
end
|
52
108
|
|
53
109
|
def method_missing(*args)
|
@@ -63,11 +119,34 @@ module Dynabute
|
|
63
119
|
end
|
64
120
|
|
65
121
|
private
|
122
|
+
|
66
123
|
def find_field(name, id, field)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
124
|
+
# Validate field argument
|
125
|
+
if field
|
126
|
+
unless field.is_a?(Dynabute::Field)
|
127
|
+
fail ArgumentError, 'Argument field must be Dynabute::Field'
|
128
|
+
end
|
129
|
+
return field
|
130
|
+
end
|
131
|
+
name_or_id = {}
|
132
|
+
# Validate name argument
|
133
|
+
if name
|
134
|
+
unless name.is_a?(String) || name.is_a?(Symbol)
|
135
|
+
fail ArgumentError, 'Argument name must be String or Symbol'
|
136
|
+
end
|
137
|
+
name_or_id[:name] = name.to_s
|
138
|
+
end
|
139
|
+
# Validate id argument
|
140
|
+
if id
|
141
|
+
unless id.is_a?(Integer)
|
142
|
+
fail ArgumentError, 'Argument id must be Integer'
|
143
|
+
end
|
144
|
+
name_or_id[:id] = id
|
145
|
+
end
|
146
|
+
name_or_id.reject!{ |k, v| v.blank? }
|
147
|
+
fail ArgumentError, 'Invalid arguments' if name_or_id.blank?
|
148
|
+
field_obj = Dynabute::Field.find_by(name_or_id.merge(target_model: self.class.to_s))
|
149
|
+
fail Dynabute::FieldNotFound.new(name_or_id) if field_obj.nil?
|
71
150
|
field_obj
|
72
151
|
end
|
73
152
|
|
data/lib/dynabute/field.rb
CHANGED
@@ -9,7 +9,7 @@ module Dynabute
|
|
9
9
|
validates :value_type, inclusion: {in: TYPES}
|
10
10
|
validates :name, presence: true, uniqueness: { scope: :target_model }
|
11
11
|
validates_presence_of :target_model
|
12
|
-
has_many :options, class_name: 'Dynabute::Option', dependent: :destroy
|
12
|
+
has_many :options, class_name: 'Dynabute::Option', dependent: :destroy, inverse_of: 'field'
|
13
13
|
accepts_nested_attributes_for :options, allow_destroy: true
|
14
14
|
|
15
15
|
scope :for, ->(klass){ where(target_model: klass) }
|
@@ -22,15 +22,6 @@ module Dynabute
|
|
22
22
|
TYPES
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.<<(records)
|
26
|
-
if records.respond_to? :each
|
27
|
-
records.each {|r| r.update!(target_model: get_parent_class_name) }
|
28
|
-
else
|
29
|
-
records.update!(target_model: get_parent_class_name)
|
30
|
-
end
|
31
|
-
all
|
32
|
-
end
|
33
|
-
|
34
25
|
private
|
35
26
|
def self.get_parent_class_name
|
36
27
|
all.where_clause.binds.detect{|w| w.name == 'target_model'}.try(:value)
|
data/lib/dynabute/option.rb
CHANGED
@@ -4,7 +4,7 @@ module Dynabute
|
|
4
4
|
class Option < ActiveRecord::Base
|
5
5
|
def self.table_name_prefix; Util.table_name_prefix; end
|
6
6
|
belongs_to :field, class_name: 'Dynabute::Field'
|
7
|
-
has_many :values, class_name: Util.value_class_name(:select), foreign_key: 'value'
|
7
|
+
has_many :values, class_name: Util.value_class_name(:select), foreign_key: 'value', inverse_of: 'option'
|
8
8
|
validates :label, presence: true, uniqueness: { scope: ['field_id'] }
|
9
9
|
validate Util.nested_attributable_presence_validator(:field_id, :field)
|
10
10
|
end
|
data/lib/dynabute/util.rb
CHANGED
@@ -5,7 +5,7 @@ module Dynabute
|
|
5
5
|
return -> {
|
6
6
|
attr = id_attr.to_sym
|
7
7
|
if (persisted? && self[attr].nil?) || (new_record? && send(id_relation_accessor).nil?)
|
8
|
-
errors
|
8
|
+
errors.add(attr, I18n.t('errors.messages.blank'))
|
9
9
|
return fail(:abort) if(halt)
|
10
10
|
end
|
11
11
|
}
|
data/lib/dynabute/values/base.rb
CHANGED
@@ -21,7 +21,7 @@ module Dynabute::Values
|
|
21
21
|
def reject_duplication_for_has_one
|
22
22
|
return if field.has_many
|
23
23
|
return unless self.class.exists?(field_id: field_id, dynabutable_id: dynabutable_id, dynabutable_type: dynabutable_type)
|
24
|
-
self.errors
|
24
|
+
self.errors.add(:base, 'Multiple records for has_one relationship detected')
|
25
25
|
throw :abort
|
26
26
|
end
|
27
27
|
end
|
@@ -7,8 +7,9 @@ module Dynabute
|
|
7
7
|
|
8
8
|
def ensure_option_is_in_same_field
|
9
9
|
if option && (option.field_id != field_id)
|
10
|
-
errors
|
11
|
-
|
10
|
+
errors.add(:value, I18n.t('errors.messages.dynabutes.wrong_field_option',
|
11
|
+
default: 'is pointing to the option for other dynabute field')
|
12
|
+
)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
data/lib/dynabute/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynabute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liooo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rails
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: sqlite3
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,16 +84,16 @@ dependencies:
|
|
84
84
|
name: rspec-rails
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '5'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '5'
|
97
97
|
description: Dynamically add attributes on ActiveRecord.
|
98
98
|
email:
|
99
99
|
- ryoyamada3@gmail.com
|
@@ -141,8 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
141
|
- !ruby/object:Gem::Version
|
142
142
|
version: '0'
|
143
143
|
requirements: []
|
144
|
-
|
145
|
-
rubygems_version: 2.6.12
|
144
|
+
rubygems_version: 3.0.3.1
|
146
145
|
signing_key:
|
147
146
|
specification_version: 4
|
148
147
|
summary: DYNAmic attriBUTEs for ActiveRecord
|