diff --git a/lib/paradocs.rb b/lib/paradocs.rb index 82eb17a..17cc3b1 100644 --- a/lib/paradocs.rb +++ b/lib/paradocs.rb @@ -1,11 +1,11 @@ -require "paradocs/version" -require "paradocs/support" -require "paradocs/registry" -require "paradocs/field" -require "paradocs/results" -require "paradocs/schema" -require "paradocs/context" -require "paradocs/base_policy" +require 'paradocs/version' +require 'paradocs/support' +require 'paradocs/registry' +require 'paradocs/field' +require 'paradocs/results' +require 'paradocs/schema' +require 'paradocs/context' +require 'paradocs/base_policy' require 'ostruct' module Paradocs @@ -19,17 +19,17 @@ def self.policy(name, plcy = nil, &block) def self.config @config ||= OpenStruct.new( - explicit_errors: false, - whitelisted_keys: [], + explicit_errors: false, + whitelisted_keys: [], default_schema_name: :schema, - meta_prefix: "_", - whitelist_coercion: nil + meta_prefix: '_', + whitelist_coercion: nil ) end def self.configure - yield self.config if block_given? - self.config + yield config if block_given? + config end end diff --git a/lib/paradocs/base_policy.rb b/lib/paradocs/base_policy.rb index e5725bc..9d45dd7 100644 --- a/lib/paradocs/base_policy.rb +++ b/lib/paradocs/base_policy.rb @@ -32,7 +32,7 @@ def self.meta_data(&block) @meta_data_block end - %w(error silent_error).each do |name| + %w[error silent_error].each do |name| getter = "#{name}s" define_singleton_method(getter) do parent_errors = superclass.respond_to?(getter) ? superclass.send(getter) : [] @@ -49,9 +49,10 @@ def self.meta_data(&block) next end next unless ex.is_a?(Class) || ex < StandardError + only_errors << ex end - instance_variable_set("@#{getter}", ((self.public_send(getter) || []) + only_errors).uniq) + instance_variable_set("@#{getter}", ((public_send(getter) || []) + only_errors).uniq) end define_method(getter) do @@ -59,12 +60,12 @@ def self.meta_data(&block) end end - def self.policy_name=(name) - @policy_name = name + class << self + attr_writer :policy_name end def self.policy_name - @policy_name || self.name.split("::").last.downcase.to_sym + @policy_name || name.split('::').last.downcase.to_sym end attr_accessor :environment @@ -89,6 +90,7 @@ def valid?(value, key, payload) def meta_data return self.class.meta_data.call(*init_params) if self.class.meta_data + meta end @@ -97,7 +99,7 @@ def validate(*args) end def policy_name - (self.class.policy_name || self.to_s.demodulize.underscore).to_sym + (self.class.policy_name || to_s.demodulize.underscore).to_sym end def message @@ -107,13 +109,13 @@ def message protected def validate(*args) - (self.class.validate || ->(*args) { true }).call(*args) + (self.class.validate || ->(*_args) { true }).call(*args) end private def meta - @meta = {self.class.policy_name => {errors: self.class.errors}} + @meta = { self.class.policy_name => { errors: self.class.errors } } end def init_params diff --git a/lib/paradocs/context.rb b/lib/paradocs/context.rb index 23f8246..28426d7 100644 --- a/lib/paradocs/context.rb +++ b/lib/paradocs/context.rb @@ -14,7 +14,7 @@ def add_error(key, msg) class Context attr_reader :environment - def initialize(path=nil, top=Top.new, environment={}, subschemes={}) + def initialize(path = nil, top = Top.new, environment = {}, subschemes = {}) @top = top @path = Array(path).compact @environment = environment @@ -24,6 +24,7 @@ def initialize(path=nil, top=Top.new, environment={}, subschemes={}) def subschema(subschema_name) subschema = @subschemes[subschema_name] return unless subschema + @subschemes.merge!(subschema.subschemes) subschema end @@ -41,14 +42,13 @@ def sub(key) end protected + attr_reader :path, :top def string_path - path.reduce(['$']) do |m, segment| + path.each_with_object(['$']) do |segment, m| m << (segment.is_a?(Integer) ? "[#{segment}]" : ".#{segment}") - m end.join end end - end diff --git a/lib/paradocs/default_types.rb b/lib/paradocs/default_types.rb index b19469e..2dc8bf0 100644 --- a/lib/paradocs/default_types.rb +++ b/lib/paradocs/default_types.rb @@ -1,44 +1,44 @@ -require "date" +require 'date' module Paradocs # type coercions Paradocs.policy :integer do - coerce do |v, k, c| + coerce do |v, _k, _c| v.to_i end meta_data do - {type: :integer} + { type: :integer } end end Paradocs.policy :number do - coerce do |v, k, c| + coerce do |v, _k, _c| v.to_f end meta_data do - {type: :number} + { type: :number } end end Paradocs.policy :string do - coerce do |v, k, c| + coerce do |v, _k, _c| v.to_s end meta_data do - {type: :string} + { type: :string } end end Paradocs.policy :boolean do - coerce do |v, k, c| + coerce do |v, _k, _c| !!v end meta_data do - {type: :boolean} + { type: :boolean } end end @@ -53,7 +53,7 @@ module Paradocs end meta_data do - {type: :array} + { type: :array } end end @@ -65,21 +65,21 @@ module Paradocs validate do |value, key, payload| !payload.key?(key) || value.respond_to?(:[]) && - value.respond_to?(:key?) + value.respond_to?(:key?) end meta_data do - {type: :object} + { type: :object } end end Paradocs.policy :datetime do - coerce do |v, k, c| + coerce do |v, _k, _c| DateTime.parse(v.to_s) end meta_data do - {type: :datetime} + { type: :datetime } end end end diff --git a/lib/paradocs/dsl.rb b/lib/paradocs/dsl.rb index 99c3131..1dcd252 100644 --- a/lib/paradocs/dsl.rb +++ b/lib/paradocs/dsl.rb @@ -1,4 +1,4 @@ -require "paradocs" +require 'paradocs' module Paradocs module DSL @@ -25,7 +25,7 @@ module DSL def self.included(base) base.extend(ClassMethods) - base.schemas = {Paradocs.config.default_schema_name => Paradocs::Schema.new} + base.schemas = { Paradocs.config.default_schema_name => Paradocs::Schema.new } end module ClassMethods @@ -48,10 +48,10 @@ def schema(*args, &block) key = args.first.is_a?(Symbol) ? args.shift : Paradocs.config.default_schema_name current_schema = @schemas.fetch(key) { Paradocs::Schema.new } new_schema = if block_given? || options.any? - Paradocs::Schema.new(options, &block) - elsif args.first.is_a?(Paradocs::Schema) - args.first - end + Paradocs::Schema.new(options, &block) + elsif args.first.is_a?(Paradocs::Schema) + args.first + end return current_schema unless new_schema diff --git a/lib/paradocs/extensions/payload_builder.rb b/lib/paradocs/extensions/payload_builder.rb index bc05ac8..b5cde23 100644 --- a/lib/paradocs/extensions/payload_builder.rb +++ b/lib/paradocs/extensions/payload_builder.rb @@ -19,8 +19,10 @@ def build_simple_structure(struct, &block) struct.map do |key, value| key = key.to_s next if key.start_with?(Paradocs.config.meta_prefix) # skip all the meta fields + ex_value = restore_one(key, value, &block) next if ex_value == @skip_word + key = value[:alias] || key [key.to_s, ex_value] end.compact.to_h @@ -29,15 +31,16 @@ def build_simple_structure(struct, &block) def restore_one(key, value, &block) default = value[:default] ex_value = if value[:structure] - data = build_simple_structure(value[:structure], &block) - value[:type] == :array ? [data] : data - elsif default - default.is_a?(Proc) ? default.call : default - elsif value[:options] && !value[:options].empty? - options = value[:options] - value[:type] == :array ? options : options.sample - end + data = build_simple_structure(value[:structure], &block) + value[:type] == :array ? [data] : data + elsif default + default.is_a?(Proc) ? default.call : default + elsif value[:options] && !value[:options].empty? + options = value[:options] + value[:type] == :array ? options : options.sample + end return ex_value unless block_given? + yield(key, value, ex_value, @skip_word) end end diff --git a/lib/paradocs/extensions/structure.rb b/lib/paradocs/extensions/structure.rb index dcd57ed..b32cf45 100644 --- a/lib/paradocs/extensions/structure.rb +++ b/lib/paradocs/extensions/structure.rb @@ -2,20 +2,20 @@ module Paradocs module Extensions class Structure DEFAULT = :generic - %w(errors subschemes).each do |key| + %w[errors subschemes].each do |key| define_method(key) { "#{Paradocs.config.meta_prefix}#{key}".to_sym } end attr_reader :schema, :ignore_transparent, :root attr_accessor :ignore_transparent - def initialize(schema, ignore_transparent=true, root="") + def initialize(schema, ignore_transparent = true, root = '') @schema = schema @ignore_transparent = ignore_transparent @root = root end def nested(&block) - schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), result| + schema.fields.each_with_object({ errors => [], subschemes => {} }) do |(_, field), result| meta, sc = collect_meta(field, root) if sc meta[:structure] = self.class.new(sc, ignore_transparent, meta[:json_path]).nested(&block) @@ -29,6 +29,7 @@ def nested(&block) yield(field_key, meta) if block_given? next unless field.mutates_schema? + schema.subschemes.each do |name, subschema| result[subschemes][name] = self.class.new(subschema, ignore_transparent, root).nested(&block) result[errors] += result[subschemes][name][errors] @@ -40,17 +41,20 @@ def all_nested(&block) all_flatten(&block).each_with_object({}) do |(name, struct), obj| obj[name] = {} # sort the flatten struct to have iterated 1lvl keys before 2lvl and so on... - struct.sort_by { |k, v| k.to_s.count(".") }.each do |key, value| + struct.sort_by { |k, _v| k.to_s.count('.') }.each do |key, value| target = obj[name] - key, value = key.to_s, value.clone # clone the values, because we do mutation below + key = key.to_s + value = value.clone # clone the values, because we do mutation below value.merge!(nested_name: key) if value.respond_to?(:merge) # it can be array (_errors) next target[key.to_sym] = value if key.start_with?(Paradocs.config.meta_prefix) # copy meta fields - parts = key.split(".") + parts = key.split('.') next target[key] ||= value if parts.size == 1 # copy 1lvl key + parts.each.with_index do |subkey, index| target[subkey] ||= value next if parts.size == index + 1 + target[subkey][:structure] ||= {} target = target[subkey][:structure] # target goes deeper for each part end @@ -58,11 +62,11 @@ def all_nested(&block) end end - def all_flatten(schema_structure=nil, &block) + def all_flatten(schema_structure = nil, &block) schema_structure ||= flatten(&block) if schema_structure[subschemes].empty? schema_structure.delete(subschemes) # don't include redundant key - return {DEFAULT => schema_structure} + return { DEFAULT => schema_structure } end schema_structure[subschemes].each_with_object({}) do |(name, subschema), result| if subschema[subschemes].empty? @@ -80,7 +84,7 @@ def all_flatten(schema_structure=nil, &block) end def flatten(&block) - schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), obj| + schema.fields.each_with_object({ errors => [], subschemes => {} }) do |(_, field), obj| meta, sc = collect_meta(field, root) humanized_name = meta.delete(:nested_name) obj[humanized_name] = meta unless ignore_transparent && field.transparent? @@ -95,6 +99,7 @@ def flatten(&block) end yield(humanized_name, meta) if block_given? next unless field.mutates_schema? + schema.subschemes.each do |name, subschema| obj[subschemes][name] ||= self.class.new(subschema, ignore_transparent, root).flatten(&block) obj[errors] += obj[subschemes][name][errors] @@ -102,7 +107,7 @@ def flatten(&block) end end - private + private def collect_meta(field, root) field_key = field.meta_data[:alias] || field.key @@ -110,8 +115,8 @@ def collect_meta(field, root) meta = field.meta_data.merge(json_path: json_path) sc = meta.delete(:schema) meta[:mutates_schema] = true if meta.delete(:mutates_schema) - json_path << "[]" if meta[:type] == :array - meta[:nested_name] = json_path.gsub("[]", "")[2..-1] + json_path << '[]' if meta[:type] == :array + meta[:nested_name] = json_path.gsub('[]', '')[2..-1] [meta, sc] end end diff --git a/lib/paradocs/field.rb b/lib/paradocs/field.rb index 51f49a9..f425965 100644 --- a/lib/paradocs/field.rb +++ b/lib/paradocs/field.rb @@ -1,4 +1,4 @@ -require "paradocs/field_dsl" +require 'paradocs/field_dsl' module Paradocs class Field @@ -28,12 +28,12 @@ def possible_errors def default(value) meta default: value - @default_block = (value.respond_to?(:call) ? value : ->(key, payload, context) { value }) + @default_block = (value.respond_to?(:call) ? value : ->(_key, _payload, _context) { value }) self end def mutates_schema!(&block) - @mutation_block ||= block if block_given? + @mutation_block ||= block if block_given? @expects_mutation = @expects_mutation.nil? && true meta mutates_schema: @mutation_block @mutation_block @@ -55,11 +55,11 @@ def policy(key, *args) self end - alias_method :type, :policy - alias_method :rule, :policy + alias type policy + alias rule policy def schema(sc = nil, &block) - sc = (sc ? sc : Schema.new(&block)) + sc = (sc || Schema.new(&block)) meta schema: sc policy sc.schema end @@ -71,7 +71,7 @@ def transparent? def visit(meta_key = nil, &visitor) if sc = meta_data[:schema] r = sc.visit(meta_key, &visitor) - (meta_data[:type] == :array) ? [r] : r + meta_data[:type] == :array ? [r] : r else meta_key ? meta_data[meta_key] : yield(self) end @@ -116,30 +116,30 @@ def resolve(payload, context) end private + attr_reader :policies, :default_block def resolve_one(policy, value, payload, context) - begin - value = policy.coerce(value, key, context) - valid = policy.valid?(value, key, payload) - - context.add_error(policy.message) unless valid - [value, valid] - rescue *(policy.try(:errors) || []) => e - # context.add_error e.message # NOTE: do we need it? - raise e - rescue *(policy.try(:silent_errors) || []) => e - context.add_error e.message - rescue StandardError => e - raise e if policy.is_a? Paradocs::Schema # from the inner level, just reraise - if Paradocs.config.explicit_errors - error = ConfigurationError.new("<#{e.class}:#{e.message}> should be registered in the policy") - error.set_backtrace(e.backtrace) - raise error - end - context.add_error policy.message unless Paradocs.config.explicit_errors - [value, false] + value = policy.coerce(value, key, context) + valid = policy.valid?(value, key, payload) + + context.add_error(policy.message) unless valid + [value, valid] + rescue *(policy.try(:errors) || []) => e + # context.add_error e.message # NOTE: do we need it? + raise e + rescue *(policy.try(:silent_errors) || []) => e + context.add_error e.message + rescue StandardError => e + raise e if policy.is_a? Paradocs::Schema # from the inner level, just reraise + + if Paradocs.config.explicit_errors + error = ConfigurationError.new("<#{e.class}:#{e.message}> should be registered in the policy") + error.set_backtrace(e.backtrace) + raise error end + context.add_error policy.message unless Paradocs.config.explicit_errors + [value, false] end def has_default? @@ -150,6 +150,7 @@ def lookup(key, args) obj = key.is_a?(Symbol) ? Paradocs.registry.policies[key] : key raise ConfigurationError, "No policies defined for #{key.inspect}" unless obj + obj.respond_to?(:new) ? obj.new(*args) : obj end end diff --git a/lib/paradocs/policies.rb b/lib/paradocs/policies.rb index 723479d..c981c01 100644 --- a/lib/paradocs/policies.rb +++ b/lib/paradocs/policies.rb @@ -5,12 +5,12 @@ module Policies class Format < Paradocs::BasePolicy attr_reader :message - def initialize(fmt, msg="invalid format") + def initialize(fmt, msg = 'invalid format') @message = msg @fmt = fmt end - def eligible?(value, key, payload) + def eligible?(_value, key, payload) payload.key?(key) end @@ -20,12 +20,12 @@ def validate(value, key, payload) end class Split < Paradocs::BasePolicy - def initialize(delimiter=/\s*,\s*/) + def initialize(delimiter = /\s*,\s*/) @delimiter = delimiter end def coerce(v, *) - v.kind_of?(Array) ? v : v.to_s.split(@delimiter) + v.is_a?(Array) ? v : v.to_s.split(@delimiter) end end end @@ -38,13 +38,13 @@ def coerce(v, *) Paradocs.policy :email, Policies::Format.new(EMAIL_REGEXP, 'invalid email') Paradocs.policy :noop do - eligible do |value, key, payload| + eligible do |_value, _key, _payload| true end end Paradocs.policy :declared do - eligible do |value, key, payload| + eligible do |_value, key, payload| payload.key? key end @@ -55,30 +55,30 @@ def coerce(v, *) Paradocs.policy :whitelisted do meta_data do - {whitelisted: true} + { whitelisted: true } end end Paradocs.policy :required do message do |*| - "is required" + 'is required' end - validate do |value, key, payload| + validate do |_value, key, payload| payload.try(:key?, key) end meta_data do - {required: true} + { required: true } end end Paradocs.policy :present do message do |*| - "is required and value must be present" + 'is required and value must be present' end - validate do |value, key, payload| + validate do |value, _key, _payload| case value when String value.strip != '' @@ -90,15 +90,15 @@ def coerce(v, *) end meta_data do - {present: true} + { present: true } end end { - gte: [:>=, "greater than or equal to"], - lte: [:<=, "less than or equal to"], - gt: [:>, "strictly greater than"], - lt: [:<, "strictly less than"] + gte: [:>=, 'greater than or equal to'], + lte: [:<=, 'less than or equal to'], + gt: [:>, 'strictly greater than'], + lt: [:<, 'strictly less than'] }.each do |policy, (comparison, text)| klass = Class.new(Paradocs::BasePolicy) do attr_reader :limit @@ -111,7 +111,8 @@ def coerce(v, *) end define_method(:validate) do |value, key, _| - @key, @value = key, value + @key = key + @value = value value.send(comparison, limit) end @@ -133,7 +134,7 @@ def coerce(v, *) "must be one of #{options.join(', ')}, but got #{actual}" end - eligible do |options, actual, key, payload| + eligible do |_options, _actual, key, payload| payload.key?(key) end @@ -142,25 +143,25 @@ def coerce(v, *) end meta_data do |opts| - {options: opts} + { options: opts } end def ok?(options, actual) - [actual].flatten.all?{|v| options.include?(v)} + [actual].flatten.all? { |v| options.include?(v) } end end Paradocs.policy :length do COMPARISONS = { - max: [:<=, "maximum"], - min: [:>=, "minimum"], - eq: [:==, "exactly"] + max: [:<=, 'maximum'], + min: [:>=, 'minimum'], + eq: [:==, 'exactly'] }.freeze - message do |options, actual, key| - "value must be " + options.each_with_object([]) do |(comparison, limit), obj| + message do |options, _actual, _key| + 'value must be ' + options.each_with_object([]) do |(comparison, limit), obj| obj << "#{COMPARISONS[comparison].last} #{limit} characters" - end.join(", ") + end.join(', ') end validate do |options, actual, key, payload| @@ -168,7 +169,7 @@ def ok?(options, actual) end meta_data do |opts| - {length: opts} + { length: opts } end def ok?(options, actual) diff --git a/lib/paradocs/registry.rb b/lib/paradocs/registry.rb index 9c896c6..979966c 100644 --- a/lib/paradocs/registry.rb +++ b/lib/paradocs/registry.rb @@ -21,22 +21,26 @@ def policy(name, plcy = nil, &block) self end - private + private def validate_policy_class(plcy) plcy_cls = plcy.is_a?(Class) ? plcy : plcy.class if plcy_cls < Paradocs::BasePolicy valid_overriden = plcy_cls.instance_method(:valid?).source_location != Paradocs::BasePolicy.instance_method(:valid?).source_location - raise ConfigurationError.new("Overriding #valid? in #{plcy_cls} is forbidden. Override #validate instead") if valid_overriden + if valid_overriden + raise ConfigurationError, "Overriding #valid? in #{plcy_cls} is forbidden. Override #validate instead" + end else - required_methods = [:valid?, :coerce, :eligible?, :meta_data, :policy_name] - plcy_cls.instance_methods - raise ConfigurationError.new("Policy #{plcy_cls} should respond to #{required_methods}") unless required_methods.empty? + required_methods = %i[valid? coerce eligible? meta_data policy_name] - plcy_cls.instance_methods + unless required_methods.empty? + raise ConfigurationError, "Policy #{plcy_cls} should respond to #{required_methods}" + end return plcy unless Paradocs.config.explicit_errors return plcy if plcy_cls.respond_to?(:errors) - raise ConfigurationError.new("Policy #{plcy_cls} should respond to .errors method") + + raise ConfigurationError, "Policy #{plcy_cls} should respond to .errors method" end end end end - diff --git a/lib/paradocs/results.rb b/lib/paradocs/results.rb index 59c29dc..ee159bc 100644 --- a/lib/paradocs/results.rb +++ b/lib/paradocs/results.rb @@ -3,7 +3,9 @@ class Results attr_reader :output, :errors, :environment def initialize(output, errors, environment) - @output, @errors, @environment = output, errors, environment + @output = output + @errors = errors + @environment = environment end def valid? diff --git a/lib/paradocs/schema.rb b/lib/paradocs/schema.rb index 165713a..9b6f19e 100644 --- a/lib/paradocs/schema.rb +++ b/lib/paradocs/schema.rb @@ -1,14 +1,14 @@ -require "paradocs/context" -require "paradocs/results" -require "paradocs/field" -require "paradocs/extensions/structure" -require "paradocs/extensions/payload_builder" +require 'paradocs/context' +require 'paradocs/results' +require 'paradocs/field' +require 'paradocs/extensions/structure' +require 'paradocs/extensions/payload_builder' module Paradocs class Schema attr_accessor :environment attr_reader :subschemes, :structure_builder - def initialize(options={}, &block) + def initialize(options = {}, &block) @options = options @fields = {} @subschemes = {} @@ -55,14 +55,14 @@ def subschema(*args, &block) name = args.first.is_a?(Symbol) ? args.shift : Paradocs.config.default_schema_name current_schema = subschemes.fetch(name) { self.class.new } new_schema = if block_given? - sc = self.class.new(options) - sc.definitions << block - sc - elsif args.first.is_a?(self.class) - args.first - else - self.class.new(options) - end + sc = self.class.new(options) + sc.definitions << block + sc + elsif args.first.is_a?(self.class) + args.first + else + self.class.new(options) + end subschemes[name] = current_schema.merge(new_schema) end @@ -108,16 +108,16 @@ def copy_into(instance) subschemes.each { |name, subsc| instance.subschema(name, subsc) } - instance.ignore *ignored_field_keys + instance.ignore(*ignored_field_keys) instance end def field(field_or_key) - f, key = if field_or_key.kind_of?(Field) - [field_or_key, field_or_key.key] - else - [Field.new(field_or_key), field_or_key.to_sym] - end + f, key = if field_or_key.is_a?(Field) + [field_or_key, field_or_key.key] + else + [Field.new(field_or_key), field_or_key.to_sym] + end if ignored_field_keys.include?(f.key) f @@ -131,14 +131,14 @@ def expand(exp, &block) self end - def resolve(payload, environment={}) + def resolve(payload, environment = {}) @environment = environment context = Context.new(nil, Top.new, @environment, subschemes) output = coerce(payload, nil, context) Results.new(output, context.errors, @environment) end - def eligible?(value, key, payload) + def eligible?(_value, key, payload) payload.key? key end @@ -191,10 +191,12 @@ def invoke_subschemes!(payload, context, flds: fields) # recoursive definitions call depending on payload flds.clone.each_pair do |_, field| next unless field.expects_mutation? + subschema_name = field.subschema_for_mutation(payload, context.environment) subschema = subschemes[subschema_name] || context.subschema(subschema_name) next unless subschema # or may be raise error? - subschema.definitions.each { |block| self.instance_exec(&block) } + + subschema.definitions.each { |block| instance_exec(&block) } invoked_any = true end # if definitions are applied new subschemes may appear, apply them until they end @@ -212,9 +214,11 @@ def resolve_expansions(payload, into, context) payload.each do |key, value| match = exp.match(key.to_s) next unless match + fld = MatchContext.new.instance_exec(match, &block) next unless fld - into.update(coerce_one({fld.key => value}, context, flds: {fld.key => apply_default_field_policies_to(fld)})) + + into.update(coerce_one({ fld.key => value }, context, flds: { fld.key => apply_default_field_policies_to(fld) })) end end @@ -222,13 +226,14 @@ def resolve_expansions(payload, into, context) end def apply_default_field_policies_to(field) - default_field_policies.reduce(field) {|f, policy_name| f.policy(policy_name) } + default_field_policies.reduce(field) { |f, policy_name| f.policy(policy_name) } end def apply! return if @applied + definitions.each do |d| - self.instance_exec(options, &d) + instance_exec(options, &d) end @applied = true end diff --git a/lib/paradocs/struct.rb b/lib/paradocs/struct.rb index c9895bf..de61659 100644 --- a/lib/paradocs/struct.rb +++ b/lib/paradocs/struct.rb @@ -31,7 +31,7 @@ def errors _results.errors end - # returns a shallow copy. + #  returns a shallow copy. def to_h _results.output.clone end @@ -45,12 +45,14 @@ def merge(attrs = {}) end private + attr_reader :_graph, :_results module ClassMethods def new!(attrs = {}, environment = {}) st = new(attrs, environment) - raise InvalidStructError.new(st) unless st.valid? + raise InvalidStructError, st unless st.valid? + st end @@ -70,7 +72,7 @@ def build(attrs) end end - def paradocs_build_class_for_child(key, child_schema) + def paradocs_build_class_for_child(_key, child_schema) klass = Class.new do include Struct end @@ -86,14 +88,14 @@ def wrap(key, value) when Hash # find constructor for field cons = field.meta_data[:schema] - if cons.kind_of?(Paradocs::Schema) + if cons.is_a?(Paradocs::Schema) klass = paradocs_build_class_for_child(key, cons) klass.paradocs_after_define_schema(cons) cons = klass end cons ? cons.new(value) : value.freeze when Array - value.map{|v| wrap(key, v) }.freeze + value.map { |v| wrap(key, v) }.freeze else value.freeze end diff --git a/lib/paradocs/support.rb b/lib/paradocs/support.rb index d50bb66..ae814b1 100644 --- a/lib/paradocs/support.rb +++ b/lib/paradocs/support.rb @@ -1,4 +1,4 @@ -require "delegate" +require 'delegate' module Support module Tryable #:nodoc: diff --git a/lib/paradocs/version.rb b/lib/paradocs/version.rb index 33cbed2..be420e6 100644 --- a/lib/paradocs/version.rb +++ b/lib/paradocs/version.rb @@ -1,3 +1,3 @@ module Paradocs - VERSION = "1.1.6" + VERSION = '1.1.6'.freeze end diff --git a/lib/paradocs/whitelist.rb b/lib/paradocs/whitelist.rb index 2f9bc7e..a9b887a 100644 --- a/lib/paradocs/whitelist.rb +++ b/lib/paradocs/whitelist.rb @@ -16,8 +16,8 @@ module Whitelist # params = {title: "title", age: 25} # foo.filter!(params, schema) # => {title: "title", age: "[FILTERED]"} # - FILTERED = "[FILTERED]" - EMPTY = "[EMPTY]" + FILTERED = '[FILTERED]'.freeze + EMPTY = '[EMPTY]'.freeze def self.included(base) base.include(ClassMethods) @@ -56,12 +56,12 @@ def resolve(payload, schema, context) else value = if whitelisted?(meta, key) - coercion_block ? coercion_block.call(value, meta) : value - elsif value.nil? || value.try(:blank?) || value.try(:empty?) - !!value == value ? value : EMPTY - else - FILTERED - end + coercion_block ? coercion_block.call(value, meta) : value + elsif value.nil? || value.try(:blank?) || value.try(:empty?) + !!value == value ? value : EMPTY + else + FILTERED + end value end filtered_payload[key] = value @@ -85,6 +85,7 @@ def get_meta_data(schema, key) return {} unless schema.respond_to?(:fields) return {} unless schema.fields[key] return {} unless schema.fields[key].respond_to?(:meta_data) + meta_data = schema.fields[key].meta_data || {} end end diff --git a/spec/custom_block_validator_spec.rb b/spec/custom_block_validator_spec.rb index 818e04e..d7a95c7 100644 --- a/spec/custom_block_validator_spec.rb +++ b/spec/custom_block_validator_spec.rb @@ -2,7 +2,7 @@ describe 'custom block validator' do Paradocs.policy :validate_if do - eligible do |options, value, key, payload| + eligible do |options, _value, _key, payload| options.all? do |key, value| payload[key] == value end @@ -16,72 +16,73 @@ end expect(schema.resolve(age: 30).errors.any?).to be false - expect(schema.resolve(age: 40).errors.any?).to be true # name is missing + expect(schema.resolve(age: 40).errors.any?).to be true #  name is missing end before do expect(Paradocs.registry.policies.values.map(&:policy_name)).to all(be) end - describe "error handling" do + describe 'error handling' do Paradocs.policy :strict_validation do - register_error ArgumentError - register_silent_error RuntimeError - - validate do |value, key, payload| - raise ArgumentError.new("test") if value > 50 - raise RuntimeError.new("value should not exceed 30") if value > 30 - true - end - - coerce do |value, key, context| - value / 0 if value > 100 - value - end + register_error ArgumentError + register_silent_error RuntimeError + + validate do |value, _key, _payload| + raise ArgumentError, 'test' if value > 50 + raise 'value should not exceed 30' if value > 30 + + true + end + + coerce do |value, _key, _context| + value / 0 if value > 100 + value end + end - let(:schema) do - Paradocs::Schema.new do - field(:age).type(:integer).policy(:strict_validation) - end + let(:schema) do + Paradocs::Schema.new do + field(:age).type(:integer).policy(:strict_validation) end + end - context "with disabled explicit errors" do - it "works fine if value is valid" do + context 'with disabled explicit errors' do + it 'works fine if value is valid' do expect(schema.resolve(age: 20).errors.any?).to be false end - it "catches silent errors and uses the error.message as validation failure message" do - expect(schema.resolve(age: 31).errors).to eq({"$.age" => ["value should not exceed 30"]}) + it 'catches silent errors and uses the error.message as validation failure message' do + expect(schema.resolve(age: 31).errors).to eq({ '$.age' => ['value should not exceed 30'] }) end - it "raises the very registered error to the highest level" do - expect { schema.resolve(age: 51) }.to raise_error(ArgumentError).with_message("test") + it 'raises the very registered error to the highest level' do + expect { schema.resolve(age: 51) }.to raise_error(ArgumentError).with_message('test') end - it "catches unregistered error and uses the policy.message as validation failure message" do - expect(schema.resolve(age: 101).errors).to eq({"$.age" => ["is invalid"]}) + it 'catches unregistered error and uses the policy.message as validation failure message' do + expect(schema.resolve(age: 101).errors).to eq({ '$.age' => ['is invalid'] }) end end - context "with enabled explicit errors" do + context 'with enabled explicit errors' do before { allow(Paradocs.config).to receive(:explicit_errors) { true } } - it "works fine if value is valid" do + it 'works fine if value is valid' do expect(schema.resolve(age: 20).errors.any?).to be false end - it "catches silent errors and uses the error.message as validation failure message" do - expect(schema.resolve(age: 31).errors).to eq({"$.age" => ["value should not exceed 30"]}) + it 'catches silent errors and uses the error.message as validation failure message' do + expect(schema.resolve(age: 31).errors).to eq({ '$.age' => ['value should not exceed 30'] }) end - it "raises the very registered error to the highest level" do - expect { schema.resolve(age: 51) }.to raise_error(ArgumentError).with_message("test") + it 'raises the very registered error to the highest level' do + expect { schema.resolve(age: 51) }.to raise_error(ArgumentError).with_message('test') end - it "catches unregistered error and raises Configuration error" do + it 'catches unregistered error and raises Configuration error' do expect { schema.resolve(age: 101).errors }.to raise_error(Paradocs::ConfigurationError) - .with_message(" should be registered in the policy") + .with_message(' should be registered in the policy') end end end diff --git a/spec/custom_validator.rb b/spec/custom_validator.rb index b1cc78b..ce01e04 100644 --- a/spec/custom_validator.rb +++ b/spec/custom_validator.rb @@ -1,13 +1,13 @@ -require "spec_helper" +require 'spec_helper' -describe "custom validator" do +describe 'custom validator' do let(:validator) do class PresentIf < Paradocs::BasePolicy - def initialize(condition) + def initialize(_condition) @condition = @condition end - def validate(value, key, payload) + def validate(value, key, _payload) value.present? && key.present? end @@ -18,7 +18,7 @@ def eligible?(value, key, payload) PresentIf end - describe "registration" do + describe 'registration' do it "doesn't raise error if validator is built properly" do expect { Paradocs.policy :present_if, validator }.to_not raise_error expect(Paradocs.registry.policies[:present_if]).to eq(validator) @@ -29,7 +29,7 @@ def eligible?(value, key, payload) .with_message(/Policy .* should respond to \[:valid\?, :coerce, :eligible\?, :meta_data, :policy_name\]/) end - it "raises ConfigrationError if child validator class overrides #valid? method" do + it 'raises ConfigrationError if child validator class overrides #valid? method' do expect do Paradocs.policy( :bad_validator, @@ -41,15 +41,19 @@ def eligible?(value, key, payload) .with_message(/Overriding #valid\? in .* is forbidden\. Override #validate instead/) end - context "with enabled explicit_errors" do + context 'with enabled explicit_errors' do before { allow(Paradocs.config).to receive(:explicit_errors) { true } } it "raises ConfigurationError if custom validator doesn't implement .errors method" do validator = Class.new do def valid?; end + def coerce; end + def eligible?; end + def meta_data; end + def policy_name; end end diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index 7e7caaa..7f61a00 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -require "paradocs/dsl" +require 'paradocs/dsl' -describe "classes including DSL module" do +describe 'classes including DSL module' do class Parent include Paradocs::DSL @@ -31,56 +31,56 @@ class GrandChild < Child schema(age_type: :integer) end - describe "#schema" do - let(:input) { + describe '#schema' do + let(:input) do { - title: "A title", + title: 'A title', age: 38, - description: "A description" + description: 'A description' } - } + end it "merges parent's schema into child's" do parent_output = Parent.schema.resolve(input).output child_output = Child.schema.resolve(input).output - expect(parent_output.keys).to match_array([:title, :age]) - expect(parent_output[:title]).to eq "A title" + expect(parent_output.keys).to match_array(%i[title age]) + expect(parent_output[:title]).to eq 'A title' expect(parent_output[:age]).to eq 38 - expect(child_output.keys).to match_array([:title, :age, :description]) - expect(child_output[:title]).to eq "A title" - expect(child_output[:age]).to eq "38" - expect(child_output[:description]).to eq "A description" + expect(child_output.keys).to match_array(%i[title age description]) + expect(child_output[:title]).to eq 'A title' + expect(child_output[:age]).to eq '38' + expect(child_output[:description]).to eq 'A description' # named schema parent_output = Parent.schema(:extras).resolve(search: 10, query: 'foo').output child_output = Child.schema(:extras).resolve(search: 10, query: 'foo').output expect(parent_output.keys).to match_array([:search]) - expect(parent_output[:search]).to eq "10" - expect(child_output.keys).to match_array([:search, :query]) - expect(child_output[:search]).to eq "10" - expect(child_output[:query]).to eq "foo" + expect(parent_output[:search]).to eq '10' + expect(child_output.keys).to match_array(%i[search query]) + expect(child_output[:search]).to eq '10' + expect(child_output[:query]).to eq 'foo' end - it "inherits options" do + it 'inherits options' do grand_child_output = GrandChild.schema.resolve(input).output - expect(grand_child_output.keys).to match_array([:title, :age, :description]) - expect(grand_child_output[:title]).to eq "A title" + expect(grand_child_output.keys).to match_array(%i[title age description]) + expect(grand_child_output[:title]).to eq 'A title' expect(grand_child_output[:age]).to eq 38 - expect(grand_child_output[:description]).to eq "A description" + expect(grand_child_output[:description]).to eq 'A description' # named schema - grand_child_output = GrandChild.schema(:extras).resolve(search: "100", query: "bar").output - expect(grand_child_output.keys).to match_array([:search, :query]) + grand_child_output = GrandChild.schema(:extras).resolve(search: '100', query: 'bar').output + expect(grand_child_output.keys).to match_array(%i[search query]) expect(grand_child_output[:search]).to eq 100 end end - describe "inheriting schema policy" do - let!(:a) { + describe 'inheriting schema policy' do + let!(:a) do Class.new do include Paradocs::DSL @@ -88,23 +88,23 @@ class GrandChild < Child field(:title).policy(:string) end end - } + end - let!(:b) { + let!(:b) do Class.new(a) - } + end - it "inherits policy" do + it 'inherits policy' do results = a.schema.resolve({}) - expect(results.errors["$.title"]).not_to be_empty + expect(results.errors['$.title']).not_to be_empty results = b.schema.resolve({}) - expect(results.errors["$.title"]).not_to be_empty + expect(results.errors['$.title']).not_to be_empty end end - describe "overriding schema policy" do - let!(:a) { + describe 'overriding schema policy' do + let!(:a) do Class.new do include Paradocs::DSL @@ -112,15 +112,15 @@ class GrandChild < Child field(:title).policy(:string) end end - } + end - let!(:b) { + let!(:b) do Class.new(a) do schema.policy(:declared) end - } + end - it "does not mutate parent schema" do + it 'does not mutate parent schema' do results = a.schema.resolve({}) expect(results.errors).not_to be_empty @@ -129,8 +129,8 @@ class GrandChild < Child end end - describe "removes fields defined in the parent class" do - let!(:a) { + describe 'removes fields defined in the parent class' do + let!(:a) do Class.new do include Paradocs::DSL @@ -138,26 +138,26 @@ class GrandChild < Child field(:title).policy(:string) end end - } + end - let!(:b) { + let!(:b) do Class.new(a) do schema.ignore(:title) do field(:age) end end - } + end - it "removes inherited field from child class" do - results = a.schema.resolve({title: "Mr.", age: 20}) - expect(results.output).to eq({title: "Mr."}) + it 'removes inherited field from child class' do + results = a.schema.resolve({ title: 'Mr.', age: 20 }) + expect(results.output).to eq({ title: 'Mr.' }) - results = b.schema.resolve({title: "Mr.", age: 20}) - expect(results.output).to eq({age: 20}) + results = b.schema.resolve({ title: 'Mr.', age: 20 }) + expect(results.output).to eq({ age: 20 }) end end - describe "passing other schema or form in definition" do + describe 'passing other schema or form in definition' do it 'applies schema' do a = Paradocs::Schema.new do field(:name).policy(:string) @@ -169,7 +169,7 @@ class GrandChild < Child end results = b.schema.resolve(name: 'Neil') - expect(results.output).to eq({name: 'Neil', age: 40}) + expect(results.output).to eq({ name: 'Neil', age: 40 }) end end end diff --git a/spec/expand_spec.rb b/spec/expand_spec.rb index 21fddc4..f8eeaed 100644 --- a/spec/expand_spec.rb +++ b/spec/expand_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Paradocs::Schema do - it "expands fields dynamically" do + it 'expands fields dynamically' do schema = described_class.new do field(:title).type(:string).present expand(/^attr_(.+)/) do |match| @@ -13,12 +13,12 @@ end out = schema.resolve({ - title: "foo", - :"attr_Attribute 1" => "attr 1", - :"attr_Attribute 2" => "attr 2", - :"validate_valid_attr" => "valid", - :"validate_invalid_attr" => "", - }) + title: 'foo', + "attr_Attribute 1": 'attr 1', + "attr_Attribute 2": 'attr 2', + "validate_valid_attr": 'valid', + "validate_invalid_attr": '' + }) expect(out.output[:title]).to eq 'foo' expect(out.output[:"Attribute 1"]).to eq 'attr 1' diff --git a/spec/extensions/payload_builder_spec.rb b/spec/extensions/payload_builder_spec.rb index 45be7e3..6f7b210 100644 --- a/spec/extensions/payload_builder_spec.rb +++ b/spec/extensions/payload_builder_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' describe Paradocs::Extensions::PayloadBuilder do let(:schema) do @@ -11,16 +11,16 @@ field(:subtest2).required.type(:array).schema do field(:hello).type(:string).mutates_schema! { |*| :deep_schema } subschema(:deep_schema) { field(:deep_field).type(:boolean) } - subschema(:empty) { } + subschema(:empty) {} end end # 2 mutation fields and more than 1 subschema pack work good in validation but docs # will contain only 1 subschema at once: foo subschemes will never be mixed with test subschemes field(:foo).required.type(:object).schema do - field(:bar).present.type(:string).options(["foo", "bar"]).mutates_schema! do |value, *| - value == "foo" ? :fooschema : :barschema + field(:bar).present.type(:string).options(%w[foo bar]).mutates_schema! do |value, *| + value == 'foo' ? :fooschema : :barschema end - subschema(:fooschema) { } + subschema(:fooschema) {} subschema(:barschema) do field(:barfield).present.type(:boolean).as(:bar_field) end @@ -28,43 +28,43 @@ end end - it "gives an example payload and takes into account the subschemes" do - allow_any_instance_of(Array).to receive(:sample) { "bar" } + it 'gives an example payload and takes into account the subschemes' do + allow_any_instance_of(Array).to receive(:sample) { 'bar' } payloads = described_class.new(schema).build! - expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty]) - expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => "bar", "bar_field" => nil}}) - expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => "bar"}}) - expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => "bar"}, "subtest1" => nil}) + expect(payloads.keys.sort).to eq(%i[barschema fooschema subschema1 subschema2_deep_schema subschema2_empty]) + expect(payloads[:barschema]).to eq({ 'test' => nil, 'foo' => { 'bar' => 'bar', 'bar_field' => nil } }) + expect(payloads[:fooschema]).to eq({ 'test' => nil, 'foo' => { 'bar' => 'bar' } }) + expect(payloads[:subschema1]).to eq({ 'test' => nil, 'foo' => { 'bar' => 'bar' }, 'subtest1' => nil }) expect(payloads[:subschema2_deep_schema]).to eq({ - "subtest2" => [{"deep_field" => nil, "hello" => nil}], "test" => nil, "foo" => {"bar" => "bar"} - }) + 'subtest2' => [{ 'deep_field' => nil, 'hello' => nil }], 'test' => nil, 'foo' => { 'bar' => 'bar' } + }) expect(payloads[:subschema2_empty]).to eq({ - "test" => nil, "foo" => {"bar" => "bar"}, "subtest2" => [{"hello" => nil}] - }) + 'test' => nil, 'foo' => { 'bar' => 'bar' }, 'subtest2' => [{ 'hello' => nil }] + }) end - it "yields a usefull block that changes the result" do + it 'yields a usefull block that changes the result' do payloads = described_class.new(schema).build! do |key, meta, example, skip_word| - if key == "bar" + if key == 'bar' nil elsif meta[:type] == :boolean true - elsif key == "subtest1" + elsif key == 'subtest1' skip_word # this key value pair will be ommited else example # return suggested value end end - expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty]) - expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => nil, "bar_field" => true}}) # barfield is change to true and bar is nil - expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # bar is nil - expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # subtest is missing, bar is nil + expect(payloads.keys.sort).to eq(%i[barschema fooschema subschema1 subschema2_deep_schema subschema2_empty]) + expect(payloads[:barschema]).to eq({ 'test' => nil, 'foo' => { 'bar' => nil, 'bar_field' => true } }) # barfield is change to true and bar is nil + expect(payloads[:fooschema]).to eq({ 'test' => nil, 'foo' => { 'bar' => nil } }) # bar is nil + expect(payloads[:subschema1]).to eq({ 'test' => nil, 'foo' => { 'bar' => nil } }) # subtest is missing, bar is nil expect(payloads[:subschema2_deep_schema]).to eq({ - "subtest2" => [{"deep_field" => true, "hello" => nil}], "test" => nil, "foo" => {"bar" => nil} - }) + 'subtest2' => [{ 'deep_field' => true, 'hello' => nil }], 'test' => nil, 'foo' => { 'bar' => nil } + }) expect(payloads[:subschema2_empty]).to eq({ - "test" => nil, "foo" => {"bar" => nil}, "subtest2" => [{"hello" => nil}] - }) + 'test' => nil, 'foo' => { 'bar' => nil }, 'subtest2' => [{ 'hello' => nil }] + }) end end diff --git a/spec/extensions/structures_spec.rb b/spec/extensions/structures_spec.rb index 41dd64e..dd3a274 100644 --- a/spec/extensions/structures_spec.rb +++ b/spec/extensions/structures_spec.rb @@ -19,8 +19,8 @@ field(:data).type(:object).present.schema do field(:id).type(:integer).present.policy(:policy_with_error).as(:user_id) - field(:name).type(:string).meta(label: "very important staff").description("Example description").example("John") - field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*| + field(:name).type(:string).meta(label: 'very important staff').description('Example description').example('John') + field(:role).type(:string).declared.options(%w[admin user]).default('user').mutates_schema! do |*| :test_subschema end field(:extra).type(:array).required.schema do @@ -39,212 +39,213 @@ end end - describe "#nested" do - it "generates nested data for documentation generation" do - result = schema.structure.nested { |k, meta| meta[:block_works] = true unless meta[:present] } + describe '#nested' do + it 'generates nested data for documentation generation' do + result = schema.structure.nested { |_k, meta| meta[:block_works] = true unless meta[:present] } expect(result[:_subschemes]).to eq({}) expect(result[:_errors]).to eq([ArgumentError]) data_structure = result[:data].delete(:structure) expect(result[:data]).to eq({ - type: :object, - required: true, - present: true, - json_path: "$.data", - nested_name: "data", - }) + type: :object, + required: true, + present: true, + json_path: '$.data', + nested_name: 'data' + }) expect(data_structure[:_subschemes]).to eq({ - test_subschema: { - _errors: [], - _subschemes: {}, - test1: {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"} - }, - subschema: { - _errors: [], - _subschemes: {}, - test_field: {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"} - } - }) + test_subschema: { + _errors: [], + _subschemes: {}, + test1: { required: true, present: true, json_path: '$.data.test1', nested_name: 'data.test1' } + }, + subschema: { + _errors: [], + _subschemes: {}, + test_field: { required: true, present: true, json_path: '$.data.test_field', nested_name: 'data.test_field' } + } + }) expect(data_structure[:user_id]).to eq({ - type: :integer, - required: true, - present: true, - policy_with_error: {errors: [ArgumentError]}, - alias: :user_id, - json_path: "$.data.user_id", - nested_name: "data.user_id" - }) + type: :integer, + required: true, + present: true, + policy_with_error: { errors: [ArgumentError] }, + alias: :user_id, + json_path: '$.data.user_id', + nested_name: 'data.user_id' + }) expect(data_structure[:name]).to eq({ - type: :string, - label: "very important staff", - description: "Example description", - example: "John", - mutates_schema: true, - block_works: true, - json_path: "$.data.name", - nested_name: "data.name" - }) + type: :string, + label: 'very important staff', + description: 'Example description', + example: 'John', + mutates_schema: true, + block_works: true, + json_path: '$.data.name', + nested_name: 'data.name' + }) expect(data_structure[:role]).to eq({ - type: :string, - options: ["admin", "user"], - default: "user", - mutates_schema: true, - block_works: true, - json_path: "$.data.role", - nested_name: "data.role" - }) + type: :string, + options: %w[admin user], + default: 'user', + mutates_schema: true, + block_works: true, + json_path: '$.data.role', + nested_name: 'data.role' + }) expect(data_structure[:extra]).to eq({ - type: :array, - required: true, - block_works: true, - json_path: "$.data.extra[]", - nested_name: "data.extra", - structure: { - extra: { - default: false, - block_works: true, - json_path: "$.data.extra[].extra", - nested_name: "data.extra.extra", - policy_with_silent_error: {errors: []} - }, - _subschemes: {} - } - }) + type: :array, + required: true, + block_works: true, + json_path: '$.data.extra[]', + nested_name: 'data.extra', + structure: { + extra: { + default: false, + block_works: true, + json_path: '$.data.extra[].extra', + nested_name: 'data.extra.extra', + policy_with_silent_error: { errors: [] } + }, + _subschemes: {} + } + }) end end - describe "#flatten" do - it "generates flatten data for documentation generation" do - expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split(".").size == 1 }).to eq({ - "data" => { - type: :object, - required: true, - present: true, - block_works: true, - json_path: "$.data" - }, - "data.extra" => { - type: :array, - required: true, - json_path: "$.data.extra[]" - }, - "data.extra.extra" => { - default: false, - json_path: "$.data.extra[].extra", - policy_with_silent_error: {errors: []} - }, - "data.user_id" => { - type: :integer, - required: true, - present: true, - alias: :user_id, - json_path: "$.data.user_id", - policy_with_error: {errors: [ArgumentError]} - }, - "data.name" => { - type: :string, - json_path: "$.data.name", - label: "very important staff", - description: "Example description", - example: "John", - mutates_schema: true - }, - "data.role" => { - type: :string, - options: ["admin", "user"], - default: "user", - json_path: "$.data.role", - mutates_schema: true - }, - _errors: [ArgumentError], - _subschemes: { - test_subschema: { - _errors: [], - _subschemes: {}, - "data.test1"=>{ - required: true, - present: true, - json_path: "$.data.test1" - } - }, - subschema: { - _errors: [], - _subschemes: {}, - "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"} - } - } - }) + describe '#flatten' do + it 'generates flatten data for documentation generation' do + expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split('.').size == 1 }).to eq({ + 'data' => { + type: :object, + required: true, + present: true, + block_works: true, + json_path: '$.data' + }, + 'data.extra' => { + type: :array, + required: true, + json_path: '$.data.extra[]' + }, + 'data.extra.extra' => { + default: false, + json_path: '$.data.extra[].extra', + policy_with_silent_error: { errors: [] } + }, + 'data.user_id' => { + type: :integer, + required: true, + present: true, + alias: :user_id, + json_path: '$.data.user_id', + policy_with_error: { errors: [ArgumentError] } + }, + 'data.name' => { + type: :string, + json_path: '$.data.name', + label: 'very important staff', + description: 'Example description', + example: 'John', + mutates_schema: true + }, + 'data.role' => { + type: :string, + options: %w[admin user], + default: 'user', + json_path: '$.data.role', + mutates_schema: true + }, + _errors: [ArgumentError], + _subschemes: { + test_subschema: { + _errors: [], + _subschemes: {}, + 'data.test1' => { + required: true, + present: true, + json_path: '$.data.test1' + } + }, + subschema: { + _errors: [], + _subschemes: {}, + 'data.test_field' => { required: true, present: true, json_path: '$.data.test_field' } + } + } + }) end end - describe "#all_flatten" do - it "generates N structures, where N = number of unique combinations of applied subschemas" do + describe '#all_flatten' do + it 'generates N structures, where N = number of unique combinations of applied subschemas' do expect(schema.structure.all_flatten).to eq({ - subschema: { - _errors: [ArgumentError], - "data" => {type: :object, required: true, present: true, json_path: "$.data"}, - "data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"}, - "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"}, - "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true}, - "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"}, - "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"}, - "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"} - }, - test_subschema: { - _errors: [ArgumentError], - "data" => {type: :object, required: true, present: true, json_path: "$.data"}, - "data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"}, - "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"}, - "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true}, - "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"}, - "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"}, - "data.test1" => {required: true, present: true, json_path: "$.data.test1"} - } - }) + subschema: { + _errors: [ArgumentError], + 'data' => { type: :object, required: true, present: true, json_path: '$.data' }, + 'data.user_id' => { type: :integer, required: true, present: true, policy_with_error: { errors: [ArgumentError] }, alias: :user_id, json_path: '$.data.user_id' }, + 'data.name' => { type: :string, label: 'very important staff', json_path: '$.data.name', mutates_schema: true, description: 'Example description', example: 'John' }, + 'data.role' => { type: :string, options: %w[admin user], default: 'user', json_path: '$.data.role', mutates_schema: true }, + 'data.extra' => { type: :array, required: true, json_path: '$.data.extra[]' }, + 'data.extra.extra' => { default: false, policy_with_silent_error: { errors: [] }, json_path: '$.data.extra[].extra' }, + 'data.test_field' => { required: true, present: true, json_path: '$.data.test_field' } + }, + test_subschema: { + _errors: [ArgumentError], + 'data' => { type: :object, required: true, present: true, json_path: '$.data' }, + 'data.user_id' => { type: :integer, required: true, present: true, policy_with_error: { errors: [ArgumentError] }, alias: :user_id, json_path: '$.data.user_id' }, + 'data.name' => { type: :string, label: 'very important staff', json_path: '$.data.name', mutates_schema: true, description: 'Example description', example: 'John' }, + 'data.role' => { type: :string, options: %w[admin user], default: 'user', json_path: '$.data.role', mutates_schema: true }, + 'data.extra' => { type: :array, required: true, json_path: '$.data.extra[]' }, + 'data.extra.extra' => { default: false, policy_with_silent_error: { errors: [] }, json_path: '$.data.extra[].extra' }, + 'data.test1' => { required: true, present: true, json_path: '$.data.test1' } + } + }) end end - describe "#all_nested" do - it "generates N structures, where N = number of unique combinations of applied subschemas" do + describe '#all_nested' do + it 'generates N structures, where N = number of unique combinations of applied subschemas' do result = schema.structure.all_nested expect(result[:subschema]).to eq({ - _errors: [ArgumentError], - "data" => { - type: :object, - required: true, - present: true, - json_path: "$.data", - nested_name: "data", - structure: { - "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true}, - "test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}, - "user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"}, - "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"}, - "extra" => { - type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra", - structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}} - } - } - } - }) + _errors: [ArgumentError], + 'data' => { + type: :object, + required: true, + present: true, + json_path: '$.data', + nested_name: 'data', + structure: { + 'role' => { type: :string, options: %w[admin user], default: 'user', json_path: '$.data.role', nested_name: 'data.role', mutates_schema: true }, + 'test_field' => { required: true, present: true, json_path: '$.data.test_field', nested_name: 'data.test_field' }, + 'user_id' => { type: :integer, required: true, present: true, policy_with_error: { errors: [ArgumentError] }, alias: :user_id, json_path: '$.data.user_id', nested_name: 'data.user_id' }, + 'name' => { type: :string, label: 'very important staff', json_path: '$.data.name', mutates_schema: true, nested_name: 'data.name', description: 'Example description', example: 'John' }, + 'extra' => { + type: :array, required: true, json_path: '$.data.extra[]', nested_name: 'data.extra', + structure: { 'extra' => { default: false, policy_with_silent_error: { errors: [] }, json_path: '$.data.extra[].extra', nested_name: 'data.extra.extra' } } + } + } + } + }) expect(result[:test_subschema]).to eq({ - _errors: [ArgumentError], - "data" => { - type: :object, - required: true, - present: true, - json_path: "$.data", - nested_name: "data", - structure: { - "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true}, - "test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"}, - "user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"}, - "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"}, - "extra" => { - type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra", - structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}} - } - } - }) + _errors: [ArgumentError], + 'data' => { + type: :object, + required: true, + present: true, + json_path: '$.data', + nested_name: 'data', + structure: { + 'role' => { type: :string, options: %w[admin user], default: 'user', json_path: '$.data.role', nested_name: 'data.role', mutates_schema: true }, + 'test1' => { required: true, present: true, json_path: '$.data.test1', nested_name: 'data.test1' }, + 'user_id' => { type: :integer, required: true, present: true, policy_with_error: { errors: [ArgumentError] }, alias: :user_id, json_path: '$.data.user_id', nested_name: 'data.user_id' }, + 'name' => { type: :string, label: 'very important staff', json_path: '$.data.name', mutates_schema: true, nested_name: 'data.name', description: 'Example description', example: 'John' }, + 'extra' => { + type: :array, required: true, json_path: '$.data.extra[]', nested_name: 'data.extra', + structure: { 'extra' => { default: false, policy_with_silent_error: { errors: [] }, json_path: '$.data.extra[].extra', nested_name: 'data.extra.extra' } } + } + } + } + }) end end end diff --git a/spec/field_spec.rb b/spec/field_spec.rb index ef93858..8ea21d7 100644 --- a/spec/field_spec.rb +++ b/spec/field_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' describe Paradocs::Field do let(:context) { Paradocs::Context.new } @@ -7,7 +7,7 @@ def register_coercion(name, block) Paradocs.registry.policy name do - coerce &block + coerce(&block) end end @@ -27,21 +27,21 @@ def has_error(key, message) expect(context.errors[key]).to include(message) end - let(:payload) { {a_key: "Joe"} } + let(:payload) { { a_key: 'Joe' } } - describe "#resolve" do - it "returns value" do + describe '#resolve' do + it 'returns value' do resolve(subject, payload).tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Joe" + expect(r.value).to eq 'Joe' end end end - BUILT_IN_COERCIONS = [:string, :integer, :number, :array, :object, :boolean] + BUILT_IN_COERCIONS = %i[string integer number array object boolean].freeze - describe "#meta_data" do + describe '#meta_data' do BUILT_IN_COERCIONS.each do |t| it "policy #{t} adds #{t} to meta data" do subject.policy(t) @@ -50,82 +50,82 @@ def has_error(key, message) end end - describe "#type" do - it "is an alias for #policy" do + describe '#type' do + it 'is an alias for #policy' do subject.type(:integer) - resolve(subject, a_key: "10.0").tap do |r| + resolve(subject, a_key: '10.0').tap do |r| expect(r.value).to eq 10 end end end - describe "#policy" do - it "coerces integer" do + describe '#policy' do + it 'coerces integer' do subject.policy(:integer) - resolve(subject, a_key: "10.0").tap do |r| + resolve(subject, a_key: '10.0').tap do |r| expect(r.eligible?).to be true no_errors expect(r.value).to eq 10 end end - it "coerces number" do + it 'coerces number' do subject.policy(:number) - resolve(subject, a_key: "10.0").tap do |r| + resolve(subject, a_key: '10.0').tap do |r| expect(r.eligible?).to be true no_errors expect(r.value).to eq 10.0 end end - it "coerces string" do + it 'coerces string' do subject.policy(:string) resolve(subject, a_key: 10.0).tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "10.0" + expect(r.value).to eq '10.0' end end end - describe "#default" do - it "is default if missing key" do - resolve(subject.default("AA"), foobar: 1).tap do |r| + describe '#default' do + it 'is default if missing key' do + resolve(subject.default('AA'), foobar: 1).tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "AA" + expect(r.value).to eq 'AA' end end - it "returns value if key is present" do - resolve(subject.default("AA"), a_key: nil).tap do |r| + it 'returns value if key is present' do + resolve(subject.default('AA'), a_key: nil).tap do |r| expect(r.eligible?).to be true no_errors expect(r.value).to eq nil end - resolve(subject.default("AA"), a_key: "abc").tap do |r| + resolve(subject.default('AA'), a_key: 'abc').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "abc" + expect(r.value).to eq 'abc' end end end - describe "#present" do - it "is valid if value is present" do - resolve(subject.present, a_key: "abc").tap do |r| + describe '#present' do + it 'is valid if value is present' do + resolve(subject.present, a_key: 'abc').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "abc" + expect(r.value).to eq 'abc' end end - it "is invalid if value is empty" do - resolve(subject.present, a_key: "").tap do |r| + it 'is invalid if value is empty' do + resolve(subject.present, a_key: '').tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "" + expect(r.value).to eq '' end resolve(subject.present, a_key: nil).tap do |r| @@ -135,8 +135,8 @@ def has_error(key, message) end end - it "is invalid if key is missing" do - resolve(subject.present, foo: "abc").tap do |r| + it 'is invalid if key is missing' do + resolve(subject.present, foo: 'abc').tap do |r| expect(r.eligible?).to be true has_errors expect(r.value).to eq nil @@ -144,25 +144,25 @@ def has_error(key, message) end end - describe "#required" do - it "is valid if key is present" do - resolve(subject.required, a_key: "abc").tap do |r| + describe '#required' do + it 'is valid if key is present' do + resolve(subject.required, a_key: 'abc').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "abc" + expect(r.value).to eq 'abc' end end - it "is valid if key is present and value empty" do - resolve(subject.required, a_key: "").tap do |r| + it 'is valid if key is present and value empty' do + resolve(subject.required, a_key: '').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "" + expect(r.value).to eq '' end end - it "is invalid if key is missing" do - resolve(subject.required, foobar: "lala").tap do |r| + it 'is invalid if key is missing' do + resolve(subject.required, foobar: 'lala').tap do |r| expect(r.eligible?).to be true has_errors expect(r.value).to eq nil @@ -170,97 +170,97 @@ def has_error(key, message) end end - describe "#options" do + describe '#options' do before do - subject.options(['a', 'b', 'c']) + subject.options(%w[a b c]) end - it "resolves if value within options" do - resolve(subject, a_key: "b").tap do |r| + it 'resolves if value within options' do + resolve(subject, a_key: 'b').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "b" + expect(r.value).to eq 'b' end end - it "resolves if value is array within options" do - resolve(subject, a_key: ["b", "c"]).tap do |r| + it 'resolves if value is array within options' do + resolve(subject, a_key: %w[b c]).tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq ["b", "c"] + expect(r.value).to eq %w[b c] end end - it "does not resolve if missing key" do - resolve(subject, foobar: ["b", "c"]).tap do |r| + it 'does not resolve if missing key' do + resolve(subject, foobar: %w[b c]).tap do |r| expect(r.eligible?).to be false no_errors expect(r.value).to be_nil end end - it "does resolve if missing key and default set" do - subject.default("Foobar") - resolve(subject, foobar: ["b", "c"]).tap do |r| + it 'does resolve if missing key and default set' do + subject.default('Foobar') + resolve(subject, foobar: %w[b c]).tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "Foobar" + expect(r.value).to eq 'Foobar' end end - it "is invalid if missing key and required" do - subject = described_class.new(:a_key).required.options(%w(a b c)) - resolve(subject, foobar: ["b", "c"]).tap do |r| + it 'is invalid if missing key and required' do + subject = described_class.new(:a_key).required.options(%w[a b c]) + resolve(subject, foobar: %w[b c]).tap do |r| expect(r.eligible?).to be true has_errors expect(r.value).to be_nil end end - it "is invalid if value outside options" do - resolve(subject, a_key: "x").tap do |r| + it 'is invalid if value outside options' do + resolve(subject, a_key: 'x').tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "x" + expect(r.value).to eq 'x' end - resolve(subject, a_key: ["x", "b"]).tap do |r| + resolve(subject, a_key: %w[x b]).tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq ["x", "b"] + expect(r.value).to eq %w[x b] end end end - describe ":split policy" do - it "splits by comma" do - resolve(subject.policy(:split), a_key: "tag1,tag2").tap do |r| + describe ':split policy' do + it 'splits by comma' do + resolve(subject.policy(:split), a_key: 'tag1,tag2').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq ["tag1", "tag2"] + expect(r.value).to eq %w[tag1 tag2] end end end - describe ":declared policy" do - it "is eligible if key exists" do - resolve(subject.policy(:declared).present, a_key: "").tap do |r| + describe ':declared policy' do + it 'is eligible if key exists' do + resolve(subject.policy(:declared).present, a_key: '').tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "" + expect(r.value).to eq '' end end - it "is available as method" do - resolve(subject.declared.present, a_key: "").tap do |r| + it 'is available as method' do + resolve(subject.declared.present, a_key: '').tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "" + expect(r.value).to eq '' end end - it "is not eligible if key does not exist" do - resolve(subject.policy(:declared).present, foo: "").tap do |r| + it 'is not eligible if key does not exist' do + resolve(subject.policy(:declared).present, foo: '').tap do |r| expect(r.eligible?).to be false no_errors expect(r.value).to eq nil @@ -268,15 +268,15 @@ def has_error(key, message) end end - describe ":noop policy" do - it "does not do anything" do - resolve(subject.policy(:noop).present, a_key: "").tap do |r| + describe ':noop policy' do + it 'does not do anything' do + resolve(subject.policy(:noop).present, a_key: '').tap do |r| expect(r.eligible?).to be true has_errors - expect(r.value).to eq "" + expect(r.value).to eq '' end - resolve(subject.policy(:noop).present, foo: "").tap do |r| + resolve(subject.policy(:noop).present, foo: '').tap do |r| expect(r.eligible?).to be true has_errors expect(r.value).to eq nil @@ -284,22 +284,22 @@ def has_error(key, message) end end - describe "#schema" do - it "runs sub-schema" do + describe '#schema' do + it 'runs sub-schema' do subject.schema do field(:name).policy(:string) field(:tags).policy(:split).policy(:array) end - payload = {a_key: [{name: "n1", tags: "t1,t2"}, {name: "n2", tags: ["t3"]}]} + payload = { a_key: [{ name: 'n1', tags: 't1,t2' }, { name: 'n2', tags: ['t3'] }] } resolve(subject, payload).tap do |r| expect(r.eligible?).to be true no_errors expect(r.value).to eq([ - {name: "n1", tags: ["t1", "t2"]}, - {name: "n2", tags: ["t3"]}, - ]) + { name: 'n1', tags: %w[t1 t2] }, + { name: 'n2', tags: ['t3'] } + ]) end end end @@ -319,12 +319,12 @@ def valid?(*_) true end - def coerce(value, key, context) + def coerce(value, _key, _context) "#{@title} #{value}" end def meta_data - {foo: "bar"} + { foo: 'bar' } end def policy_name @@ -334,82 +334,82 @@ def policy_name end it 'works with policy in registry' do - register_coercion :foo, ->(v, k, c){ "Hello #{v}" } + register_coercion :foo, ->(v, _k, _c) { "Hello #{v}" } subject.policy(:foo) - resolve(subject, a_key: "Ismael").tap do |r| + resolve(subject, a_key: 'Ismael').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Hello Ismael" + expect(r.value).to eq 'Hello Ismael' end end it 'raises if policy not found' do - expect{ + expect do subject.policy(:foobar) - }.to raise_exception Paradocs::ConfigurationError + end.to raise_exception Paradocs::ConfigurationError end it 'chains policies' do - Paradocs.registry.policy :general, custom_klass.new("General") - Paradocs.registry.policy :commander, custom_klass.new("Commander") + Paradocs.registry.policy :general, custom_klass.new('General') + Paradocs.registry.policy :commander, custom_klass.new('Commander') subject .policy(:general) .policy(:commander) - resolve(subject, a_key: "Ismael").tap do |r| + resolve(subject, a_key: 'Ismael').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Commander General Ismael" + expect(r.value).to eq 'Commander General Ismael' end end - it "can instantiate policy class and pass arguments" do + it 'can instantiate policy class and pass arguments' do Paradocs.registry.policy :job_title, custom_klass - subject.policy(:job_title, "Developer") + subject.policy(:job_title, 'Developer') - resolve(subject, a_key: "Ismael").tap do |r| + resolve(subject, a_key: 'Ismael').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Developer Ismael" + expect(r.value).to eq 'Developer Ismael' end end - it "can take a class not in the registry" do - subject.policy(custom_klass, "Developer") + it 'can take a class not in the registry' do + subject.policy(custom_klass, 'Developer') - resolve(subject, a_key: "Ismael").tap do |r| + resolve(subject, a_key: 'Ismael').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Developer Ismael" + expect(r.value).to eq 'Developer Ismael' end end - it "adds policy meta data" do - subject.policy(custom_klass, "Developer") - expect(subject.meta_data[:foo]).to eq "bar" + it 'adds policy meta data' do + subject.policy(custom_klass, 'Developer') + expect(subject.meta_data[:foo]).to eq 'bar' end - it "can take an instance not in the registry" do - subject.policy(custom_klass.new("Developer"), "ignore this") + it 'can take an instance not in the registry' do + subject.policy(custom_klass.new('Developer'), 'ignore this') - resolve(subject, a_key: "Ismael").tap do |r| + resolve(subject, a_key: 'Ismael').tap do |r| expect(r.eligible?).to be true no_errors - expect(r.value).to eq "Developer Ismael" + expect(r.value).to eq 'Developer Ismael' end end it 'add policy message to #errors if validation fails' do - register_coercion :error, ->(v, k, c) { raise "This is an error" } + register_coercion :error, ->(_v, _k, _c) { raise 'This is an error' } subject.policy(:error) - resolve(subject, a_key: "b").tap do |r| + resolve(subject, a_key: 'b').tap do |r| expect(r.eligible?).to be true - has_error("$", "is invalid") - expect(r.value).to eq "b" + has_error('$', 'is invalid') + expect(r.value).to eq 'b' end end end diff --git a/spec/helpers.rb b/spec/helpers.rb index 24e7179..a6a9997 100644 --- a/spec/helpers.rb +++ b/spec/helpers.rb @@ -1,10 +1,10 @@ def expected_policy_behavior(policy:, policy_args: [], input:, output: nil, errors: {}, environment: {}, ignore_for: []) output ||= input schema = Paradocs::Schema.new do - input.map do |key, value| + input.map do |key, _value| instruction = ignore_for.include?(key) ? -> { field(key) } : -> { field(key).policy(*([policy] + policy_args)) } - instance_exec &instruction + instance_exec(&instruction) end end diff --git a/spec/policies_spec.rb b/spec/policies_spec.rb index 8b87bdb..e8acc96 100644 --- a/spec/policies_spec.rb +++ b/spec/policies_spec.rb @@ -9,7 +9,7 @@ def test_coercion(key, value, expected) describe ':datetime' do it { coercion = Paradocs.registry.coercions[:datetime] - coercion.new.coerce("2016-11-05T14:23:34Z", nil, nil).tap do |d| + coercion.new.coerce('2016-11-05T14:23:34Z', nil, nil).tap do |d| expect(d).to be_a Date expect(d.year).to eq 2016 expect(d.month).to eq 11 @@ -17,7 +17,7 @@ def test_coercion(key, value, expected) expect(d.hour).to eq 14 expect(d.minute).to eq 23 expect(d.second).to eq 34 - expect(d.zone).to eq "+00:00" + expect(d.zone).to eq '+00:00' end } end @@ -63,97 +63,97 @@ def test_coercion(key, value, expected) describe ':split' do it { - test_coercion(:split, 'aaa,bb,cc', ['aaa', 'bb', 'cc']) - test_coercion(:split, 'aaa ,bb, cc', ['aaa', 'bb', 'cc']) + test_coercion(:split, 'aaa,bb,cc', %w[aaa bb cc]) + test_coercion(:split, 'aaa ,bb, cc', %w[aaa bb cc]) test_coercion(:split, 'aaa', ['aaa']) - test_coercion(:split, ['aaa', 'bb', 'cc'], ['aaa', 'bb', 'cc']) + test_coercion(:split, %w[aaa bb cc], %w[aaa bb cc]) } end describe ':gt' do it "passes the value if it's greater than policy param" do - expected_policy_behavior(policy: :gt, policy_args: [3], input: {a: 4}, output: {a: 4}) + expected_policy_behavior(policy: :gt, policy_args: [3], input: { a: 4 }, output: { a: 4 }) end - it "raises error if value is greater to policy param" do - errors = {"$.a"=>["value must be strictly greater than 10"]} - errors2 = {"$.a"=>["value must be strictly greater than 10"]} - expected_policy_behavior(policy: :gt, policy_args: [10], input: {a: 4}, errors: errors) - expected_policy_behavior(policy: :gt, policy_args: [10], input: {a: 10}, errors: errors2) + it 'raises error if value is greater to policy param' do + errors = { '$.a' => ['value must be strictly greater than 10'] } + errors2 = { '$.a' => ['value must be strictly greater than 10'] } + expected_policy_behavior(policy: :gt, policy_args: [10], input: { a: 4 }, errors: errors) + expected_policy_behavior(policy: :gt, policy_args: [10], input: { a: 10 }, errors: errors2) end end describe ':gte' do it "passes the value if it's greater or equal to policy param" do - expected_policy_behavior(policy: :gte, policy_args: [3], input: {a: 4}, output: {a: 4}) - expected_policy_behavior(policy: :gte, policy_args: [3], input: {a: 3}, output: {a: 3}) + expected_policy_behavior(policy: :gte, policy_args: [3], input: { a: 4 }, output: { a: 4 }) + expected_policy_behavior(policy: :gte, policy_args: [3], input: { a: 3 }, output: { a: 3 }) end - it "raises error if value is greater than policy param" do - errors = {"$.a"=>["value must be greater than or equal to 10"]} - expected_policy_behavior(policy: :gte, policy_args: [10], input: {a: 4}, errors: errors) + it 'raises error if value is greater than policy param' do + errors = { '$.a' => ['value must be greater than or equal to 10'] } + expected_policy_behavior(policy: :gte, policy_args: [10], input: { a: 4 }, errors: errors) end end describe ':lt' do it "passes the value if it's less to policy param" do - expected_policy_behavior(policy: :lt, policy_args: [100], input: {a: 4}, output: {a: 4}) + expected_policy_behavior(policy: :lt, policy_args: [100], input: { a: 4 }, output: { a: 4 }) end - it "raises error if value is strictly less to policy param" do - errors = {"$.a"=>["value must be strictly less than 10"]} - errors2 = {"$.a"=>["value must be strictly less than 10"]} - expected_policy_behavior(policy: :lt, policy_args: [10], input: {a: 40}, errors: errors) - expected_policy_behavior(policy: :lt, policy_args: [10], input: {a: 10}, errors: errors2) + it 'raises error if value is strictly less to policy param' do + errors = { '$.a' => ['value must be strictly less than 10'] } + errors2 = { '$.a' => ['value must be strictly less than 10'] } + expected_policy_behavior(policy: :lt, policy_args: [10], input: { a: 40 }, errors: errors) + expected_policy_behavior(policy: :lt, policy_args: [10], input: { a: 10 }, errors: errors2) end end describe ':lte' do it "passes the value if it's less or equal to policy param" do - expected_policy_behavior(policy: :lte, policy_args: [100], input: {a: 4}, output: {a: 4}) - expected_policy_behavior(policy: :lte, policy_args: [100], input: {a: 100}, output: {a: 100}) + expected_policy_behavior(policy: :lte, policy_args: [100], input: { a: 4 }, output: { a: 4 }) + expected_policy_behavior(policy: :lte, policy_args: [100], input: { a: 100 }, output: { a: 100 }) end - it "raises error if value is less or equal than policy param" do - errors = {"$.a"=>["value must be less than or equal to 1"]} - expected_policy_behavior(policy: :lte, policy_args: [1], input: {a: 40}, output: {a: 40}, errors: errors) + it 'raises error if value is less or equal than policy param' do + errors = { '$.a' => ['value must be less than or equal to 1'] } + expected_policy_behavior(policy: :lte, policy_args: [1], input: { a: 40 }, output: { a: 40 }, errors: errors) end end describe ':length' do it "passes the value if it's between min and max limits" do - expected_policy_behavior(policy: :length, policy_args: [{min: 5, max: 20}], input: {a: "string"}, output: {a: "string"}) - expected_policy_behavior(policy: :length, policy_args: [{min: 5}], input: {a: "string"}, output: {a: "string"}) - expected_policy_behavior(policy: :length, policy_args: [{max: 20}], input: {a: "string"}, output: {a: "string"}) + expected_policy_behavior(policy: :length, policy_args: [{ min: 5, max: 20 }], input: { a: 'string' }, output: { a: 'string' }) + expected_policy_behavior(policy: :length, policy_args: [{ min: 5 }], input: { a: 'string' }, output: { a: 'string' }) + expected_policy_behavior(policy: :length, policy_args: [{ max: 20 }], input: { a: 'string' }, output: { a: 'string' }) end it "passes the value if it's exactly :eq limit" do - expected_policy_behavior(policy: :length, policy_args: [{eq: 5}], input: {a: "12345"}, output: {a: "12345"}) + expected_policy_behavior(policy: :length, policy_args: [{ eq: 5 }], input: { a: '12345' }, output: { a: '12345' }) end - it "raises error if value is not exactly than defined :eq limit" do - errors = {"$.a"=>["value must be exactly 10 characters"]} - expected_policy_behavior(policy: :length, policy_args: [{eq: 10}], input: {a: "test"}, output: {a: "test"}, errors: errors) + it 'raises error if value is not exactly than defined :eq limit' do + errors = { '$.a' => ['value must be exactly 10 characters'] } + expected_policy_behavior(policy: :length, policy_args: [{ eq: 10 }], input: { a: 'test' }, output: { a: 'test' }, errors: errors) end - it "raises error if value is less than min limit" do - errors = {"$.a"=>["value must be minimum 10 characters"]} - expected_policy_behavior(policy: :length, policy_args: [{min: 10}], input: {a: "test"}, output: {a: "test"}, errors: errors) + it 'raises error if value is less than min limit' do + errors = { '$.a' => ['value must be minimum 10 characters'] } + expected_policy_behavior(policy: :length, policy_args: [{ min: 10 }], input: { a: 'test' }, output: { a: 'test' }, errors: errors) end - it "raises error if value is greater than max limit" do - errors = {"$.a"=>["value must be maximum 3 characters"]} - expected_policy_behavior(policy: :length, policy_args: [{max: 3}], input: {a: "test"}, output: {a: "test"}, errors: errors) + it 'raises error if value is greater than max limit' do + errors = { '$.a' => ['value must be maximum 3 characters'] } + expected_policy_behavior(policy: :length, policy_args: [{ max: 3 }], input: { a: 'test' }, output: { a: 'test' }, errors: errors) end - it "raises error with full description if value is less than min limit" do - errors = {"$.a"=>["value must be minimum 10 characters, maximum 20 characters"]} - expected_policy_behavior(policy: :length, policy_args: [{min: 10, max: 20}], input: {a: "test"}, output: {a: "test"}, errors: errors) + it 'raises error with full description if value is less than min limit' do + errors = { '$.a' => ['value must be minimum 10 characters, maximum 20 characters'] } + expected_policy_behavior(policy: :length, policy_args: [{ min: 10, max: 20 }], input: { a: 'test' }, output: { a: 'test' }, errors: errors) end - it "raises error with full description if value is greater than max limit" do - errors = {"$.a"=>["value must be minimum 3 characters, maximum 5 characters"]} - expected_policy_behavior(policy: :length, policy_args: [{min: 3, max: 5}], input: {a: "test_test"}, output: {a: "test_test"}, errors: errors) + it 'raises error with full description if value is greater than max limit' do + errors = { '$.a' => ['value must be minimum 3 characters, maximum 5 characters'] } + expected_policy_behavior(policy: :length, policy_args: [{ min: 3, max: 5 }], input: { a: 'test_test' }, output: { a: 'test_test' }, errors: errors) end end end diff --git a/spec/schema_spec.rb b/spec/schema_spec.rb index c33dede..99de369 100644 --- a/spec/schema_spec.rb +++ b/spec/schema_spec.rb @@ -3,7 +3,7 @@ describe Paradocs::Schema do before do Paradocs.policy :flexible_bool do - coerce do |v, k, c| + coerce do |v, _k, _c| case v when '1', 'true', 'TRUE', true true @@ -17,8 +17,8 @@ subject do described_class.new do field(:title).policy(:string).present.as(:article_title) - field(:price).policy(:integer).meta(label: "A price") - field(:status).policy(:string).options(['visible', 'hidden']) + field(:price).policy(:integer).meta(label: 'A price') + field(:status).policy(:string).options(%w[visible hidden]) field(:tags).policy(:split).policy(:array) field(:description).policy(:string) field(:variants).policy(:array).schema do @@ -30,13 +30,13 @@ end end - describe "#structure" do - it "represents data structure and meta data" do + describe '#structure' do + it 'represents data structure and meta data' do sc = subject.structure.nested expect(sc[:article_title][:present]).to be true expect(sc[:article_title][:type]).to eq :string expect(sc[:price][:type]).to eq :integer - expect(sc[:price][:label]).to eq "A price" + expect(sc[:price][:label]).to eq 'A price' expect(sc[:variants][:type]).to eq :array sc[:variants][:structure].tap do |sc| expect(sc[:name][:type]).to eq :string @@ -46,7 +46,7 @@ end end - def resolve(schema, payload, &block) + def resolve(schema, payload) yield schema.resolve(payload) end @@ -60,14 +60,14 @@ def test_schema(schema, payload, result) payload = { tags: 'tag', status: 'visible', - extra_field: "extra", - price: "100", - title: "title", + extra_field: 'extra', + price: '100', + title: 'title', variants: [ { stock: '10', available_if_no_stock: true, - extra_field: "extra", + extra_field: 'extra', name: 'v1', sku: 'ABC' } @@ -76,47 +76,47 @@ def test_schema(schema, payload, result) output = subject.resolve(payload).output expect(output).to eq({ - article_title: "title", - price: 100, - status: "visible", - tags: ["tag"], - variants: [ - { - name: "v1", - sku: "ABC", - stock: 10, - available_if_no_stock: true - } - ] - }) + article_title: 'title', + price: 100, + status: 'visible', + tags: ['tag'], + variants: [ + { + name: 'v1', + sku: 'ABC', + stock: 10, + available_if_no_stock: true + } + ] + }) end it 'works' do test_schema(subject, { - title: 'iPhone 6 Plus', - price: '100.0', - status: 'visible', - tags: 'tag1, tag2', - description: 'A description', - variants: [{name: 'v1', sku: 'ABC', stock: '10', available_if_no_stock: true}] - }, - { - article_title: 'iPhone 6 Plus', - price: 100, - status: 'visible', - tags: ['tag1', 'tag2'], - description: 'A description', - variants: [{name: 'v1', sku: 'ABC', stock: 10, available_if_no_stock: true}] - }) + title: 'iPhone 6 Plus', + price: '100.0', + status: 'visible', + tags: 'tag1, tag2', + description: 'A description', + variants: [{ name: 'v1', sku: 'ABC', stock: '10', available_if_no_stock: true }] + }, + { + article_title: 'iPhone 6 Plus', + price: 100, + status: 'visible', + tags: %w[tag1 tag2], + description: 'A description', + variants: [{ name: 'v1', sku: 'ABC', stock: 10, available_if_no_stock: true }] + }) test_schema(subject, { - title: 'iPhone 6 Plus', - variants: [{name: 'v1', available_if_no_stock: '1'}] - }, - { - article_title: 'iPhone 6 Plus', - variants: [{name: 'v1', stock: 1, available_if_no_stock: true}] - }) + title: 'iPhone 6 Plus', + variants: [{ name: 'v1', available_if_no_stock: '1' }] + }, + { + article_title: 'iPhone 6 Plus', + variants: [{ name: 'v1', stock: 1, available_if_no_stock: true }] + }) resolve(subject, {}) do |results| expect(results.valid?).to be false @@ -125,25 +125,25 @@ def test_schema(schema, payload, result) expect(results.errors['$.status']).to be_nil end - resolve(subject, {title: 'Foobar', variants: [{name: 'v1'}, {sku: '345'}]}) do |results| + resolve(subject, { title: 'Foobar', variants: [{ name: 'v1' }, { sku: '345' }] }) do |results| expect(results.valid?).to be false expect(results.errors['$.variants[1].name']).not_to be_nil end end - it "ignores nil fields if using :declared policy" do + it 'ignores nil fields if using :declared policy' do schema = described_class.new do field(:id).type(:integer) field(:title).declared.type(:string) end - resolve(schema, {id: 123}) do |results| + resolve(schema, { id: 123 }) do |results| expect(results.output.keys).to eq [:id] end end - describe "#policy" do - it "applies policy to all fields" do + describe '#policy' do + it 'applies policy to all fields' do subject.policy(:declared) resolve(subject, {}) do |results| @@ -152,23 +152,23 @@ def test_schema(schema, payload, result) end end - it "replaces previous policies" do + it 'replaces previous policies' do subject.policy(:declared) subject.policy(:present) - resolve(subject, {title: "hello"}) do |results| + resolve(subject, { title: 'hello' }) do |results| expect(results.valid?).to be false - expect(results.errors.keys).to match_array(%w( - $.price - $.status - $.tags - $.description - $.variants - )) + expect(results.errors.keys).to match_array(%w[ + $.price + $.status + $.tags + $.description + $.variants + ]) end end - it "applies :noop policy to all fields" do + it 'applies :noop policy to all fields' do subject.policy(:noop) resolve(subject, {}) do |results| @@ -178,25 +178,25 @@ def test_schema(schema, payload, result) end end - describe "#merge" do - context "no options" do - let!(:schema1) { + describe '#merge' do + context 'no options' do + let!(:schema1) do described_class.new do field(:title).policy(:string).present field(:price).policy(:integer) end - } + end - let!(:schema2) { + let!(:schema2) do described_class.new do field(:price).policy(:string) field(:description).policy(:string) end - } + end - it "returns a new schema adding new fields and updating existing ones" do + it 'returns a new schema adding new fields and updating existing ones' do new_schema = schema1.merge(schema2) - expect(new_schema.fields.keys).to match_array([:title, :price, :description]) + expect(new_schema.fields.keys).to match_array(%i[title price description]) # did not mutate original expect(schema1.fields[:price].meta_data[:type]).to eq :integer @@ -206,29 +206,29 @@ def test_schema(schema, payload, result) end end - context "with options" do - let!(:schema1) { - described_class.new(price_type: :integer, label: "Foo") do |opts| + context 'with options' do + let!(:schema1) do + described_class.new(price_type: :integer, label: 'Foo') do |opts| field(:title).policy(:string).present field(:price).policy(opts[:price_type]).meta(label: opts[:label]) end - } + end - let!(:schema2) { + let!(:schema2) do described_class.new(price_type: :string) do field(:description).policy(:string) end - } + end - it "inherits options" do + it 'inherits options' do new_schema = schema1.merge(schema2) expect(new_schema.fields[:price].meta_data[:type]).to eq :string - expect(new_schema.fields[:price].meta_data[:label]).to eq "Foo" + expect(new_schema.fields[:price].meta_data[:label]).to eq 'Foo' end - it "re-applies blocks with new options" do + it 're-applies blocks with new options' do new_schema = schema1.merge(schema2) - expect(new_schema.fields.keys).to match_array([:title, :price, :description]) + expect(new_schema.fields.keys).to match_array(%i[title price description]) # did not mutate original expect(schema1.fields[:price].meta_data[:type]).to eq :integer @@ -239,41 +239,41 @@ def test_schema(schema, payload, result) end end - describe "#clone" do - let!(:schema1) { - described_class.new do |opts| + describe '#clone' do + let!(:schema1) do + described_class.new do |_opts| field(:id).present field(:title).policy(:string).present field(:price) end - } + end - it "returns a copy that can be further manipulated" do + it 'returns a copy that can be further manipulated' do schema2 = schema1.clone.policy(:declared).ignore(:id) - expect(schema1.fields.keys).to match_array([:id, :title, :price]) - expect(schema2.fields.keys).to match_array([:title, :price]) + expect(schema1.fields.keys).to match_array(%i[id title price]) + expect(schema2.fields.keys).to match_array(%i[title price]) - results1 = schema1.resolve(id: "abc", price: 100) - expect(results1.errors.keys).to eq ["$.title"] + results1 = schema1.resolve(id: 'abc', price: 100) + expect(results1.errors.keys).to eq ['$.title'] - results2 = schema2.resolve(id: "abc", price: 100) + results2 = schema2.resolve(id: 'abc', price: 100) expect(results2.errors.keys).to eq [] end end - describe "#ignore" do - it "ignores fields" do + describe '#ignore' do + it 'ignores fields' do s1 = described_class.new.ignore(:title, :status) do field(:status) field(:title).policy(:string).present field(:price).policy(:integer) end - output = s1.resolve(status: "draft", title: "foo", price: "100").output - expect(output).to eq({price: 100}) + output = s1.resolve(status: 'draft', title: 'foo', price: '100').output + expect(output).to eq({ price: 100 }) end - it "ignores when merging" do + it 'ignores when merging' do s1 = described_class.new do field(:status) field(:title).policy(:string).present @@ -283,11 +283,11 @@ def test_schema(schema, payload, result) field(:price).policy(:integer) end - output = s1.resolve(title: "foo", status: "draft", price: "100").output - expect(output).to eq({price: 100}) + output = s1.resolve(title: 'foo', status: 'draft', price: '100').output + expect(output).to eq({ price: 100 }) end - it "returns self so it can be chained" do + it 'returns self so it can be chained' do s1 = described_class.new do field(:status) field(:title).policy(:string).present diff --git a/spec/schema_walk_spec.rb b/spec/schema_walk_spec.rb index efa6c99..f5466b1 100644 --- a/spec/schema_walk_spec.rb +++ b/spec/schema_walk_spec.rb @@ -4,7 +4,7 @@ let(:schema) do Paradocs::Schema.new do field(:title).meta(example: 'a title', label: 'custom title') - field(:tags).policy(:array).meta(example: ['tag1', 'tag2'], label: 'comma-separated tags') + field(:tags).policy(:array).meta(example: %w[tag1 tag2], label: 'comma-separated tags') field(:friends).policy(:array).schema do field(:name).meta(example: 'a friend', label: 'friend full name') field(:age).meta(example: 34, label: 'age') @@ -12,31 +12,31 @@ end end - it "recursively walks the schema and collects meta data" do + it 'recursively walks the schema and collects meta data' do results = schema.walk(:label) expect(results.output).to eq({ - title: 'custom title', - tags: 'comma-separated tags', - friends: [ - { - name: 'friend full name', - age: 'age' - } - ] - }) + title: 'custom title', + tags: 'comma-separated tags', + friends: [ + { + name: 'friend full name', + age: 'age' + } + ] + }) end - it "works with blocks" do - results = schema.walk{|field| field.meta_data[:example]} + it 'works with blocks' do + results = schema.walk { |field| field.meta_data[:example] } expect(results.output).to eq({ - title: 'a title', - tags: ['tag1', 'tag2'], - friends: [ - { - name: 'a friend', - age: 34 - } - ] - }) + title: 'a title', + tags: %w[tag1 tag2], + friends: [ + { + name: 'a friend', + age: 34 + } + ] + }) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c4f1e2..7d3d76c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,4 @@ -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) require 'pry' require 'paradocs' require_relative 'helpers' diff --git a/spec/struct_spec.rb b/spec/struct_spec.rb index ec779a7..2b3d624 100644 --- a/spec/struct_spec.rb +++ b/spec/struct_spec.rb @@ -28,12 +28,12 @@ expect(new_instance.errors['$.title']).not_to be_nil instance = klass.new({ - title: 'foo', - friends: [ - {name: 'Ismael', age: 40}, - {name: 'Joe', age: 39}, - ] - }) + title: 'foo', + friends: [ + { name: 'Ismael', age: 40 }, + { name: 'Joe', age: 39 } + ] + }) expect(instance.example_title).to eq 'foo' expect(instance.friends.size).to eq 2 @@ -41,11 +41,11 @@ expect(instance.friends.first).to be_a friend_class invalid_instance = klass.new({ - friends: [ - {name: 'Ismael', age: 40}, - {age: 39}, - ] - }) + friends: [ + { name: 'Ismael', age: 40 }, + { age: 39 } + ] + }) expect(invalid_instance.valid?).to be false expect(invalid_instance.errors['$.title']).not_to be_nil @@ -65,13 +65,13 @@ end instance = klass.new - expect { + expect do instance.title = 'foo' - }.to raise_error NoMethodError + end.to raise_error NoMethodError - expect { + expect do instance.friends << 1 - }.to raise_error RuntimeError + end.to raise_error RuntimeError end it 'works with anonymous nested schemas' do @@ -87,12 +87,12 @@ end instance = klass.new({ - title: 'foo', - friends: [ - {age: 10}, - {age: 39}, - ] - }) + title: 'foo', + friends: [ + { age: 10 }, + { age: 39 } + ] + }) expect(instance.title).to eq 'foo' expect(instance.friends.size).to eq 2 @@ -103,7 +103,7 @@ klass = Class.new do include Paradocs::Struct - def self.paradocs_build_class_for_child(key, child_schema) + def self.paradocs_build_class_for_child(_key, child_schema) Class.new do include Paradocs::Struct schema child_schema @@ -121,7 +121,7 @@ def salutation end end - user = klass.new(name: 'Ismael', friends: [{age: 43}]) + user = klass.new(name: 'Ismael', friends: [{ age: 43 }]) expect(user.friends.first.salutation).to eq 'my age is 43' end @@ -140,9 +140,9 @@ def salutation end instance = klass.new({ - title: 'foo', - friends: [{name: 'Ismael'}] - }) + title: 'foo', + friends: [{ name: 'Ismael' }] + }) expect(instance.friends.first.name).to eq 'Ismael' end @@ -161,20 +161,20 @@ def salutation end instance = klass.new({ - title: 'foo', - friends: [ - {name: 'Jane'}, - {name: 'Joe', age: '39'}, - ] - }) + title: 'foo', + friends: [ + { name: 'Jane' }, + { name: 'Joe', age: '39' } + ] + }) expect(instance.to_h).to eq({ - title: 'foo', - friends: [ - {person_name: 'Jane', age: 20}, - {person_name: 'Joe', age: 39}, - ] - }) + title: 'foo', + friends: [ + { person_name: 'Jane', age: 20 }, + { person_name: 'Joe', age: 39 } + ] + }) new_instance = klass.new(instance.to_h) expect(new_instance.title).to eq 'foo' @@ -208,8 +208,8 @@ def salutation title: 'foo', email: 'email@me.com', friends: [ - {name: 'Jane', age: 20}, - {name: 'Joe', age: 39}, + { name: 'Jane', age: 20 }, + { name: 'Joe', age: 39 } ] ) @@ -231,37 +231,36 @@ def salutation end s1 = klass.new({ - title: 'foo', - friends: [ - {age: 10}, - {age: 39}, - ] - }) - + title: 'foo', + friends: [ + { age: 10 }, + { age: 39 } + ] + }) s2 = klass.new({ - title: 'foo', - friends: [ - {age: 10}, - {age: 39}, - ] - }) + title: 'foo', + friends: [ + { age: 10 }, + { age: 39 } + ] + }) s3 = klass.new({ - title: 'foo', - friends: [ - {age: 11}, - {age: 39}, - ] - }) + title: 'foo', + friends: [ + { age: 11 }, + { age: 39 } + ] + }) s4 = klass.new({ - title: 'bar', - friends: [ - {age: 10}, - {age: 39}, - ] - }) + title: 'bar', + friends: [ + { age: 10 }, + { age: 39 } + ] + }) expect(s1 == s2).to be true expect(s1 == s3).to be false @@ -284,12 +283,12 @@ def salutation original = klass.new( title: 'foo', desc: 'no change', - friends: [{name: 'joe'}] + friends: [{ name: 'joe' }] ) copy = original.merge( title: 'bar', - friends: [{name: 'jane'}] + friends: [{ name: 'jane' }] ) expect(original.title).to eq 'foo' diff --git a/spec/subschema_spec.rb b/spec/subschema_spec.rb index 547c477..7d2da24 100644 --- a/spec/subschema_spec.rb +++ b/spec/subschema_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -require "paradocs/dsl" +require 'paradocs/dsl' -describe "schemes with subschemes" do +describe 'schemes with subschemes' do let(:validation_class) do Class.new do include Paradocs::DSL schema(:request) do - field(:action).present.options([:update, :delete]).mutates_schema! do |action, *| + field(:action).present.options(%i[update delete]).mutates_schema! do |action, *| action == :update ? :update_schema : :generic_schema end @@ -26,28 +26,28 @@ def self.validate(schema_name, data) end end - let(:update_request) { + let(:update_request) do { action: :update, - event: "test" + event: 'test' } - } + end - it "invokes necessary subschema based on condition" do + it 'invokes necessary subschema based on condition' do valid_result = validation_class.validate(:request, update_request) expect(valid_result.output).to eq(update_request) expect(valid_result.errors).to eq({}) - failed_result = validation_class.validate(:request, {action: :update, generic_field: "test"}) + failed_result = validation_class.validate(:request, { action: :update, generic_field: 'test' }) - expect(failed_result.errors).to eq({"$.event"=>["is required"]}) - expect(failed_result.output).to eq({action: :update, event: nil}) + expect(failed_result.errors).to eq({ '$.event' => ['is required'] }) + expect(failed_result.output).to eq({ action: :update, event: nil }) end - describe "ghost fields" do + describe 'ghost fields' do let(:schema) do Paradocs::Schema.new do - mutation_by!(:error) do |value, key, *args| + mutation_by!(:error) do |value, _key, *_args| value.nil? ? :success : :fail end @@ -64,32 +64,32 @@ def self.validate(schema_name, data) structure = { _errors: [], _subschemes: { - fail: {_errors: [], _subschemes: {}, fail_field: {required: true, present: true, json_path: "$.fail_field", nested_name: "fail_field"}}, - success: {_errors: [], _subschemes: {}, success_field: {required: true, present: true, json_path: "$.success_field", nested_name: "success_field"}} + fail: { _errors: [], _subschemes: {}, fail_field: { required: true, present: true, json_path: '$.fail_field', nested_name: 'fail_field' } }, + success: { _errors: [], _subschemes: {}, success_field: { required: true, present: true, json_path: '$.success_field', nested_name: 'success_field' } } } } - result = schema.resolve({error: :here}) - expect(result.errors).to eq({"$.fail_field"=>["is required"]}) - expect(result.output).to eq({error: :here, fail_field: nil}) + result = schema.resolve({ error: :here }) + expect(result.errors).to eq({ '$.fail_field' => ['is required'] }) + expect(result.output).to eq({ error: :here, fail_field: nil }) expect(schema.structure.nested).to eq(structure) expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge( - error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"} - )) + error: { transparent: true, mutates_schema: true, json_path: '$.error', nested_name: 'error' } + )) result = schema.resolve({}) - expect(result.errors).to eq({"$.success_field"=>["is required"]}) - expect(result.output).to eq({success_field: nil}) + expect(result.errors).to eq({ '$.success_field' => ['is required'] }) + expect(result.output).to eq({ success_field: nil }) expect(schema.structure.nested).to eq(structure) expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge( - error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"} - )) + error: { transparent: true, mutates_schema: true, json_path: '$.error', nested_name: 'error' } + )) end end - describe "nested subschemes" do + describe 'nested subschemes' do let(:schema) do Paradocs::Schema.new do - field(:action).present.options([:update, :delete]).mutates_schema! do |value, key, payload| + field(:action).present.options(%i[update delete]).mutates_schema! do |value, _key, _payload| value == :update ? :update_schema : :generic_schema end field(:event).declared.type(:string) @@ -99,7 +99,7 @@ def self.validate(schema_name, data) end subschema(:update_schema) do - field(:event).present.mutates_schema! do |value, key, payload| + field(:event).present.mutates_schema! do |value, _key, _payload| value == :go_deeper ? :very_deep_schema : :deep_update_schema end field(:update_field).present @@ -119,59 +119,59 @@ def self.validate(schema_name, data) end end - context "update_schema -> deep_update_schema" do - let(:payload) { + context 'update_schema -> deep_update_schema' do + let(:payload) do { - action: :update, - event: :must_be_present, - update_field: 1, + action: :update, + event: :must_be_present, + update_field: 1, field_from_deep_schema: nil } - } + end - it "builds schema as expected" do + it 'builds schema as expected' do result = schema.resolve(payload) expect(result.output).to eq(payload) expect(result.errors).to eq({}) end - it "fails when validation fails in subschemas" do + it 'fails when validation fails in subschemas' do result = schema.resolve(payload.merge(action: :delete)) - expect(result.output).to eq(action: :delete, event: "must_be_present", generic_field: nil) - expect(result.errors).to eq("$.generic_field"=>["is required"]) + expect(result.output).to eq(action: :delete, event: 'must_be_present', generic_field: nil) + expect(result.errors).to eq('$.generic_field' => ['is required']) end - it "overwrites fields: subschema field overwrites parent field" do + it 'overwrites fields: subschema field overwrites parent field' do payload.delete(:event) result = schema.resolve(payload) end end - context "update_schema -> very_deep_schema -> draft_subschema" do - let(:payload) { + context 'update_schema -> very_deep_schema -> draft_subschema' do + let(:payload) do { - action: :update, - event: :go_deeper, - update_field: 1, - a_hash: { - key: :value, + action: :update, + event: :go_deeper, + update_field: 1, + a_hash: { + key: :value, another_event: true } } - } + end - it "builds schema as expected" do + it 'builds schema as expected' do result = schema.resolve(payload) expect(result.output).to eq(payload) expect(result.errors).to eq({}) end it "fails when payload doesn't suit just built schema" do - payload[:a_hash] = {key: :random} + payload[:a_hash] = { key: :random } result = schema.resolve(payload) payload[:a_hash][:another_event] = nil expect(result.output).to eq(payload) - expect(result.errors).to eq({"$.a_hash.another_event"=>["is required"]}) + expect(result.errors).to eq({ '$.a_hash.another_event' => ['is required'] }) end end end diff --git a/spec/validators_spec.rb b/spec/validators_spec.rb index 0f9809f..2d0ec6d 100644 --- a/spec/validators_spec.rb +++ b/spec/validators_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe 'default validators' do - def test_validator(payload, key, name, eligible, valid, *args) validator = Paradocs.registry.policies[name] validator = validator.new(*args) if validator.respond_to?(:new) @@ -11,76 +10,76 @@ def test_validator(payload, key, name, eligible, valid, *args) describe ':format' do it { - test_validator({key: 'Foobar'}, :key, :format, true, true, /^Foo/) - test_validator({key: 'Foobar'}, :key, :format, true, false, /^Bar/) - test_validator({foo: 'Foobar'}, :key, :format, false, true, /^Foo/) + test_validator({ key: 'Foobar' }, :key, :format, true, true, /^Foo/) + test_validator({ key: 'Foobar' }, :key, :format, true, false, /^Bar/) + test_validator({ foo: 'Foobar' }, :key, :format, false, true, /^Foo/) } end describe ':email' do it { - test_validator({key: 'foo@bar.com'}, :key, :email, true, true) - test_validator({key: 'foo@'}, :key, :email, true, false) - test_validator({foo: 'foo@bar.com'}, :key, :email, false, true) + test_validator({ key: 'foo@bar.com' }, :key, :email, true, true) + test_validator({ key: 'foo@' }, :key, :email, true, false) + test_validator({ foo: 'foo@bar.com' }, :key, :email, false, true) } end describe ':required' do it { - test_validator({key: 'foo'}, :key, :required, true, true) - test_validator({key: ''}, :key, :required, true, true) - test_validator({key: nil}, :key, :required, true, true) - test_validator({foo: 'foo'}, :key, :required, true, false) + test_validator({ key: 'foo' }, :key, :required, true, true) + test_validator({ key: '' }, :key, :required, true, true) + test_validator({ key: nil }, :key, :required, true, true) + test_validator({ foo: 'foo' }, :key, :required, true, false) } end describe ':present' do it { - test_validator({key: 'foo'}, :key, :present, true, true) - test_validator({key: ''}, :key, :present, true, false) - test_validator({key: nil}, :key, :present, true, false) - test_validator({foo: 'foo'}, :key, :present, true, false) + test_validator({ key: 'foo' }, :key, :present, true, true) + test_validator({ key: '' }, :key, :present, true, false) + test_validator({ key: nil }, :key, :present, true, false) + test_validator({ foo: 'foo' }, :key, :present, true, false) } end describe ':declared' do it { - test_validator({key: 'foo'}, :key, :declared, true, true) - test_validator({key: ''}, :key, :declared, true, true) - test_validator({key: nil}, :key, :declared, true, true) - test_validator({foo: 'foo'}, :key, :declared, false, true) + test_validator({ key: 'foo' }, :key, :declared, true, true) + test_validator({ key: '' }, :key, :declared, true, true) + test_validator({ key: nil }, :key, :declared, true, true) + test_validator({ foo: 'foo' }, :key, :declared, false, true) } end describe ':options' do it { - test_validator({key: 'b'}, :key, :options, true, true, %w(a b c)) - test_validator({key: 'd'}, :key, :options, true, false, %w(a b c)) - test_validator({key: ['c', 'b']}, :key, :options, true, true, %w(a b c)) - test_validator({key: ['c', 'b', 'd']}, :key, :options, true, false, %w(a b c)) - test_validator({foo: 'b'}, :key, :options, false, true, %w(a b c)) + test_validator({ key: 'b' }, :key, :options, true, true, %w[a b c]) + test_validator({ key: 'd' }, :key, :options, true, false, %w[a b c]) + test_validator({ key: %w[c b] }, :key, :options, true, true, %w[a b c]) + test_validator({ key: %w[c b d] }, :key, :options, true, false, %w[a b c]) + test_validator({ foo: 'b' }, :key, :options, false, true, %w[a b c]) } end describe ':array' do it { - test_validator({key: ['a', 'b']}, :key, :array, true, true) - test_validator({key: []}, :key, :array, true, true) - test_validator({key: nil}, :key, :array, true, false) - test_validator({key: 'hello'}, :key, :array, true, false) - test_validator({key: 123}, :key, :array, true, false) - test_validator({foo: []}, :key, :array, true, true) + test_validator({ key: %w[a b] }, :key, :array, true, true) + test_validator({ key: [] }, :key, :array, true, true) + test_validator({ key: nil }, :key, :array, true, false) + test_validator({ key: 'hello' }, :key, :array, true, false) + test_validator({ key: 123 }, :key, :array, true, false) + test_validator({ foo: [] }, :key, :array, true, true) } end describe ':object' do it { - test_validator({key: {'a' =>'b'}}, :key, :object, true, true) - test_validator({key: {}}, :key, :object, true, true) - test_validator({key: ['a', 'b']}, :key, :object, true, false) - test_validator({key: nil}, :key, :object, true, false) - test_validator({key: 123}, :key, :object, true, false) - test_validator({foo: {}}, :key, :object, true, true) + test_validator({ key: { 'a' => 'b' } }, :key, :object, true, true) + test_validator({ key: {} }, :key, :object, true, true) + test_validator({ key: %w[a b] }, :key, :object, true, false) + test_validator({ key: nil }, :key, :object, true, false) + test_validator({ key: 123 }, :key, :object, true, false) + test_validator({ foo: {} }, :key, :object, true, true) } end end diff --git a/spec/whitelist_spec.rb b/spec/whitelist_spec.rb index 82645c7..deef396 100644 --- a/spec/whitelist_spec.rb +++ b/spec/whitelist_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -require "paradocs/whitelist" -require "paradocs/dsl" +require 'paradocs/whitelist' +require 'paradocs/dsl' -describe "classes including Whitelist module" do +describe 'classes including Whitelist module' do class TestWhitelist include Paradocs::DSL include Paradocs::Whitelist @@ -36,57 +36,57 @@ class TestWhitelist end end - describe ".filter!" do + describe '.filter!' do let(:schema) { TestWhitelist.schema(:request) } - let(:input) { + let(:input) do { - "unexpected" => "test", - from_config: "whitelisted", + 'unexpected' => 'test', + from_config: 'whitelisted', data: [ - "id" => 5, + 'id' => 5, name: nil, unexpected: nil, empty_array: [], - subschema_1: "subfield_1", + subschema_1: 'subfield_1', subfield_1: true, - subschema_2: "subfield_2", + subschema_2: 'subfield_2', subfield_2: true, empty_hash: {}, - "extra" => { + 'extra' => { id: 6, - name: "name", - unexpected: "unexpected", - empty_string: "" + name: 'name', + unexpected: 'unexpected', + empty_string: '' } ] } - } + end - before { Paradocs.config.whitelisted_keys = [:from_config]} + before { Paradocs.config.whitelisted_keys = [:from_config] } it "should filter not whitelisted attributes with different key's type" do whitelisted = TestWhitelist.new.filter!(input, schema) expect(whitelisted).to eq( { - unexpected: "[FILTERED]", - from_config: "whitelisted", + unexpected: '[FILTERED]', + from_config: 'whitelisted', data: [ { id: 5, - name: "[EMPTY]", - unexpected: "[EMPTY]", + name: '[EMPTY]', + unexpected: '[EMPTY]', empty_array: [], - subschema_1: "subfield_1", + subschema_1: 'subfield_1', subfield_1: true, - subschema_2: "[FILTERED]", + subschema_2: '[FILTERED]', subfield_2: true, empty_hash: {}, extra: { id: 6, - name: "[FILTERED]", - unexpected: "[FILTERED]", - empty_string: "[EMPTY]" + name: '[FILTERED]', + unexpected: '[FILTERED]', + empty_string: '[EMPTY]' } } ] @@ -94,31 +94,31 @@ class TestWhitelist ) end - context "when Paradocs.config.whitelist_coercion block is set" do - before { Paradocs.config.whitelist_coercion = Proc.new { |value, meta| meta[:type] != :string ? "FILTER" : value.to_s }} + context 'when Paradocs.config.whitelist_coercion block is set' do + before { Paradocs.config.whitelist_coercion = proc { |value, meta| meta[:type] != :string ? 'FILTER' : value.to_s } } - it "executes block for each value" do + it 'executes block for each value' do whitelisted = TestWhitelist.new.filter!(input, schema) expect(whitelisted).to eq( { - unexpected: "[FILTERED]", - from_config: "FILTER", + unexpected: '[FILTERED]', + from_config: 'FILTER', data: [ { - id: "5", - name: "[EMPTY]", - unexpected: "[EMPTY]", + id: '5', + name: '[EMPTY]', + unexpected: '[EMPTY]', empty_array: [], - subschema_1: "FILTER", - subfield_1: "FILTER", - subschema_2: "[FILTERED]", - subfield_2: "FILTER", + subschema_1: 'FILTER', + subfield_1: 'FILTER', + subschema_2: '[FILTERED]', + subfield_2: 'FILTER', empty_hash: {}, extra: { - id: "6", - name: "[FILTERED]", - unexpected: "[FILTERED]", - empty_string: "[EMPTY]" + id: '6', + name: '[FILTERED]', + unexpected: '[FILTERED]', + empty_string: '[EMPTY]' } } ]