{"id":17159261,"url":"https://github.com/akuzko/flatter","last_synced_at":"2025-04-13T14:06:59.532Z","repository":{"id":56846727,"uuid":"43566384","full_name":"akuzko/flatter","owner":"akuzko","description":"Deep object graph to a plain flat properties mapper.","archived":false,"fork":false,"pushed_at":"2017-02-26T14:38:59.000Z","size":62,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-13T14:06:53.251Z","etag":null,"topics":["activemodel","activerecord","mapper"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/akuzko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-02T17:24:35.000Z","updated_at":"2017-02-22T15:08:57.000Z","dependencies_parsed_at":"2022-09-09T01:01:12.216Z","dependency_job_id":null,"html_url":"https://github.com/akuzko/flatter","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akuzko%2Fflatter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akuzko%2Fflatter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akuzko%2Fflatter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akuzko%2Fflatter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akuzko","download_url":"https://codeload.github.com/akuzko/flatter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248724639,"owners_count":21151561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activemodel","activerecord","mapper"],"created_at":"2024-10-14T22:13:48.868Z","updated_at":"2025-04-13T14:06:59.514Z","avatar_url":"https://github.com/akuzko.png","language":"Ruby","readme":"# Flatter\n\n[![build status](https://secure.travis-ci.org/akuzko/flatter.png)](http://travis-ci.org/akuzko/flatter)\n[![github release](https://img.shields.io/github/release/akuzko/flatter.svg)](https://github.com/akuzko/flatter/releases)\n\nThis gem supersedes [FlatMap](https://github.com/TMXCredit/flat_map) gem. With\nonly it's core concepts in mind it has been written from complete scratch to\nprovide more pure, clean, extensible code and reliable functionality.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'flatter'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install flatter\n\n## Usage\n\nIf you happen to use `FlatMap` gem , check out\n[Flatter and FlatMap: What's Changed](https://github.com/akuzko/flatter/wiki/Flatter-and-FlatMap:-What's-Changed) wiki page.\n\nFlatter's main working units are instances of `Mapper` class. **Mappers** are essentially\nwrappers around your related ActiveModel-like objects, map their attributes to mapper's\naccessors via **mappings**, can be **mounted** by other mappers, and can define flexible\nbehavior via **traits**. Let's cover this topics one by one.\n\n### Mappings\n\nMappings represent a mapper's property, which maps it to some attribute of the\ntarget object. Since eventually mappers are used in combination with each other, it is\nbetter to map model's attribute with a unique \"full name\" to avoid collisions, for example:\n\n```ruby\n# models:\nclass Person\n  include ActiveModel::Model\n\n  attr_accessor :first_name, :last_name\nend\n\nclass Group\n  include ActiveModel::Model\n\n  attr_accessor :name\nend\n\nclass Department\n  include ActiveModel::Model\n\n  attr_accessor :name\nend\n\n# mappers:\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n  # it's ok, since :first_name and :last_name attributes are\n  # not likely to be used somewhere else\nend\n\nclass GroupMapper \u003c Flatter::Mapper\n  map group_name: :name\n  # maps mapper's :group_name attribute to target's :name attribute\nend\n\nclass DepartmentMapper \u003c Flatter::Mapper\n  map department_name: :name\n  # maps mapper's :department_name attribute to target's :name attribute\nend\n```\n\n#### Mapping Options\n\n- `:reader` Allows to add a custom logic for reading target's attribute.\n  When value is `Symbol`, calls a method defined by a **mapper** class.\n  If that method accepts an argument, mapping name will be passed to it.\n  When value is `Proc`, it is executed in context of mapper object, yielding\n  mapping name if block has arity of 1. For other arbitrary objects (including\n  `String`s) will simply return that object.\n\n- `:writer` Allows to control a way how value is assigned (written).\n  When value is `Symbol`, calls a method defined by a **mapper** class, passing\n  a value to it. If that method accepts second argument, mapping name will be\n  additionally passed to it.\n  When value is `Proc`, it is executed in context of mapper object, yielding\n  value and optionally mapping name if block has arity of 2. For other values will\n  raise error.\n\n### Mountings\n\nStand-alone mappers provide not very much benefit. However, mappers have a powerful\nability to be mounted on top of each other. When mapper mounts another one, it\ngains access to all of it's mappings, and they become accessible in a plain way.\n\nFor example, having `Person`, `Department` and `Group` classes defined above with\nadditional sample relationship we might have:\n\n```ruby\n# models:\nclass Person\n  def department\n    @department ||= Department.new(name: 'Default')\n  end\n\n  def group\n    @group ||= Group.new(name: 'General')\n  end\nend\n\n# mappers:\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n\n  mount :department\n  mount :group\nend\n\nperson = Person.new(first_name: 'John', last_name: 'Smith')\nmapper = PersonMapper.new(person)\n\nmapper.read # =\u003e\n  # { 'first_name'      =\u003e 'John',\n  #   'last_name'       =\u003e 'Smith',\n  #   'department_name' =\u003e 'Default',\n  #   'group_name'      =\u003e 'General' }\n\nmapper.group_name = 'Managers'\nperson.group.name # =\u003e \"Managers\"\n```\n\n#### Mounting Options\n\n- `:mapper_class_name` Name of the mapper class (`String`) if it cannot be\n  determined from the mounting name itself. By default it is camelized name\n  followed by 'Mapper', for example, for `:group` mounting, default mapper\n  class name is `'GroupMapper'`.\n\n- `:mapper_class` Used mostly internally, but allows to specify mapper class\n  itself. Has more priority than `:mapper_class_name` option.\n\n- `:target` Allows to manually set mounted mapper's target. By default target is\n  obtained from mounting mapper's target by sending it mounting name. In example\n  above target for `:group` mapping was obtained by sending `:group` method to\n  `person` object, which was the target of root mapper.\n  When value is `String` or `Symbol`, it is considered as a method name of the\n  **mapper**, which is called with no arguments.\n  When value is `Proc`, it is called yielding mapper's target to it.\n  For other objects, objects themselves are used as targets.\n\n- `:traits` Allows to specify a list of traits to be applied for mounted mappers.\n  See **Traits** section bellow.\n\n### Callbacks\n\nMappers include `ActiveModel::Validation` module and thus support `ActiveSupport`'s\ncallbacks. Additionally, `:save` callbacks have been defined for `Flatter::Mapper`,\nso you can do something like `set_callback :save, :after, :send_invitation`.\n\n### Mapper and Target Validations\n\nIf mapper's target responds to `valid?` method, it will be called upon mapper's\nvalidation. If target is invalid, mapper will receive `:target, :invalid` error.\nAdditionally, all target's errors on attributes that have declared mapping will\nbe consolidated with mapper's errors.\n\n### Traits\n\nTraits are another powerful mapper ability. Traits allow to encapsulate named sets\nof additional definitions, and optionally use them on mapper initialization or\nwhen mounting mapper in other one. Everything that can be defined within the mapper\ncan be defined withing the trait. For example (suppose we have some additional\n`:with_counts` trait defined on `DepartmentMapper` alongside with model relationships):\n\n```ruby\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n\n  trait :full_info do\n    map :middle_name, dob: :date_of_birth\n\n    mount :group\n  end\n\n  trait :with_department do\n    mount :department, traits: :with_counts\n  end\nend\n\nmapper = PersonMapper.new(person)\nfull_mapper = PersonMapper.new(person, :full_info, :with_department)\n\nmapper.read # =\u003e\n  # { 'first_name'      =\u003e 'John',\n  #   'last_name'       =\u003e 'Smith' }\n\nfull_mapper.read # =\u003e\n  # { 'first_name'              =\u003e 'John',\n  #   'last_name'               =\u003e 'Smith',\n  #   'middle_name'             =\u003e nil,\n  #   'dob'                     =\u003e Wed, 18 Feb 1981,\n  #   'group_name'              =\u003e 'General'\n  #   'department_name'         =\u003e 'Default',\n  #   'department_people_count' =\u003e 31 }\n```\n\n#### Traits and callbacks\n\nSince traits are internally mappers (which allows you to define everything mapper\ncan), you can also define callbacks on traits, allowing you to dynamically opt-in,\nopt-out and reuse functionality. Keep in mind that `ActiveModel`'s validation\nroutines are also just a set of callbacks, meaning that you can define sets of\nvalidation in traits, mix them together in any way. For example:\n\n```ruby\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n\n  trait :registration do\n    map personal_email: :email\n\n    validates_presence_of :first_name, :last_name\n    validates :personal_email, :presence: true, email: true\n\n    set_callback :save, :after, :send_greeting\n\n    def send_greeting\n      PersonMailer.greeting(target).deliver_now\n    end\n  end\nend\n```\n\n#### Traits and shared methods\n\nDespite the fact traits are separate objects, you can call methods defined in\none trait from another trait, as well as methods defined in root mapper itself\n(such as attribute methods). That allows you to treat traits as parts of the\nroot mapper.\n\n#### Inline extension traits\n\nWhen initializing a mapper, or defining a mounting, you can pass a block with\nadditional definitions. This block will be treated as an anonymous extension trait.\nFor example, let's suppose that `email` from example above is actually a part\nof another `User` model that has it's own `UserMapper` with defined `:email` mapping.\nThen we might have something like:\n\n```ruby\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n\n  trait :registration do\n    validates_presence_of :first_name, :last_name\n\n    mount :user do\n      validates :email, :presence: true, email: true\n      set_callback :save, :after, :send_greeting\n\n      def send_greeting\n        UserMailer.greeting(target).deliver_now\n      end\n    end\n  end\nend\n\n```\n\n### Processing Order\n\n`Flatter` mappers have a well-defined processing order of mountings (including\ntraits), best shown by example. Suppose we have something like this:\n\n```ruby\nclass AMapper \u003c Flatter::Mapper\n  trait :trait_a1 do\n    mount :b, traits: :trait_b do\n      # extension callbacks definitions\n    end\n  end\n\n  trait :trait_a2 do\n    mount :c\n  end\n\n  mount :d\nend\n```\n\nMappers are processed (validated and saved) from top to bottom. Let's have initialized\n\n```ruby\nmapper = AMapper.new(a, :trait_a2, :trait_a1)\n```\n\nPlease note traits order, it is very important: `:trait_a2` goes first, so it's\ncallbacks and mountings will go first too. So if we call `mapper.save`, we will have\nfollowing execution order (suppose, we have defined callbacks for all traits and mappers):\n\n```\ntrait_a2.before_save\ntrait_a1.before_save\nA.before_save\nA.save\nA.after_save\ntrait_a1.after_save\ntrait_a2.after_save\nC.before_save\nC.save\nC.after_save\ntrait_b.before_save\nB_extension.before_save\nB.before_save\nB.save\nB.after_save\nB_extension.after_save\ntrait_b.after_save\nD.before_save\nD.save\nD.after_save\n```\n\n### Attribute methods\n\nAll mappers can access mapped values via attribute methods that match mapping names.\nThat allows you to easily use mappers for building forms or developing other\nfunctionality.\n\nYou also have reader methods that match mounting names. They will return\nvalue read for a specific mounting (including it's own nested mountings).\nFor example:\n\n```ruby\nclass UserMapper \u003c Flatter::Mapper\n  map :email\n\n  mount :person do\n    map :first_name, :last_name\n\n    mount :phone do\n      map phone_number: :number\n    end\n  end\nend\n\nmapper = UserMapper.new(User.new)\nmapper.email = \"user@email.com\"\nmapper.first_name = \"John\"\nmapper.phone_number = \"111-222-3333\"\n\nmapper.read # =\u003e\n  # { \"email\" =\u003e \"user@email.com\",\n  #   \"first_name\" =\u003e \"John\",\n  #   \"last_name\" =\u003e nil,\n  #   \"phone_number\" =\u003e \"111-222-3333\" }\n\nmapper.person # =\u003e\n  # { \"first_name\" =\u003e \"John\",\n  #   \"last_name\" =\u003e nil,\n  #   \"phone_number\" =\u003e \"111-222-3333\" }\n\nmapper.phone # =\u003e\n  # { \"phone_number\" =\u003e \"111-222-3333\" }\n```\n\nPlease also read \"Attribute methods\" subsection for Collections bellow\nfor details on what methods do you get when mapping collections.\n\n### Collections\n\nStarting from version `0.2.0`, Flatter mappers also support handling of collections.\n\n#### Declaration\n\nTo declare a mapper that will handle a collection of items, simply mount it\nwith a pluralized name:\n\n```ruby\nclass PersonMapper \u003c Flatter::Mapper\n  mount :phones\nend\n```\n\nIf you need to mount a mapper with already pluralized name to handle single\nitem in common fashion, mount it with `collection: false` option:\n\n```ruby\nclass SeamstressMapper \u003c Flatter::Mapper\n  mount :scissors, collection: false\nend\n```\n\nIf you need your root mapper to handle a collection of items, initialize it\nwith `collection: true` option:\n\n```ruby\nmapper = PhoneMapper.new(user.phones, collection: true)\n```\n\n#### Key\n\nMapper that will be used for mapping collection should define `key` mapping.\n`Flatter` offers `key` class-level method to do it easier. You can call it\non mapper definition:\n\n```ruby\nclass PhoneMapper\n  key :id\nend\n```\n\nor when mounting mapper for collection handling:\n\n```ruby\nclass PersonMapper\n  mount :phones do\n    key -\u003e { target.number }\n  end\nend\n```\n\nAll non-nil `key` mappings have to have unique value (within collection they\nbelong to). Otherwise `NonUniqKeysError` will be raised on reading. All items\nthat have `nil` as a key value are considered to be \"new items\". All such\nitems are removed from collection on writing.\n\n#### Reading\n\nAs well as can be expected, collection mappers provide an array of hashes\nderived from reading from all items in the collection. Each hash in this array\nwill have `\"key\"` key for item identification. It should be used for writing\n(see bellow). For example:\n\n```ruby\nclass CompanyMapper \u003c Flatter::Mapper\n  map company_name: :name\n\n  mount :departments do\n    key :id\n\n    mount :location\n  end\nend\n\nclass DepartmentMapper \u003c Flatter::Mapper\n  map department_name: :name\nend\n\nclass LocationMapper \u003c Flatter::Mapper\n  map location_name: :name\nend\n\n# ...\n\nmapper = CompanyMapper.new(company)\n\nmapper.read # =\u003e\n  # { \"company_name\" =\u003e \"Web Developers, Inc.\",\n  #   \"departments\"  =\u003e [{\n  #     \"key\" =\u003e 1,\n  #     \"department_name\" =\u003e \"R \u0026 D\",\n  #     \"location_name\" =\u003e \"Good Office\"\n  #   }, {\n  #     \"key\" =\u003e 2,\n  #     \"department_name\" =\u003e \"QA\",\n  #     \"location_name\" =\u003e \"QA Office\"\n  #   }]\n  # }\n\n```\n\n#### Writing\n\nTo update collection items, you should pass an array of hashes to it's mapper.\nValue of the `:key` key of each hash is important and defines how each set of\nparams will be used.\n\n- If `key` is present in the original collection, `params` hash will be used\n  to update mapped item via `write` method\n\n- If `key` is `nil`, params are treated as attributes for the new record, so\n  new instance of mapped target class is created and updated via `write` method.\n\n- In original collection, *all* items with keys that are not listed in given\n  array of hash params considered to be marked for destruction and corresponding\n  items will be removed from mapped collection. The same concerns for *all*\n  current items in collection, which have `key` mapped to `nil`.\n\nExample:\n\n```ruby\ncompany.departments.map(\u0026:id)   # =\u003e [1, 2]\ncompany.departments.map(\u0026:name) # =\u003e [\"R \u0026 D\", \"QA\"]\n\ncompany_mapper.write(departments: [\n  {key: 1, department_name: \"D \u0026 R\"},\n  {department_name: \"Testers\"}\n])\n\ncompany.departments.map(\u0026:id)   # =\u003e [1, nil]\ncompany.departments.map(\u0026:name) # =\u003e [\"D \u0026 R\", \"Testers\"]\n```\n\n#### Attribute Methods\n\nWhen you use mappers to map collection of items, attribute method behavior\nis slightly different. For example, when you have\n\n```ruby\nclass PersonMapper \u003c Flatter::Mapper\n  map :first_name, :last_name\n  mount :phone\nend\n\nclass DepartmentMapper \u003c Flatter::Mapper\n  mount :people do\n    key :id\n  end\nend\n```\n\n`department_mapper.first_name` no longer able to return specific value, since it's\nnot clear which first name should it be. Thus, when mapper is mounted as\na collection item, instead of singular value accessors you gain pluralized\nreader methods:\n\n```ruby\n  # all first_names of all people of the mapped department:\n  department_mapper.first_names # =\u003e [\"John\", \"Derek\"]\n```\n\nThe same concerns for all nested (singular or collection) mappings and mountings\nunder collection mapper:\n\n```ruby\n  # all phone number of all people of the mapped department\n  department_mapper.phone_numbers # =\u003e [\"111-222-3333\", \"222-111-33333\"]\n\n  # all the people\n  department_mapper.people # =\u003e\n  # [{\"first_name\" =\u003e \"John\", \"last_name\" =\u003e \"Smith\", \"key\" =\u003e 1, \"phone_number\" =\u003e \"111-222-3333\"},\n  #  {\"first_name\" =\u003e \"Derek\", \"last_name\" =\u003e \"Parker\", \"key\" =\u003e 2, \"phone_number\" =\u003e \"222-111-3333\"}]\n\n  # all phones (note the :phone mapper mounted on :people, opposed to it's :phone_number mapping)\n  department_mapper.phones # =\u003e\n  # [{\"phone_number\" =\u003e \"111-222-3333\"}, {\"phone_number\" =\u003e \"222-111-33333\"}]\n```\n\nPlease note that attempt to use writer method to update collection of mappings,\nsuch as `first_names=` will raise runtime `\"Cannot directly write to a collection\"`\nerror. To update collection items and their data you have to use `write`/`apply`\nmethods to utilize `key`-dependent logic to properly update your collection items\nalongside with all nested mappings/mountings they might have.\n\n#### Errors\n\nSince all errors after validation process are consolidated into a plain hash\nof errors, there is a need to distinct errors of one collection items from\nanother ones. To achieve this, Flatter adds special prefix to error key, which is\nformed from collection name and item **index** (not id or key). For example:\n\n```ruby\nclass Person\n  include ActiveModel::Model\n\n  attr_accessor :name, :age\nend\n\nclass Department\n  include ActiveModel::Model\n\n  attr_accessor :name\n\n  def people\n    @people ||= []\n  end\nend\n\nclass PersonMapper \u003c Flatter::Mapper\n  map :age, person_name: :name\n\n  validates :age, numericality: {only_integer: true, greater_than_or_equal_to: 1}\nend\n\nclass DepartmentMapper \u003c Flatter::Mapper\n  map department_name: :name\n\n  mount :people\nend\n\ndepartment = Department.new\nmapper = DepartmentMapper.new(department)\nmapper.apply(people: [\n  { person_name: \"John\", age: \"22.5\" },\n  { person_name: \"Dave\", age: \"18\" },\n  { person_name: \"Kile\", age: \"0\" }\n]) # =\u003e false\n\nmapper.errors.messages # =\u003e\n  # { :\"people.0.age\" =\u003e [\"must be an integer\"],\n  #   :\"people.2.age\" =\u003e [\"must be greater than or equal to 1\"] }\n```\n\n### Extensions\n\nAside from core functionality and behavior described above, there is also\nnumber of handy [extensions](https://github.com/akuzko/flatter/wiki/Extensions)\n(which originally were hosted in their own gem, but now are the part of the flatter)\nthat have aim to help you use mappers more efficiently. At this point there\nare following extensions:\n\n- `:multiparam` Allows you to define multiparam mappings by adding `:multiparam`\n  option to mapping. Works pretty much like `Rails` multiparam attribute assignment.\n- `:skipping` Allows to skip mappers (mountings) from the processing chain by\n  calling `skip!` method on a particular mapper. When used in before validation\n  callbacks, for example, allows you to ignore some extra processing.\n- `:order` Allows you to manually control processing order of mappers and their\n  mountings. Provides `:index` option for mountings, which can be either a Number,\n  which means order for both validation and saving routines, or a hash like\n  `index: {validate: -1, save: 2}`. By default all mappers have index of `0` and\n  processed from top to bottom.\n- `:active_record` Very useful extension that allows you to effectively use mappers\n  when working with ActiveRecord objects with defined relationships and associations\n  that form a structured graph that you want to work with as a plain data structure.\n\n### Public API\n\nSome methods of the public API that should help you building your mappers:\n\n#### Mapper methods\n\n- `name` - return a mapper name.\n\n- `target` - returns mapper target - an object mapper extracts values from\n  and assigns values to using defined mappings.\n\n- `mappings` - returns a plain hash of all the mappings (including ones related\n  to mounted mappers) in a form of `{name \u003cString\u003e =\u003e mapping object \u003cMapping\u003e}`.\n  Note that for empty collections there will be no mentions of item mappings at all.\n  If collection has only one item, it's mappings will be listed as the rest.\n  If there are multiple same-named mappings, they will be listed in array.\n\n- `mapping_names` - returns a list of all **available** mappings. This differs\n  from `mappings.keys`, since `mapping_names` represents a list of all mappings\n  that may be used by mapper. Essentially, this is the list of mapper's\n  attribute accessor methods.\n\n- `mapping(name)` - returns a mapping with a `name` name. The same as `mappings[name.to_s]`\n\n- `mountings` - returns a plain hash of all mounted mappers (including all used traits)\n  in a form of `{name \u003cString\u003e =\u003e mapper object \u003cMapper\u003e}`. Just like in case with\n  mappings, mountings with same name will be listed in array.\n\n- `mounting_names` - returns a list of all **available** mountings. This represents\n  a list of reader methods that will return a sub-hash of specific mounting or\n  an array of such hashes for collections.\n\n- `mounting(name)` - finds a mounting by name. Best used for addressing singular\n  mountings within a mapper, but also has other internal usages under the hood\n  (see sources of `Flatter::Mapper::AttributeMethods` module).\n\n- `read` - returns a hash of all values obtained by all mappings in a form of\n  `{name \u003cString\u003e =\u003e value \u003cObject\u003e}`.\n\n- `write(params)` - for each defined mapping, including mappings from mounted\n  mappers and traits, passes value from params that corresponds to mapping name\n  to that mapping's `write` method.\n\n- `valid?` - runs validation routines and returns `true` if there are no errors.\n\n- `errors` - returns mapper's `Errors` object.\n\n- `save` - runs save routines. If target object responds to `save` method, will\n  call it and return it's value. Returns true otherwise. If multiple mappers\n  are mounted, returns `true` only if all mounted mappers returned `true` on saving\n  their targets.\n\n- `apply(params)` - writes `params`, runs validation and runs save routines if\n  validation passed.\n\n- `collection?` - returns `true` if mapper is a collection mapper.\n\n- `trait?` - returns `true` if mapper is a trait mapper.\n\n#### Mapping methods\n\n- `name` - returns mapping name.\n\n- `target_attribute` - returns an attribute name which mapping maps to.\n\n- `read` - reads value from target according to setup.\n\n- `read!` - tries to directly read value from target based on mapping's `target_attribute`\n  property. Ignores `:reader` option.\n\n- `write(value)` - assigns a `value` to target according to setup.\n\n- `write!(value)` - tries to directly assign a value to target based on mapping's\n  `target_attribute` property. Ignores `:writer` option.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run\n`rake spec` to run the tests. You can also run `bin/console` for an interactive\nprompt that will allow you to experiment.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/akuzko/flatter.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakuzko%2Fflatter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakuzko%2Fflatter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakuzko%2Fflatter/lists"}