fried-schema 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +85 -0
- data/Rakefile +6 -0
- data/bin/console +22 -0
- data/bin/setup +8 -0
- data/fried-schema.gemspec +36 -0
- data/lib/fried/schema.rb +8 -0
- data/lib/fried/schema/attribute.rb +6 -0
- data/lib/fried/schema/attribute/define_methods.rb +39 -0
- data/lib/fried/schema/attribute/define_reader.rb +27 -0
- data/lib/fried/schema/attribute/define_writer.rb +42 -0
- data/lib/fried/schema/attribute/definition.rb +39 -0
- data/lib/fried/schema/attributes_to_hash.rb +36 -0
- data/lib/fried/schema/data_entity.rb +33 -0
- data/lib/fried/schema/definition.rb +40 -0
- data/lib/fried/schema/get_attribute.rb +23 -0
- data/lib/fried/schema/hash_to_attributes.rb +37 -0
- data/lib/fried/schema/set_attribute.rb +24 -0
- data/lib/fried/schema/set_attribute_without_checks.rb +24 -0
- data/lib/fried/schema/set_defaults.rb +35 -0
- data/lib/fried/schema/struct.rb +47 -0
- data/lib/fried/schema/version.rb +5 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 23815d2c6643330c7882829e738c0443011ba57a
|
4
|
+
data.tar.gz: 758119e620df5f4a1b7f788a68332fc093142ed0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 717d5115e4e819e470e8643b3d6948cd2721bb3cb27f8225a687315bae9af01af66244a6e4778ebd0ccc48fca2ada09ed201fce8e9ae9745635771e9cc40b30c
|
7
|
+
data.tar.gz: 378fc6154e2968f271405a843aa3f9aefdb285033c01c66d011a712364a969a6a5aa9dc716ee61f98461532051c2ddc89e353d4ebd7ddb56652e511189037e69
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Francesco Belladonna
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Fried::Schema [![Build Status][test-badge]][test-link]
|
2
|
+
|
3
|
+
Struct definition with type safety
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'fried-schema'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install fried-schema
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Two modules are exposed:
|
24
|
+
|
25
|
+
- `Fried::Schema::Struct`
|
26
|
+
- `Fried::Schema::DataEntity`
|
27
|
+
|
28
|
+
### Struct
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class Person
|
32
|
+
include Fried::Schema::Struct
|
33
|
+
|
34
|
+
# attribute_name, type_checking, default_value (optional)
|
35
|
+
# default value could be anything. However if it's a Proc, it will be
|
36
|
+
# evaluated at initialization
|
37
|
+
attribute :name, String, default: ""
|
38
|
+
attribute :born_at, Time, default: -> { Time.now }
|
39
|
+
attribute :age, Numeric
|
40
|
+
end
|
41
|
+
|
42
|
+
person = Person.new
|
43
|
+
person.name = "John"
|
44
|
+
|
45
|
+
person.name # => "John"
|
46
|
+
person.born_at # => 2017-11-24 00:55:50 -0800
|
47
|
+
person.age # => nil
|
48
|
+
|
49
|
+
person.name = 123 # raises TypeError
|
50
|
+
```
|
51
|
+
|
52
|
+
### DataEntity
|
53
|
+
|
54
|
+
Has all the same functionality as `Fried::Schema::Struct`, in addition to
|
55
|
+
`#to_h` and `.build`
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class Person
|
59
|
+
include Fried::Schema::DataEntity
|
60
|
+
|
61
|
+
attribute :name, String, default: ""
|
62
|
+
attribute :born_at, Time, default: -> { Time.now }
|
63
|
+
attribute :age, Numeric
|
64
|
+
end
|
65
|
+
|
66
|
+
person = Person.build(name: "John", age: 123, not_present_key: "test")
|
67
|
+
|
68
|
+
person.name # => "John"
|
69
|
+
person.age # => 123
|
70
|
+
|
71
|
+
person.to_h # => { name: "John", born_at: 2017-11-24 00:55:50 -0800, age: 123 }
|
72
|
+
```
|
73
|
+
|
74
|
+
## Development
|
75
|
+
|
76
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
77
|
+
|
78
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
79
|
+
|
80
|
+
## Contributing
|
81
|
+
|
82
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Fire-Dragon-DoL/fried-schema.
|
83
|
+
|
84
|
+
[test-badge]: https://travis-ci.org/Fire-Dragon-DoL/fried-schema.svg?branch=master
|
85
|
+
[test-link]: https://travis-ci.org/Fire-Dragon-DoL/fried-schema
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "fried/schema"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
class Foo
|
14
|
+
include Fried::Schema::DataEntity
|
15
|
+
|
16
|
+
attribute :name, String, default: ""
|
17
|
+
attribute :age, Numeric, default: 0
|
18
|
+
attribute :born_at, Time, default: -> { Time.now }
|
19
|
+
end
|
20
|
+
|
21
|
+
require "pry-byebug"
|
22
|
+
Pry.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "fried/schema/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fried-schema"
|
7
|
+
spec.version = Fried::Schema::VERSION
|
8
|
+
spec.authors = ["Fire-Dragon-DoL"]
|
9
|
+
spec.email = ["francesco.belladonna@gmail.com"]
|
10
|
+
spec.licenses = ["MIT"]
|
11
|
+
|
12
|
+
spec.summary = %q{Struct definition with type safety}
|
13
|
+
spec.description = %q{Struct definition with type safety}
|
14
|
+
spec.homepage = "https://github.com/Fire-Dragon-DoL/fried-schema"
|
15
|
+
spec.metadata = {
|
16
|
+
"source_code_uri" => "https://github.com/Fire-Dragon-DoL/fried-schema"
|
17
|
+
}
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
20
|
+
f.match(%r{^(test|spec|features)/})
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler"
|
27
|
+
spec.add_development_dependency "minitest"
|
28
|
+
spec.add_development_dependency "minitest-focus"
|
29
|
+
spec.add_development_dependency "minitest-reporters"
|
30
|
+
spec.add_development_dependency "pry-byebug"
|
31
|
+
spec.add_development_dependency "rake"
|
32
|
+
spec.add_development_dependency "fried-test"
|
33
|
+
|
34
|
+
spec.add_runtime_dependency "fried-core"
|
35
|
+
spec.add_runtime_dependency "fried-typings"
|
36
|
+
end
|
data/lib/fried/schema.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "fried/schema/attribute"
|
2
|
+
require "fried/schema/attribute/define_reader"
|
3
|
+
require "fried/schema/attribute/define_writer"
|
4
|
+
|
5
|
+
module Fried::Schema::Attribute
|
6
|
+
# Define methods based on attributes registered in
|
7
|
+
# {::Fried::Schema::Definition}
|
8
|
+
class DefineMethods
|
9
|
+
attr_accessor :define_reader
|
10
|
+
attr_accessor :define_writer
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
self.define_reader = ::Fried::Schema::Attribute::DefineReader.new
|
14
|
+
self.define_writer = ::Fried::Schema::Attribute::DefineWriter.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.build
|
18
|
+
new.tap do |instance|
|
19
|
+
instance.define_reader = ::Fried::Schema::Attribute::DefineReader.build
|
20
|
+
instance.define_writer = ::Fried::Schema::Attribute::DefineWriter.build
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.call(attribute_definition, klass)
|
25
|
+
instance = build
|
26
|
+
instance.(attribute_definition, klass)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates methods to read/write attribute with type checking
|
30
|
+
# @param attribute_definition [AttributeDefinition]
|
31
|
+
# @param klass [Class, Module]
|
32
|
+
# @return [AttributeDefinition]
|
33
|
+
def call(attribute_definition, klass)
|
34
|
+
define_reader.(attribute_definition, klass)
|
35
|
+
define_writer.(attribute_definition, klass)
|
36
|
+
attribute_definition
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "fried/schema/attribute"
|
2
|
+
|
3
|
+
module Fried::Schema::Attribute
|
4
|
+
# Define reader based on attribute {Definition}
|
5
|
+
class DefineReader
|
6
|
+
def self.build
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.call(attribute_definition, klass)
|
11
|
+
instance = build
|
12
|
+
instance.(attribute_definition, klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Creates read method
|
16
|
+
# @param definition [Definition]
|
17
|
+
# @param klass [Class, Module]
|
18
|
+
# @return [Definition]
|
19
|
+
def call(definition, klass)
|
20
|
+
variable = definition.instance_variable
|
21
|
+
|
22
|
+
klass.instance_eval do
|
23
|
+
define_method(definition.reader) { instance_variable_get(variable) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "fried/schema/attribute"
|
2
|
+
require "fried/typings/is"
|
3
|
+
|
4
|
+
module Fried::Schema::Attribute
|
5
|
+
# Define writer based on attribute {Definition} with type-checking
|
6
|
+
class DefineWriter
|
7
|
+
attr_accessor :is
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.is = ::Fried::Typings::Is
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build
|
14
|
+
new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.call(attribute_definition, klass)
|
18
|
+
instance = build
|
19
|
+
instance.(attribute_definition, klass)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates write method with type-checking
|
23
|
+
# @param definition [Definition]
|
24
|
+
# @param klass [Class, Module]
|
25
|
+
# @return [Definition]
|
26
|
+
def call(definition, klass)
|
27
|
+
is_a = is[definition.type]
|
28
|
+
define_writer(definition, is_a, klass)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def define_writer(definition, is_a, klass)
|
34
|
+
klass.instance_eval do
|
35
|
+
define_method(definition.writer) do |value|
|
36
|
+
value = is_a.(value)
|
37
|
+
instance_variable_set(definition.instance_variable, value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "fried/schema/attribute"
|
2
|
+
|
3
|
+
module Fried::Schema::Attribute
|
4
|
+
# Value-type holding definition of attribute
|
5
|
+
class Definition < Struct.new(:name, :type, :default)
|
6
|
+
# @!attribute [rw] name
|
7
|
+
# A {Symbol}
|
8
|
+
# @!attribute [rw] type
|
9
|
+
# Any {Class} or {Module}, or {::Fried::Typings::Type}
|
10
|
+
# @!attribute [rw] default
|
11
|
+
# Any object. If a {Proc} is used, it will be evaluated during
|
12
|
+
# initialization
|
13
|
+
|
14
|
+
# Attribute reader method name
|
15
|
+
# @return [Symbol]
|
16
|
+
def reader
|
17
|
+
name.to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
# Attribute writer method name
|
21
|
+
# @return [Symbol]
|
22
|
+
def writer
|
23
|
+
:"#{reader}="
|
24
|
+
end
|
25
|
+
|
26
|
+
# Attribute instance variable name (@variable)
|
27
|
+
# @return [Symbol]
|
28
|
+
def instance_variable
|
29
|
+
:"@#{reader}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Extracts content of {#default} if it's a {Proc}, otherwise just returns
|
33
|
+
# {#default}
|
34
|
+
def extract_default
|
35
|
+
return default.() if default.is_a?(::Proc)
|
36
|
+
default
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
require "fried/schema/get_attribute"
|
3
|
+
|
4
|
+
module Fried::Schema
|
5
|
+
# Converts all attributes into a {Hash} of name => value
|
6
|
+
class AttributesToHash
|
7
|
+
attr_accessor :get_attribute
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.get_attribute = GetAttribute.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build
|
14
|
+
new.tap do |instance|
|
15
|
+
instance.get_attribute = GetAttribute.build
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.call(schema, obj)
|
20
|
+
instance = build
|
21
|
+
instance.(schema, obj)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param schema [Definition]
|
25
|
+
# @param obj [::Fried::Schema::Struct] an instance of a class including
|
26
|
+
# {::Fried::Schema::Struct}
|
27
|
+
# @return [Hash{Symbol => Object}] hash of attributes key => values
|
28
|
+
def call(schema, obj)
|
29
|
+
schema.each_attribute.inject({}) do |hash, attribute|
|
30
|
+
value = get_attribute.(obj, attribute)
|
31
|
+
hash[attribute.name] = value
|
32
|
+
hash
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
require "fried/schema/struct"
|
3
|
+
require "fried/schema/attributes_to_hash"
|
4
|
+
require "fried/schema/hash_to_attributes"
|
5
|
+
|
6
|
+
module Fried::Schema
|
7
|
+
# Includes {Struct} but also provide a {.build} method which initialize
|
8
|
+
# the object with a {Hash}, setting attributes based on keys
|
9
|
+
module DataEntity
|
10
|
+
module ClassMethods
|
11
|
+
def build(attributes = {})
|
12
|
+
new.tap do |instance|
|
13
|
+
schema = self.instance_variable_get(:@__fried_schema__)
|
14
|
+
::Fried::Schema::HashToAttributes.(schema, attributes, instance)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash{Symbol => Object}] a hash containing the values of all
|
20
|
+
# attribute name => value
|
21
|
+
def to_h
|
22
|
+
schema = self.class.instance_variable_get(:@__fried_schema__)
|
23
|
+
::Fried::Schema::AttributesToHash.(schema, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(klass)
|
27
|
+
klass.instance_eval do
|
28
|
+
include ::Fried::Schema::Struct
|
29
|
+
extend ClassMethods
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
|
3
|
+
module Fried::Schema
|
4
|
+
# Holds defined attributes for a given {Class}
|
5
|
+
class Definition
|
6
|
+
private
|
7
|
+
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@attributes = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param attribute_definition [Attribute::Definition]
|
17
|
+
# @return [Attribute::Definition]
|
18
|
+
def add_attribute(definition)
|
19
|
+
name = definition.name
|
20
|
+
attributes[name] = definition
|
21
|
+
end
|
22
|
+
|
23
|
+
# List all attributes. If no block is passed, returns an enumerator,
|
24
|
+
# otherwise it returns the last value returned by the block
|
25
|
+
# @return [Enumerator, Object]
|
26
|
+
def each_attribute(&block)
|
27
|
+
return attributes_enumerator if block.nil?
|
28
|
+
|
29
|
+
attributes_enumerator.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def attributes_enumerator
|
35
|
+
Enumerator.new(attributes.size) do |yielder|
|
36
|
+
attributes.each { |_, definition| yielder << definition }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
|
3
|
+
module Fried::Schema
|
4
|
+
# Get attribute value from object
|
5
|
+
class GetAttribute
|
6
|
+
def self.build
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.call(obj, attribute)
|
11
|
+
instance = build
|
12
|
+
instance.(obj, attribute)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param obj [Object] anything
|
16
|
+
# @param attribute [Attribute::Definition]
|
17
|
+
# @return [Object] value from the object attribute
|
18
|
+
def call(obj, attribute)
|
19
|
+
reader = attribute.reader
|
20
|
+
obj.public_send(reader)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
require "fried/schema/set_attribute"
|
3
|
+
|
4
|
+
module Fried::Schema
|
5
|
+
# Sets all attributes based on hash
|
6
|
+
class HashToAttributes
|
7
|
+
attr_accessor :set_attribute
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.set_attribute = SetAttribute.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build
|
14
|
+
new.tap do |instance|
|
15
|
+
instance.set_attribute = SetAttribute.build
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.call(schema, attributes, obj)
|
20
|
+
instance = build
|
21
|
+
instance.(schema, attributes, obj)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param schema [Definition]
|
25
|
+
# @param attributes [Hash{Symbol => Object}]
|
26
|
+
# @param obj [::Fried::Schema::Struct] an instance of a class including
|
27
|
+
# {::Fried::Schema::Struct}
|
28
|
+
# @return [void]
|
29
|
+
def call(schema, attributes, obj)
|
30
|
+
schema.each_attribute do |attribute|
|
31
|
+
next unless attributes.has_key?(attribute.name)
|
32
|
+
value = attributes[attribute.name]
|
33
|
+
set_attribute.(obj, attribute, value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
|
3
|
+
module Fried::Schema
|
4
|
+
# Set attribute value on object
|
5
|
+
class SetAttribute
|
6
|
+
def self.build
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.call(obj, attribute, value)
|
11
|
+
instance = build
|
12
|
+
instance.(obj, attribute, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param obj [Object] anything
|
16
|
+
# @param attribute [Attribute::Definition]
|
17
|
+
# @param value [Object] anything
|
18
|
+
# @return [Object] the passed value
|
19
|
+
def call(obj, attribute, value)
|
20
|
+
writer = attribute.writer
|
21
|
+
obj.public_send(writer, value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
|
3
|
+
module Fried::Schema
|
4
|
+
# Set attribute value on object skipping type-checking
|
5
|
+
class SetAttributeWithoutChecks
|
6
|
+
def self.build
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.call(obj, attribute, value)
|
11
|
+
instance = build
|
12
|
+
instance.(obj, attribute, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param obj [Object] anything
|
16
|
+
# @param attribute [Attribute::Definition]
|
17
|
+
# @param value [Object] anything
|
18
|
+
# @return [Object] the passed value
|
19
|
+
def call(obj, attribute, value)
|
20
|
+
variable = attribute.instance_variable
|
21
|
+
obj.instance_variable_set(variable, value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
require "fried/schema/set_attribute_without_checks"
|
3
|
+
|
4
|
+
module Fried::Schema
|
5
|
+
# Set defaults values for all attributes in schema definitions while
|
6
|
+
# ignoring type checking
|
7
|
+
class SetDefaults
|
8
|
+
attr_accessor :set_attribute_without_checks
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
self.set_attribute_without_checks = SetAttributeWithoutChecks.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.build
|
15
|
+
new.tap do |instance|
|
16
|
+
instance.set_attribute_without_checks = SetAttributeWithoutChecks.build
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.call(schema, obj)
|
21
|
+
instance = build
|
22
|
+
instance.(schema, obj)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param schema [Definition]
|
26
|
+
# @param obj [Object]
|
27
|
+
# @return [void]
|
28
|
+
def call(schema, obj)
|
29
|
+
schema.each_attribute do |attribute|
|
30
|
+
value = attribute.extract_default
|
31
|
+
set_attribute_without_checks.(obj, attribute, value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "fried/core"
|
2
|
+
require "fried/schema/attribute/definition"
|
3
|
+
require "fried/schema/attribute/define_methods"
|
4
|
+
require "fried/schema/definition"
|
5
|
+
require "fried/schema/set_defaults"
|
6
|
+
|
7
|
+
module Fried::Schema
|
8
|
+
# Provides {.attribute} macro which allows to easily define type-safe
|
9
|
+
# accessors. The attributes are initialized with default values or with
|
10
|
+
# {nil} if none was provided
|
11
|
+
module Struct
|
12
|
+
module Initializer
|
13
|
+
def initialize
|
14
|
+
schema = self.class.instance_variable_get(:@__fried_schema__)
|
15
|
+
::Fried::Schema::SetDefaults.(schema, self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
Noop = -> { nil }
|
21
|
+
|
22
|
+
# Defines an attr_accessor with given name, type checking and optional
|
23
|
+
# default value
|
24
|
+
# @param name [Symbol, String] name for attribute (attr_accessor)
|
25
|
+
# @param type [::Fried::Typings::Type, Class, Module] either a `Type`
|
26
|
+
# from `Fried::Typings` (even a custom one) or any `Class` or `Module`
|
27
|
+
# @param default [Object, Proc] if an object is passed, it will be used
|
28
|
+
# as default. If a {Proc} is passed, it will be evaluated during
|
29
|
+
# object initialization
|
30
|
+
# @return [Symbol] reader method name
|
31
|
+
def attribute(name, type, default: Noop)
|
32
|
+
@__fried_schema__ ||= ::Fried::Schema::Definition.new
|
33
|
+
definer = ::Fried::Schema::Attribute::Definition
|
34
|
+
attribute_definition = definer.new(name, type, default)
|
35
|
+
@__fried_schema__.add_attribute(attribute_definition)
|
36
|
+
::Fried::Schema::Attribute::DefineMethods.(attribute_definition, self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.included(klass)
|
41
|
+
klass.instance_eval do
|
42
|
+
include Initializer
|
43
|
+
extend ClassMethods
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fried-schema
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fire-Dragon-DoL
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-focus
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fried-test
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: fried-core
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: fried-typings
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Struct definition with type safety
|
140
|
+
email:
|
141
|
+
- francesco.belladonna@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- ".rspec"
|
148
|
+
- ".travis.yml"
|
149
|
+
- Gemfile
|
150
|
+
- LICENSE
|
151
|
+
- README.md
|
152
|
+
- Rakefile
|
153
|
+
- bin/console
|
154
|
+
- bin/setup
|
155
|
+
- fried-schema.gemspec
|
156
|
+
- lib/fried/schema.rb
|
157
|
+
- lib/fried/schema/attribute.rb
|
158
|
+
- lib/fried/schema/attribute/define_methods.rb
|
159
|
+
- lib/fried/schema/attribute/define_reader.rb
|
160
|
+
- lib/fried/schema/attribute/define_writer.rb
|
161
|
+
- lib/fried/schema/attribute/definition.rb
|
162
|
+
- lib/fried/schema/attributes_to_hash.rb
|
163
|
+
- lib/fried/schema/data_entity.rb
|
164
|
+
- lib/fried/schema/definition.rb
|
165
|
+
- lib/fried/schema/get_attribute.rb
|
166
|
+
- lib/fried/schema/hash_to_attributes.rb
|
167
|
+
- lib/fried/schema/set_attribute.rb
|
168
|
+
- lib/fried/schema/set_attribute_without_checks.rb
|
169
|
+
- lib/fried/schema/set_defaults.rb
|
170
|
+
- lib/fried/schema/struct.rb
|
171
|
+
- lib/fried/schema/version.rb
|
172
|
+
homepage: https://github.com/Fire-Dragon-DoL/fried-schema
|
173
|
+
licenses:
|
174
|
+
- MIT
|
175
|
+
metadata:
|
176
|
+
source_code_uri: https://github.com/Fire-Dragon-DoL/fried-schema
|
177
|
+
post_install_message:
|
178
|
+
rdoc_options: []
|
179
|
+
require_paths:
|
180
|
+
- lib
|
181
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 2.6.14
|
194
|
+
signing_key:
|
195
|
+
specification_version: 4
|
196
|
+
summary: Struct definition with type safety
|
197
|
+
test_files: []
|