Created
February 28, 2016 03:46
-
-
Save bvandgrift/ce9464b8eb1c141dd608 to your computer and use it in GitHub Desktop.
a terrible way to determine whether attrs on an ActiveRecord model are optional or required when using grape.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module AttributeRequirements | |
extend ActiveSupport::Concern | |
SQL_TYPE_MATCHERS = { | |
/^character varying/ => String, | |
/^text/ => String, | |
/^enum/ => String, | |
/^timestamp/ => DateTime, | |
/^integer/ => Integer, | |
/^numeric/ => BigDecimal, | |
/^boolean/ => Virtus::Attribute::Boolean | |
}.freeze | |
# this should apply to active record objects only | |
included do | |
class << self | |
def build_column_type_mapping | |
begin | |
column_type_mapping = self.columns.inject({}) { |r,c| r[c.name] = c.sql_type; r } | |
rescue ActiveRecord::StatementInvalid => ex | |
Rails.logger.warn "Columns not defined: #{ex.message}" | |
return {} | |
end | |
enum_keys = defined_enums.keys | |
column_type_mapping.reduce({}) do |r, e| | |
k,v = e | |
r[k] = (enum_keys.include?(k) ? "enum" : v) | |
r | |
end | |
end | |
def column_type_mapping | |
@columns_types_memo ||= build_column_type_mapping | |
end | |
def attribute_keys | |
@columns_keys_memo ||= column_type_mapping.symbolize_keys.keys | |
end | |
def build_required_keys | |
ks = self.validators.select { |v| v.is_a? ActiveRecord::Validations::PresenceValidator } | |
.select { |v| !v.options.keys.include? :if} | |
.map(&:attributes).flatten.uniq.freeze | |
ks - [:id] | |
end | |
def required_attribute_keys | |
@req_attr_keys ||= build_required_keys.map(&:to_s).freeze | |
end | |
def optional_attribute_keys | |
@opt_attr_keys ||= (attribute_keys - required_attribute_keys).map(&:to_s).freeze | |
end | |
def coercion_for(k) | |
SQL_TYPE_MATCHERS.detect { |m,v| k.match(m) }.last | |
end | |
end | |
def required_attributes | |
enum_filter(attributes.slice(*self.class.required_attribute_keys)) | |
end | |
def enum_filter(att_hash) | |
de = defined_enums.keys | |
att_hash.reduce({}) do |r,e| | |
k,v = e | |
r[k] = (de.include?(k) ? self.send(k.to_sym) : v) | |
r | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ParamScopeHelper | |
Grape::Validations::ParamsScope.class_eval do | |
def params_for(model, opts = {}) | |
mp = model.column_type_mapping.inject({}) { |r,v| r[v.first] = model.coercion_for(v.last); r } | |
if opts[:all_optional] | |
optional_params_for(mp) | |
else | |
required_params_for(mp.slice(*model.required_attribute_keys)) | |
optional_params_for(mp.slice(*model.optional_attribute_keys)) | |
end | |
end | |
def required_params_for(mapping) | |
mapping.each do |k,v| | |
requires k, type: v | |
end | |
end | |
def optional_params_for(mapping) | |
mapping.each do |k,v| | |
optional k, type: v | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module StrongParamsHelpers | |
extend Grape::API::Helpers | |
def permitted_params | |
@permitted_params ||= declared(params, include_missing: false, include_parent_namespaces: false) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module API | |
module V1 | |
class Resource < Grape::API | |
version 'v1' | |
format :json | |
helpers StrongParamsHelpers | |
helpers ParamScopeHelper | |
resource :things do | |
# .. other stuff | |
desc "Create an Thing" | |
params do | |
requires :thing, type: Hash do |scope| | |
params_for(Thing) # magic! | |
end | |
end | |
post do | |
thing = ::Thing.new(permitted_params[:thing]) | |
error!({message: "invalid", | |
params: params, | |
errors: thing.errors.messages}, | |
422) unless (thing.valid? && thing.save!) | |
serialize_model(thing) | |
end | |
# more thing stuff | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment