{"id":17656524,"url":"https://github.com/imdrasil/factory","last_synced_at":"2025-05-07T11:43:05.168Z","repository":{"id":49105454,"uuid":"94192825","full_name":"imdrasil/factory","owner":"imdrasil","description":"Library for factorizing objects","archived":false,"fork":false,"pushed_at":"2021-06-28T20:01:32.000Z","size":37,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-21T08:14:05.157Z","etag":null,"topics":["crystal","factory","testing"],"latest_commit_sha":null,"homepage":null,"language":"Crystal","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/imdrasil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-13T09:03:04.000Z","updated_at":"2023-11-22T18:24:39.000Z","dependencies_parsed_at":"2022-08-27T23:22:14.242Z","dependency_job_id":null,"html_url":"https://github.com/imdrasil/factory","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imdrasil%2Ffactory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imdrasil%2Ffactory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imdrasil%2Ffactory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imdrasil%2Ffactory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/imdrasil","download_url":"https://codeload.github.com/imdrasil/factory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252873796,"owners_count":21817708,"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":["crystal","factory","testing"],"created_at":"2024-10-23T14:33:21.947Z","updated_at":"2025-05-07T11:43:05.145Z","avatar_url":"https://github.com/imdrasil.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Factory [![Build Status](https://travis-ci.org/imdrasil/factory.svg)](https://travis-ci.org/imdrasil/factory) [![Latest Release](https://img.shields.io/github/release/imdrasil/factory.svg)](https://github.com/imdrasil/factory/releases) [![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://imdrasil.github.io/factory/latest/)\n\nEasy to use but flexible factory definition utility. Could be used for testing purpose and for developing as well.\n\n## Installation\n\nAdd this to your application's `shard.yml`:\n\n```yaml\ndependencies:\n  factory:\n    github: imdrasil/factory\n```\n\n## Usage\n\n```crystal\nrequire \"factory\"\n```\n\nTo define new factory\n```crystal\nclass HumanFactory \u003c Factory::Base\nend\n```\n\nBy convenience this factory will builds `Human` class but this behavior can be overrided using `describe_class` macro:\n\n```crystal\nclass AdminFactory \u003c Factory::Base\n  describe_class User\nend\n```\n\nFactory will build class passing to constructor hash with string keys, so those class should be ready for this. To define attributes for passing to constructor use `attr` macro:\n\n```crystal\nclass TestFactory \u003c Factory::Base\n  attr :f1, \"Ivan\"\n  attr :f2, rand, Float64\n  attr :f3, -\u003e { rand(1..3) }\nend\n```\n\nAttributes, passed as `Proc` will be executed each time. Other ones - only once and cached. If type could be analyzed (as with calling `rand` upper), you can specify exact type passing it as third parameter.\n\nThere is also assign strategy using `assign` macro. Using it all attributes will be assigned after initializing.\n\n```crystal\nclass TestFactory \u003c Factory::Base\n  assign :f1, \"Ivan\"\n  assign :f2, rand, Float64\n  assign :f3, -\u003e { rand(1..3 }\nend\n\n# Will be do smth like\nobj = Test.new\nobj.f1 = TestFactory.f1 # \"Ivan\"\nobj.f2 = TestFactory.f2 # 0.61 - just random value shared across all object\nobj.f3 = -\u003e { rand(1..3) }.call\n```\n\nIf you specify no `attr` - will call construtor without any arguments and you will not be able pass anything to it.\n\nIf you need to specify exact type of given hash value use `argument_type`:\n\n```crystal\nclass Test\n  @@static = 1\n  @@dynamic = 1\n  property f1 : String, f2 : Int32, f3 : Float64,\n    f4 : String?, f5 : Int32?, f6 : Array(Int32)?\n\n  def initialize(hash)\n    @f1 = hash[\"f1\"].as(String)\n    @f2 = hash[\"f2\"].as(Int32)\n    @f3 = hash[\"f3\"].as(Float64)\n    @f6 = hash[\"f6\"].as(Array(Int32)) if hash.has_key?(\"f6\")\n  end\nend\n\nclass TestFactory \u003c Factory::Base\n  argument_type String | Int32 | Float64 | Array(Int32)\n  attr :f1, \"some\"\n  attr :f2, 1\n  attr :f3, rand, Float64\nend\n```\n\nAlso `after_initialize` callback could be specified:\n\n```crystal\nclass TestFactory \u003c Factory::Base\n  after_initialize do |t|\n    super # if you want parrent one to be inked as well\n    t.f1.not_nil! += 1\n  end\nend\n```\n\nBuilder method could be specified as well:\n\n```crystal\nclass TestFactory \u003c Factory::Base\n  # here is default builder\n  initialize_with do |hash, traits|\n    obj = Test.new(hash)\n    make_assigns(obj, traits) # makes all assignements (traits will be described later)\n    obj\n  end\nend\n```\n\nTo specify sequence of some attributes (only allowed as attr hook) use `sequence`:\n\n```crystal\nsequence(:f1) { |i| \"user#{i}@example.com\" }\n```\n\nYou could inherite from existing factory and override some parameters:\n```crystal\nclass HumanFactory \u003c Factory::Base\n  describe_class User\n  attr :f1, \"asd\"\nend\n\nclass AdminFactory \u003c HumanFactory\n  attr :f1, \"admin\"\n  assign :f2, 1\nend\n```\n\nChild factory inherits all attrs, assigns, traits, sequences, callbacks, class names, has value type.\n\nTo group several attributes or assignments use trait. \n\n```crystal\nclass HumanFactory \u003c Factory::Base\n  trait :homo do\n    attr :iq, 50\n  end\nend\n```\n\nTraits can't specify callbacks, described type, hash value type.\n\nTo build object direct call could be used\n```crystal\nHumanFactory.build\nHumanFactory.build(some_attr: \"asd\")\nHumanFactory.build({\"some_attr\" =\u003e \"asd\")\nHumanFactory.build([\"some_trait\"], some_attr: \"asd\")\nHumanFactory.build([\"some_trait\"], {\"some_attr\" =\u003e \"asd\"})\n```\n\nAlso helper methods are defined as well\n\n```crystal\nFactory.build_human\nFactory.build_human(some_attr: \"asd\")\nFactory.build_human({\"some_attr\" =\u003e \"asd\")\nFactory.build_human([\"some_trait\"], some_attr: \"asd\")\nFactory.build_human([\"some_trait\"], {\"some_attr\" =\u003e \"asd\"})\n# also you can specify count as first parameter in any of thos methods\nFactory.build_human(3, [\"some_trait\"], {\"some_attr\" =\u003e \"asd\"})\n```\n\n#### Jennifer Support\n\nTo create factory for (Jennifer)[https://github.com/imdrasil/jennifer.cr] model \n\n```crystal\n# require all jennifer staff and models\nrequire \"factory\"\nrequire \"factory/jennifer\"\n\nclass FilmFactory \u003c Factory::Jennifer::Base\n  attr :rating, 5\n  assign :name, \"Test Film\" \n\n  trait :bad do\n    assign :rating, 0\n  end\nend\n```\n\nIt provides direct creating methods same as for building:\n\n```crystal\nFilmFactory.create([:bad], {:name =\u003e \"Atilla\"})\n```\n\nAlso any association could be described on the factory or trait level:\n\n```crystal\nclass FilmFactory \u003c Factory::Jennifer::Base\n  association :author\n  association :actor, UserFactory, options: {name: \"Artemius Fault\"}\nend\n```\n\nAllowed arguments:\n\n- `:name` - first argument - represent model association name (mandatory)\n- `:factory` - represents factory class (optional); is defaulted from association name\n- `:strategy` - represents creation strategy; optional; default is \"create\" (also \"build\" is allowed)\n- `:options` - represents extra arguments to association factory; optional \n\n## Development\n\nFor development postgres is required because of testing integration with Jennifer.\n\nPossible next tasks:\n\n- think  about adding assigning via hash or named tuble argument;\n- adding `%attr` to traits.\n\n\n## Contributing\n\n1. Fork it ( https://github.com/imdrasil/factory/fork )\n2. Create your feature branch (git checkout -b my-new-feature)\n3. Commit your changes (git commit -am 'Add some feature')\n4. Push to the branch (git push origin my-new-feature)\n5. Create a new Pull Request\n\n## Contributors\n\n- [imdrasil](https://github.com/imdrasil) Roman Kalnytskyi - creator, maintainer\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimdrasil%2Ffactory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimdrasil%2Ffactory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimdrasil%2Ffactory/lists"}