parametric:Ruby应用程序的声明式输入模式

  • P7_772426
    了解作者
  • 32.1KB
    文件大小
  • zip
    文件格式
  • 0
    收藏次数
  • VIP专享
    资源类型
  • 0
    下载次数
  • 2022-04-18 04:39
    上传日期
参数 在Ruby对象中声明性地定义数据模式,并使用它们将白名单,验证或转换为程序的输入。 对于构建自定义API,搜索或表单对象很有用。 或者可以替代Rails的强参数(它不依赖于Rails,可以独立使用)。 架构图 定义架构 schema = Parametric :: Schema . new do field ( :title ) . type ( :string ) . present field ( :status ) . options ( [ "draft" , "published" ] ) . default ( "draft" ) field ( :tags ) . type ( :array ) end 填充和使用。 缺少键将返回默认值(如果提供)。 form = schema . resolve ( title : "A new blog post
parametric-master.zip
  • parametric-master
  • bench
  • struct_bench.rb
    1.2KB
  • lib
  • parametric
  • block_validator.rb
    1.8KB
  • schema.rb
    5.3KB
  • results.rb
    240B
  • context.rb
    815B
  • field_dsl.rb
    394B
  • field.rb
    2.8KB
  • version.rb
    74B
  • policies.rb
    2.5KB
  • registry.rb
    371B
  • struct.rb
    2.6KB
  • default_types.rb
    1.5KB
  • dsl.rb
    1.8KB
  • parametric.rb
    440B
  • spec
  • expand_spec.rb
    825B
  • schema_spec.rb
    8.2KB
  • schema_walk_spec.rb
    1KB
  • policies_spec.rb
    1.9KB
  • spec_helper.rb
    97B
  • schema_lifecycle_hooks_spec.rb
    4.2KB
  • field_spec.rb
    11KB
  • dsl_spec.rb
    4.4KB
  • struct_spec.rb
    6.5KB
  • custom_block_validator_spec.rb
    571B
  • validators_spec.rb
    3.7KB
  • bin
  • console
    335B
  • LICENSE.txt
    1KB
  • Gemfile
    144B
  • parametric.gemspec
    934B
  • Rakefile
    117B
  • .travis.yml
    40B
  • README.md
    26.4KB
  • .rspec
    31B
  • .gitignore
    170B
内容介绍
# Parametric [![Build Status](https://travis-ci.org/ismasan/parametric.png)](https://travis-ci.org/ismasan/parametric) [![Gem Version](https://badge.fury.io/rb/parametric.png)](http://badge.fury.io/rb/parametric) Declaratively define data schemas in your Ruby objects, and use them to whitelist, validate or transform inputs to your programs. Useful for building self-documeting APIs, search or form objects. Or possibly as an alternative to Rails' _strong parameters_ (it has no dependencies on Rails and can be used stand-alone). ## Schema Define a schema ```ruby schema = Parametric::Schema.new do field(:title).type(:string).present field(:status).options(["draft", "published"]).default("draft") field(:tags).type(:array) end ``` Populate and use. Missing keys return defaults, if provided. ```ruby form = schema.resolve(title: "A new blog post", tags: ["tech"]) form.output # => {title: "A new blog post", tags: ["tech"], status: "draft"} form.errors # => {} ``` Undeclared keys are ignored. ```ruby form = schema.resolve(foobar: "BARFOO", title: "A new blog post", tags: ["tech"]) form.output # => {title: "A new blog post", tags: ["tech"], status: "draft"} ``` Validations are run and errors returned ```ruby form = schema.resolve({}) form.errors # => {"$.title" => ["is required"]} ``` If options are defined, it validates that value is in options ```ruby form = schema.resolve({title: "A new blog post", status: "foobar"}) form.errors # => {"$.status" => ["expected one of draft, published but got foobar"]} ``` ## Nested schemas A schema can have nested schemas, for example for defining complex forms. ```ruby person_schema = Parametric::Schema.new do field(:name).type(:string).required field(:age).type(:integer) field(:friends).type(:array).schema do field(:name).type(:string).required field(:email).policy(:email) end end ``` It works as expected ```ruby results = person_schema.resolve( name: "Joe", age: "38", friends: [ {name: "Jane", email: "jane@email.com"} ] ) results.output # => {name: "Joe", age: 38, friends: [{name: "Jane", email: "jane@email.com"}]} ``` Validation errors use [JSON path](http://goessner.net/articles/JsonPath/) expressions to describe errors in nested structures ```ruby results = person_schema.resolve( name: "Joe", age: "38", friends: [ {email: "jane@email.com"} ] ) results.errors # => {"$.friends[0].name" => "is required"} ``` ### Reusing nested schemas You can optionally use an existing schema instance as a nested schema: ```ruby FRIENDS_SCHEMA = Parametric::Schema.new do field(:friends).type(:array).schema do field(:name).type(:string).required field(:email).policy(:email) end end person_schema = Parametric::Schema.new do field(:name).type(:string).required field(:age).type(:integer) # Nest friends_schema field(:friends).type(:array).schema(FRIENDS_SCHEMA) end ``` Note that _person_schema_'s definition has access to `FRIENDS_SCHEMA` because it's a constant. Definition blocks are run in the context of the defining schema instance by default. To preserve the original block's context, declare two arguments in your block, the defining schema `sc` and options has. ```ruby person_schema = Parametric::Schema.new do |sc, options| # this block now preserves its context. Call `sc.field` to add fields to the current schema. sc.field(:name).type(:string).required sc.field(:age).type(:integer) # We now have access to local variables sc.field(:friends).type(:array).schema(friends_schema) end ``` ## Built-in policies Type coercions (the `type` method) and validations (the `validate` method) are all _policies_. Parametric ships with a number of built-in policies. ### :string Calls `:to_s` on the value ```ruby field(:title).type(:string) ``` ### :integer Calls `:to_i` on the value ```ruby field(:age).type(:integer) ``` ### :number Calls `:to_f` on the value ```ruby field(:price).type(:number) ``` ### :boolean Returns `true` or `false` (`nil` is converted to `false`). ```ruby field(:published).type(:boolean) ``` ### :datetime Attempts parsing value with [Datetime.parse](http://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-parse). If invalid, the error will be added to the output's `errors` object. ```ruby field(:expires_on).type(:datetime) ``` ### :format Check value against custom regexp ```ruby field(:salutation).policy(:format, /^Mr\/s/) # optional custom error message field(:salutation).policy(:format, /^Mr\/s\./, "must start with Mr/s.") ``` ### :email ```ruby field(:business_email).policy(:email) ``` ### :required Check that the key exists in the input. ```ruby field(:name).required # same as field(:name).policy(:required) ``` Note that _required_ does not validate that the value is not empty. Use _present_ for that. ### :present Check that the key exists and the value is not blank. ```ruby field(:name).present # same as field(:name).policy(:present) ``` If the value is a `String`, it validates that it's not blank. If an `Array`, it checks that it's not empty. Otherwise it checks that the value is not `nil`. ### :declared Check that a key exists in the input, or stop any further validations otherwise. This is useful when chained to other validations. For example: ```ruby field(:name).declared.present ``` The example above will check that the value is not empty, but only if the key exists. If the key doesn't exist no validations will run. Note that any defaults will still be returned. ```ruby field(:name).declared.present.default('return this') ``` ### :declared_no_default Like `:declared`, it stops the policy chain if a key is not in input, but it also skips any default value. ```ruby field(:name).policy(:declared_no_default).present ``` ### :gt Validate that the value is greater than a number ```ruby field(:age).policy(:gt, 21) ``` ### :lt Validate that the value is less than a number ```ruby field(:age).policy(:lt, 21) ``` ### :options Pass allowed values for a field ```ruby field(:status).options(["draft", "published"]) # Same as field(:status).policy(:options, ["draft", "published"]) ``` ### :split Split comma-separated string values into an array. Useful for parsing comma-separated query-string parameters. ```ruby field(:status).policy(:split) # turns "pending,confirmed" into ["pending", "confirmed"] ``` ## Custom policies You can also register your own custom policy objects. A policy must implement the following methods: ```ruby class MyPolicy # Validation error message, if invalid def message 'is invalid' end # Whether or not to validate and coerce this value # if false, no other policies will be run on the field def eligible?(value, key, payload) true end # Transform the value def coerce(value, key, context) value end # Is the value valid? def valid?(value, key, payload) true end # merge this object into the field's meta data def meta_data {type: :string} end end ``` You can register your policy with: ```ruby Parametric.policy :my_policy, MyPolicy ``` And then refer to it by name when declaring your schema fields ```ruby field(:title).policy(:my_policy) ``` You can chain custom policies with other policies. ```ruby field(:title).required.policy(:my_policy) ``` Note that you can also register instances. ```ruby Parametric.policy :my_policy, MyPolicy.new ``` For example, a policy that can be configured on a field-by-field basis: ```ruby class AddJobTitle def initialize(job_title) @job_title = job_title end def message 'is invalid' end # Noop def eligible?(value, key, payload) true end # Add job title to value def coerce(value, key, context) "#{value}, #{@job_title}" end # Noop def valid?(value, key, payload) true end def meta_data {} end end # Register it Parametric.policy :job_title, AddJobTitle ``` Now you can reuse the same policy wit
评论
    相关推荐