Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
imdrasil committed Jun 11, 2017
0 parents commit baf5bbc
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/doc/
/lib/
/bin/
/.shards/

# Libraries don't need dependency lock
# Dependencies will be locked in application that uses them
/shard.lock
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: crystal
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2017 Roman Kalnytskyi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# factory

TODO: Write a description here

## Installation

Add this to your application's `shard.yml`:

```yaml
dependencies:
factory:
github: [your-github-name]/factory
```
## Usage
```crystal
require "factory"
```

TODO: Write usage instructions here

## Development

TODO: Write development instructions here

## Contributing

1. Fork it ( https://github.com/[your-github-name]/factory/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request

## Contributors

- [[your-github-name]](https://github.com/[your-github-name]) Roman Kalnytskyi - creator, maintainer
9 changes: 9 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: factory
version: 0.1.0

authors:
- Roman Kalnytskyi <moranibaca@gmail.com>

crystal: 0.22.0

license: MIT
12 changes: 12 additions & 0 deletions spec/factory_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "./spec_helper"

describe Factory do
describe "#build" do
# puts Factory.build(:super_test).inspect
hash = {"f6" => [1], "f1" => "try"}
puts Factory.build_super_test.inspect
puts Factory.build_super_test.inspect
puts Factory.build_test.inspect
puts Factory.build_test(3, hash)
end
end
41 changes: 41 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require "spec"
require "../src/factory"

class Test
property f1 : String, f2 : Int32, f3 : Float64,
f4 : String?, f5 : Int32?, f6 : Array(Int32)?

def initialize(hash : Hash(String, String | Int32 | Float64 | Array(Int32)))
@f1 = hash["f1"].as(String)
@f2 = hash["f2"].as(Int32)
@f3 = hash["f3"].as(Float64)
@f6 = hash["f6"].as(Array(Int32)) if hash.has_key?("f6")
end

def self.tratata
rand(1..20)
end

def save
end
end

class TestFactory < Factory::Base
hash_type String | Int32 | Float64 | Array(Int32)
sequence(:f1) { |i| "some#{i}" }
attr :f2, ->{ Test.tratata }
attr :f3, rand, Float64
assign :f4, "assign"
assign :f5, ->{ rand(1..3) }

after_initialize do |t|
puts t.f1
puts "asd"
end
end

class SuperTestFactory < TestFactory
hash_type String | Int32 | Float64 | Array(Int32)
describe_class Test
attr :f1, "tome"
end
6 changes: 6 additions & 0 deletions src/factory.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require "./factory/*"

module Factory
# TODO: isn't used anywhere. Think to remove this
FACTORIES = {} of String => String
end
190 changes: 190 additions & 0 deletions src/factory/base.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
module Factory
class Base
def self.build
end

def self.after_initialize(obj)
end

def self.after_create(obj)
end

def self.before_create(obj)
end

def self.attributes
{} of String => String
end

macro after_initialize(&block)
def self.after_initialize({{block.args[0].id}})
super
{{block.body}}
end
end

macro before_create(&block)
def self.before_create({{block.args[0].id}})
super
{{block.body}}
end
end

macro after_create(&block)
def self.after_create({{block.args[0].id}})
super
{{block.body}}
end
end

macro describe_class(klass)
{% CLASS_NAME.push(klass.stringify) %}
end

macro hash_type(type)
{% HASH_TYPE.push(type.stringify) %}
end

macro attr(name, value, klass = nil)
{% if !value.is_a?(ProcLiteral) %}
{% if klass != nil %}
@@{{name.id}} : {{klass}} = {{value}}
{% else %}
@@{{name.id}} = {{value}}
{% end %}
{% ATTRIBUTES[name.id.stringify] = "plain" %}
{% else %}
{% ATTRIBUTES[name.id.stringify] = value.id.stringify %}
{% end %}
end

macro assign(name, value, klass = nil)
{% if !value.is_a?(ProcLiteral) %}
{% if klass != nil %}
@@assign_{{name.id}} : {{klass}} = {{value}}
{% else %}
@@assign_{{name.id}} = {{value}}
{% end %}
{% ASSIGNS[name.id.stringify] = "plain" %}
{% else %}
{% ASSIGNS[name.id.stringify] = value.id.stringify %}
{% end %}
end

macro sequence(name, init = 0, &block)
class {{(name.id.stringify.camelcase + "Sequence").id}}
@@start = {{init}}

def self.next
@@start += 1
@@start - 1
end
end

{% SEQUENCES[name.id.stringify] = "->(#{block.args[0]} : Int32) { #{block.body.id} }" %}
end

macro avoid_sequence(name)
{% STOP_SEQUENCE.push(name.id.stringify) %}
end

macro inherited
STOP_SEQUENCE = [] of String
SEQUENCES = {} of String => String
CLASS_NAME = [] of String
HASH_TYPE = [] of String
ATTRIBUTES = {} of String => String
ASSIGNS = {} of String => String
\{% if @type.superclass != Factory::Base %}
\{% for k, v in @type.superclass.constant("ASSIGNS") %}
\{% ASSIGNS[k] = v %}
\{% end %}

\{% for k, v in @type.superclass.constant("ATTRIBUTES") %}
\{% ATTRIBUTES[k] = v %}
\{% end %}

\{% for k, v in @type.superclass.constant("SEQUENCES") %}
\{% SEQUENCES[k] = v %}
\{% end %}
\{% end %}

macro finished
\{% if CLASS_NAME.empty? %}
\{% CLASS_NAME.push(@type.stringify.gsub(/Factory$/, "").id) %}
\{% end %}
module ::Factory
def self.build_\{{@type.stringify.gsub(/Factory$/, "").underscore.id}}(**args)
\{{@type}}.build(**args)
end

def self.build_\{{@type.stringify.gsub(/Factory$/, "").underscore.id}}(opts)
\{{@type}}.build(opts)
end

def self.build_\{{@type.stringify.gsub(/Factory$/, "").underscore.id}}(count : Int32, **args)
arr = [] of \{{CLASS_NAME.last.id}}
count.times { arr << \{{@type}}.build(**args) }
arr
end

def self.build_\{{@type.stringify.gsub(/Factory$/, "").underscore.id}}(count : Int32, opts)
arr = [] of \{{CLASS_NAME.last.id}}
count.times { arr << \{{@type}}.build(opts) }
arr
end
end

\{% Factory::FACTORIES[@type.stringify.gsub(/Factory$/, "").underscore] = @type.stringify %}

def self.build(opts)
attrs = attributes
opts.each do |k, v|
attrs[k.to_s] = v
end
obj = \{{CLASS_NAME.last.id}}.new(attrs)
\{% for k, v in ASSIGNS %}
obj.\{{k.id}} = \{% if v =~ /->/ %} \{{v.id}}.call \{% else %} @@assign_\{{k.id}} \{% end %}
\{% end %}
after_initialize(obj)
obj
end

def self.build(**opts)
attrs = attributes
opts.each do |k, v|
attrs[k.to_s] = v
end
obj = \{{CLASS_NAME.last.id}}.new(attrs)
\{% for k, v in ASSIGNS %}
obj.\{{k.id}} = \{% if v =~ /->/ %} \{{v.id}}.call \{% else %} @@assign_\{{k.id}} \{% end %}
\{% end %}
after_initialize(obj)
obj
end

def self.attributes
\{% if HASH_TYPE.empty? %}
{
\{% for k, v in ATTRIBUTES %}
\{{k.id.stringify}} => \{% if v =~ /->/ %} \{{v.id}}.call \{% else %} @@\{{k.id}} \{% end %},
\{% end %}
\{% for k, v in SEQUENCES %}
\{{k.id.stringify}} => (\{{v.id}}.call(\{{(k.camelcase + "Sequence").id}}.next))
\{% end %}
}
\{% else %}
hash = {} of String => \{{HASH_TYPE.last.id}}
\{% for k, v in ATTRIBUTES %}
hash[\{{k.id.stringify}}] = \{% if v =~ /->/ %} \{{v.id}}.call \{% else %} @@\{{k.id}} \{% end %}
\{% end %}
\{% for k, v in SEQUENCES %}
hash[\{{k.id.stringify}}] = (\{{v.id}}.call(\{{(k.camelcase + "Sequence").id}}.next))
\{% end %}
hash
\{% end %}
end
end
end
end
end
Empty file added src/factory/sequence.cr
Empty file.
3 changes: 3 additions & 0 deletions src/factory/version.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Factory
VERSION = "0.1.0"
end

0 comments on commit baf5bbc

Please sign in to comment.