Passing parameter to Configuration
JoeKaldas opened this issue ยท 13 comments
Is it possible to pass a parameter to the configuration?
So I have a json column I'm validating, but the keys I'm validating depend on an enum
So instead of using multiple configurations for each key in the enum, I want to use just one configuration and pass the enum key to it, since all enums have the same validations logic, just different keys
Hi @JoeKaldas!
I believe you could do it using a plain Ruby: define a module with a method that initializes the attribute and validations by its name and call this method for each attribute:
module StoreModel
module Model
def attribute(attribute_name, attribute_type)
puts "attribute defined: #{attribute_name} (#{attribute_type})"
end
def validates(attribute_name, options)
puts "validation defined: #{attribute_name} (#{options})"
end
end
end
module SharedAttributeConfig
include StoreModel::Model
def attribute_with_shared_config(attribute_name)
attribute attribute_name, :string
validates attribute_name, presence: true
end
end
class MyModel
extend SharedAttributeConfig
attribute_with_shared_config :first
attribute_with_shared_config :second
end
Thank you for your answer. However I think you misunderstood me, let me explain more.
I have a single table database, called Content and this table has an enum column that tells me the record belongs to which "table". So let's say I have 2 enum keys "team" and "project"
And then a json column in content table that has the columns of each table team/project
So let's say team has name/email (again those are dynamic and could change)
And project has title/client
Let me share a bit of the code. So this is the team's configuration file
I have a json schema that I read from where everything is defined.
So Project's configuration would be exactly the same except that I want to set "model" variable to "project" instead
So I don't want to make another configuration that has exactly the same code, just with "model" variable changed
I just want to have one configuration and pass the model variable to it
I hope it's clear
class TeamConfiguration
model = 'team'
include StoreModel::Model
ApplicationController.helpers.exclude_id_from_columns(model).each do |key, value|
attribute key.to_sym, value[:type].to_sym
end
ApplicationController.helpers.get_required_columns_by_validation(model, 'presence').each do |key, value|
validates key.to_sym, presence: true
end
end
You cannot do it out of the box using the library DSL, but my initial approach still works, you can generate there configuration classes using inheritance/composition and helper method:
# fake code to make it runnable
module StoreModel
module Model
def self.included(base)
base.extend(Module.new do
def attribute(attribute_name, attribute_type)
puts "attribute defined: #{attribute_name} (#{attribute_type})"
end
def validates(attribute_name, options)
puts "validation defined: #{attribute_name} (#{options})"
end
end)
end
end
end
module ApplicationController
module_function
def helpers
Module.new do
module_function
def exclude_id_from_columns(model)
case model
when "team" then { name: { type: :string }, email: { type: :string } }
when "project" then { name: { type: :string }, client: { type: :string } }
end
end
def get_required_columns_by_validation(model, _type)
{ name: { type: :string } }
end
end
end
end
# app code
class ContentConfiguration
include StoreModel::Model
def self.configure_as(model)
ApplicationController.helpers.exclude_id_from_columns(model).each do |key, value|
attribute key.to_sym, value[:type].to_sym
end
ApplicationController.helpers.get_required_columns_by_validation(model, 'presence').each do |key, value|
validates key.to_sym, presence: true
end
end
end
class ProjectConfiguration < ContentConfiguration
configure_as('project')
end
class TeamConfiguration < ContentConfiguration
configure_as('team')
end
Thanks again.
I did the following
- Created a configurations folder under app
- Created 3 files under this folder. One for ContentConfiguration, TeamConfiguration, Project Configuration
- Here is TeamConfiguration for example
class TeamConfiguration < ContentConfiguration
configure_as('team')
end
And I have a team model that inherits from content model and has the following
class Team < Content
attribute :data, TeamConfiguration.to_type
end
But I'm getting following error:
undefined method `type' for TeamConfiguration:Class
Thanks again
Is there a line that calls this #type
method in the stacktrace?
Log doesn't say anything. Just this
But the puts for attributes and validations are printed
NoMethodError (undefined method `to_type' for TeamConfiguration:Class):
Looks like I figured it out, the only change is in the ContentConfiguration
class (see the comment):
# fake code to make it runnable
module StoreModel
module Model
def self.included(base)
base.include(Module.new do
def attribute(attribute_name, attribute_type)
puts "attribute defined: #{attribute_name} (#{attribute_type})"
end
def validates(attribute_name, options)
puts "validation defined: #{attribute_name} (#{options})"
end
end)
end
def to_type
puts "StoreModel::Model#to_type"
end
end
end
module ApplicationController
module_function
def helpers
Module.new do
module_function
def exclude_id_from_columns(model)
case model
when "team" then { name: { type: :string }, email: { type: :string } }
when "project" then { name: { type: :string }, client: { type: :string } }
end
end
def get_required_columns_by_validation(model, _type)
{ name: { type: :string } }
end
end
end
end
# app code
class ContentConfiguration
def self.configure_as(model)
self.class.include StoreModel::Model # this line was changed
ApplicationController.helpers.exclude_id_from_columns(model).each do |key, value|
attribute key.to_sym, value[:type].to_sym
end
ApplicationController.helpers.get_required_columns_by_validation(model, 'presence').each do |key, value|
validates key.to_sym, presence: true
end
end
end
class ProjectConfiguration < ContentConfiguration
configure_as('project')
end
class TeamConfiguration < ContentConfiguration
configure_as('team')
end
Hello, now I'm getting the following error and the stacktrace
NameError (undefined local variable or method `attributes' for #<Class:0x00007fb9490c5260>)
Did you mean? attribute
Traceback (most recent call last):
35: from bin/rails:6:in `<main>'
34: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:324:in `require'
33: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:291:in `load_dependency'
32: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:324:in `block in require'
31: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
30: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
29: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
28: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
27: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
26: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/commands.rb:18:in `<main>'
25: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/command.rb:46:in `invoke'
24: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/command/base.rb:69:in `perform'
23: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch'
22: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
21: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
20: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/commands/console/console_command.rb:102:in `perform'
19: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/commands/console/console_command.rb:19:in `start'
18: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/railties-6.0.3.4/lib/rails/commands/console/console_command.rb:70:in `start'
17: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:400:in `start'
16: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:471:in `run'
15: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:471:in `catch'
14: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:472:in `block in run'
13: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:537:in `eval_input'
12: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:150:in `each_top_level_statement'
11: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:150:in `catch'
10: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:151:in `block in each_top_level_statement'
9: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:151:in `loop'
8: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:154:in `block (2 levels) in each_top_level_statement'
7: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/ruby-lex.rb:182:in `lex'
6: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:518:in `block in eval_input'
5: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:704:in `signal_status'
4: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb.rb:519:in `block (2 levels) in eval_input'
3: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/irb/input-method.rb:290:in `gets'
2: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/forwardable.rb:230:in `input='
1: from /Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/2.7.0/reline.rb:133:in `input='
/Users/joekaldas/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/store_model-0.8.0/lib/store_model/model.rb:40:in `==': undefined local variable or method `attributes' for #<Class:0x00007fb94d8bf778> (NameError)
Did you mean? attribute
Hi @DmitryTsepelev
Any help with the previous error?
Sorry, we have a loooong New Year holiday season here in Russia ๐
So it looks like something is wrong in the order of calls, and in order to debug that I'll have to rebuild the app from scratch (which is kinda time consuming), so let me explain what I tried to do and it will probably help you to fix the problem.
There is a base class (ContentConfiguration
) which has the class method configure_as
. Each child class calls the method and passes the name of the entity to it. The method does two things:
- Configures the class to be a
StoreModel
(something might be wrong here, you could try to use a regularinclude
in each child model - Configures validations according to the helper methods
It's okay! Appreciate the response :)
Yes I understand the approach you're using. Was just stuck since the error is store model related.
But I'll look into it and let you know if I reach anything.
Thanks a lot again!
@DmitryTsepelev so I figured it out. It was pretty straight forward haha
It's the same approach you suggested. Only difference is I didn't override StoreModel module
I suggest you can also add it to the readme as an inheritance feature.
I'll close the issue now. Thanks again!
module ApplicationController
module_function
def helpers
Module.new do
module_function
def exclude_id_from_columns(model)
case model
when "team" then { name: { type: :string }, email: { type: :string } }
when "project" then { name: { type: :string }, client: { type: :string } }
end
end
def get_required_columns_by_validation(model, _type)
{ name: { type: :string } }
end
end
end
end
# app code
class ContentConfiguration
include StoreModel::Model
def self.configure_as(model)
ApplicationController.helpers.exclude_id_from_columns(model).each do |key, value|
attribute key.to_sym, value[:type].to_sym
end
ApplicationController.helpers.get_required_columns_by_validation(model, 'presence').each do |key, value|
validates key.to_sym, presence: true
end
end
end
class ProjectConfiguration < ContentConfiguration
configure_as('project')
end
class TeamConfiguration < ContentConfiguration
configure_as('team')
end
Good to know! I'll be very grateful if you could make a documentation PR with your snippet