Skip to content

Commit

Permalink
Automatically build missing values for valid fields.
Browse files Browse the repository at this point in the history
Allow update value for fields that do not have inserted record in
appropriate value table.
  • Loading branch information
CrAsH1101 committed Jan 20, 2022
1 parent 49830c8 commit 3b83611
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 14 deletions.
102 changes: 99 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,29 @@ now set value
```ruby
user = User.create
user.build_dynabute_value( name: 'age', value: 35 ).save
# => <Dynabute::Values::IntegerValue:0x007faba5279540 id: 1, field_id: 1, dynabutable_id: 1, dynabutable_type: "User", value: 35>
user.set_dynabute_value( name: 'age', value: 40 )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: 40>
user.set_dynabute_value( name: 'skinny', value: true )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: true>
user.save
# => true
# or update single value
user.set_dynabute_value( name: 'age', value: 35 ).save
# => true
```

check the value
```ruby
user.dynabute_value( name: 'age' ).value
user.get_dynabute_value(name: 'age')
# => 35
```

or check entire value object
```ruby
value_obj = user.dynabute_value( name: 'age' )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: 35>
value_obj.value
# => 35
```

Expand All @@ -39,6 +55,75 @@ user.dynabute_age_value.value
# => 35
```

set values for fields which can contain more then one value
```ruby
user = User.create
user.set_dynabute_value( name: 'personalities', value: 'good' )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "good">
user.set_dynabute_value( name: 'personalities', value: 'bad' )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "bad">
user.set_dynabute_value( name: 'personalities', value: 'ugly' )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "ugly">
user.save
# => true
```

get values for fields which can contain more then one value
```ruby
user = User.first
user.get_dynabute_value( name: 'personalities' )
# => ["good", "bad", "ugly"]
user.dynabute_value( name: 'personalities' )
# => [#<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "good">,
# #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "bad">,
# #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "ugly">]
```

update specific value for field with `has_many` option
```ruby
user = User.first
values_obj = user.dynabute_value( name: 'personalities' )
# => [#<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "good">,
# #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "bad">,
# #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "ugly">]
good_value_obj = values_obj.first
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "good">
bad_value_obj = values_obj.last
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "ugly">
user.set_dynabute_value( name: 'personalities', value: 'very ugly', value_id: good_value_obj.id )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "very good">
user.set_dynabute_value( name: 'personalities', value: 'very ugly', value_id: bad_value_obj.id )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "very ugly">
user.save
# => true
# in case all changes are not required to be saved within db transaction,
# each value can be saved separately.
good_value_obj.value = 'very very good'
# => "very very good"
good_value_obj.save
```

remove value
```ruby
user = User.first
user.remove_dynabute_value( name: 'age' )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: 35>
value_obj = user.dynabute_value( name: 'personalities' ).first
user.remove_dynabute_value( name: 'personalities', value_id: value_obj.id )
# => #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "good">
user.remove_dynabute_value( name: 'personalities' )
# => [#<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "bad">,
# #<Dynabute::Values::StringValue:0x0000 ... dynabutable_type: "User", value: "ugly">]
```

nested attributes of glory
```ruby
personality_field_id = User.dynabutes.find_by( name: 'personalities' ).id
Expand Down Expand Up @@ -102,6 +187,17 @@ $ rake db:migrate
```
## Contributing
#### Rspec: set test environment
```bash
$ bundle install
$ cd spec/dummy/
$ RAILS_ENV=test bundle exec rake db:create
$ RAILS_ENV=test bundle exec rake db:migrate
$ cd ../../
$ bundle exec rspec
```
yea?
## License
Expand Down
80 changes: 69 additions & 11 deletions lib/dynabute/dynabutable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

module Dynabute
module Dynabutable
class ValueNotFound < StandardError; end
extend ActiveSupport::Concern

included do
Expand Down Expand Up @@ -36,18 +37,72 @@ def dynabute_values
end

def dynabute_value(name: nil, field_id: nil, field: nil)
field = find_field(name, field_id, field)

if field.has_many
send(Util.value_relation_name(field.value_type)).select{|v| v.field_id == field.id }
field_obj = find_field(name, field_id, field)
field_values = send(Util.value_relation_name(field_obj.value_type))
field_value = if field_obj.has_many
field_values.select{ |v| v.field_id == field_obj.id }
else
send(Util.value_relation_name(field.value_type)).detect{|v| v.field_id == field.id }
field_values.detect{ |v| v.field_id == field_obj.id }
end
field_value
end

def build_dynabute_value(name: nil, field_id: nil, field: nil, **rest)
field = find_field(name, field_id, field)
send(Util.value_relation_name(field.value_type)).build(field_id: field.id, **rest)
field_obj = find_field(name, field_id, field)
send(Util.value_relation_name(field_obj.value_type)).build(field_id: field_obj.id, **rest)
end

# Returns value attribute for specified field.
# If field can have multiple values for single target model object,
# an array of values is returned, unless specific value_id is provided.
def get_dynabute_value(name: nil, field_id: nil, field: nil, value_id: nil)
field_obj = find_field(name, field_id, field)
value_obj = dynabute_value(field: field_obj)
return unless value_obj
if field_obj.has_many && value_id
value_obj = value_obj.detect{|v| v.id == value_id}
end
value_obj.is_a?(Array) ? value_obj.map(&:value) : value_obj.value
end

# Sets the value in the target model object nested attribute structure.
# If field can have multiple values for single target model object,
# a new value will be added, unless specific value_id os provided.
#
# This method does not store changes in the database. "save" method should
# be called on target model to store all changes, or individually on every
# value record returned by this method.
def set_dynabute_value(name: nil, field_id: nil, field: nil, value: nil, value_id: nil)
field_obj = find_field(name, field_id, field)
value_obj = dynabute_value(field: field_obj)
if field_obj.has_many
if value_id
value_obj = value_obj.detect{|v| v.id == value_id} if value_obj
fail ValueNotFound unless value_obj
else
value_obj = build_dynabute_value(field: field_obj)
end
else
value_obj ||= build_dynabute_value(field: field_obj)
end
value_obj.value = value
value_obj
end

# Removes value from database and keeps dynabute relations up-to-date.
# If field can have multiple values for single target model object,
# all values will be removed, unless specific value_id os provided.
#
# This method stores changes in the database
def remove_dynabute_value(name: nil, field_id: nil, field: nil, value_id: nil)
field_obj = find_field(name, field_id, field)
value_obj = dynabute_value(field: field_obj)
if value_obj && field_obj.has_many && value_id
value_obj = value_obj.detect{|v| v.id == value_id}
end
if value_obj
send(Util.value_relation_name(field_obj.value_type)).destroy(value_obj)
end
end

def method_missing(*args)
Expand All @@ -64,10 +119,13 @@ def method_missing(*args)

private
def find_field(name, id, field)
name_or_id = {name: name, id: id}.compact
return nil if name_or_id.blank? && field.blank?
field_obj = field || Dynabute::Field.find_by(name_or_id.merge(target_model: self.class.to_s))
fail Dynabute::FieldNotFound.new(name_or_id, field) if field_obj.nil?
field_obj = field
if field_obj.blank?
name_or_id = {name: name, id: id}.compact
return nil if name_or_id.blank?
field_obj = Dynabute::Field.find_by(name_or_id.merge(target_model: self.class.to_s))
fail Dynabute::FieldNotFound.new(name_or_id, field) if field_obj.nil?
end
field_obj
end

Expand Down

0 comments on commit 3b83611

Please sign in to comment.