sire 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +59 -70
- data/lib/sire.rb +12 -2
- data/lib/sire/aggregate.rb +16 -0
- data/lib/sire/entity.rb +10 -14
- data/lib/sire/entity_builder.rb +54 -0
- data/lib/sire/immutable_delegator.rb +25 -0
- data/lib/sire/relation.rb +37 -0
- data/lib/sire/struct.rb +7 -14
- metadata +6 -4
- data/lib/sire/delegator.rb +0 -16
- data/lib/sire/value_parser.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 644bb4b56a4f703358b995a7436bc5b8bb9e85d1
|
4
|
+
data.tar.gz: ac1711f86c4d933e1a041f81ce51664be8725631
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7dd2374ca218b06db34b535e59cb6da00a748fcdedb248f9aef002a93db39fd374358b1bb1ffba0381c69f8ddd0f05eb371b85a47d6d88078535a58de01bf353
|
7
|
+
data.tar.gz: ecbb9e95ba320112a4b479dc7905293a4fd99ce3dd2680e35de38fd50e3847701acb70be8110f74603dd68576139e0a91fd632ff564c5e1518c3c5278b545447
|
data/README.md
CHANGED
@@ -1,70 +1,59 @@
|
|
1
|
-
# Sire
|
2
|
-
|
3
|
-
[![Build Status](https://travis-ci.org/mushishi78/sire.svg?branch=master)](https://travis-ci.org/mushishi78/sire)
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/sire.svg)](http://badge.fury.io/rb/sire)
|
5
|
-
|
6
|
-
Simple Immutable Relational Entity
|
7
|
-
|
8
|
-
##
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
```
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
```
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
```
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
gem 'sire'
|
62
|
-
```
|
63
|
-
|
64
|
-
## Contributing
|
65
|
-
|
66
|
-
1. Fork it ( https://github.com/[my-github-username]/sire/fork )
|
67
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
-
5. Create a new Pull Request
|
1
|
+
# Sire
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/mushishi78/sire.svg?branch=master)](https://travis-ci.org/mushishi78/sire)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/sire.svg)](http://badge.fury.io/rb/sire)
|
5
|
+
|
6
|
+
Simple Immutable Relational Entity
|
7
|
+
|
8
|
+
## Defining
|
9
|
+
|
10
|
+
``` ruby
|
11
|
+
require 'sire'
|
12
|
+
|
13
|
+
Order = Sire.entity(:line_items, :total)
|
14
|
+
LineItem = Sire.entity(:purchasable, :quantity)
|
15
|
+
LineItems = Sire.aggregate(LineItem)
|
16
|
+
Product = Sire.entity(:name, :price)
|
17
|
+
Service = Sire.entity(:name, :rate)
|
18
|
+
Purchasable = Sire.relation(Product, Service)
|
19
|
+
Rate = Sire.entity(:price, :period)
|
20
|
+
```
|
21
|
+
|
22
|
+
## Initializing
|
23
|
+
|
24
|
+
``` ruby
|
25
|
+
rate = Rate[price: 13.99, period: :weekly]
|
26
|
+
service1 = Service[name: 'Consulting', rate: rate]
|
27
|
+
service2 = Service[name: 'Support', rate: { price: 56.50, period: :monthly }]
|
28
|
+
# Service[name: "Support", rate: Rate[price: 56.5, period: :monthly]]
|
29
|
+
line_item = LineItem[purchasable: { name: 'Hat', price: 5.36 }, quantity: 2]
|
30
|
+
# LineItem[purchasable: Product[name: "Hat", price: 5.36], quantity: 2]
|
31
|
+
order = Order[line_items: [line_item, {purchasable: service1, quantity: 1}], total: 12.50]
|
32
|
+
# Order[line_items: [LineItem[purchasable: Product[name: "Hat", price: 5.36], quantity: 2], LineItem[purchasable: Service[name: "Consulting", rate: Rate[price: 13.99, period: :weekly]], quantity: 1]], total: 12.5]
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
``` ruby
|
38
|
+
rate.price # 13.99
|
39
|
+
rate.attributes # [:price, :period]
|
40
|
+
rate2 = rate.merge(price: 10.00) # Rate[price: 10.00, period: :weekly]
|
41
|
+
rate == rate2 # false
|
42
|
+
rate.map { |k, v| "#{k} = #{v}" }.join(', ') # "price = 13.99, period = weekly"
|
43
|
+
```
|
44
|
+
|
45
|
+
## Installation
|
46
|
+
|
47
|
+
Add this line to your application's Gemfile:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
gem 'sire'
|
51
|
+
```
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it ( https://github.com/[my-github-username]/sire/fork )
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create a new Pull Request
|
data/lib/sire.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
1
|
require 'sire/entity'
|
2
|
+
require 'sire/relation'
|
3
|
+
require 'sire/aggregate'
|
2
4
|
|
3
5
|
module Sire
|
4
6
|
class << self
|
5
|
-
def
|
6
|
-
Entity.define(*attributes
|
7
|
+
def entity(*attributes)
|
8
|
+
Entity.define(*attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def relation(*entity_types)
|
12
|
+
Relation.define(*entity_types)
|
13
|
+
end
|
14
|
+
|
15
|
+
def aggregate(*entity_types)
|
16
|
+
Aggregate.define(*entity_types)
|
7
17
|
end
|
8
18
|
end
|
9
19
|
end
|
data/lib/sire/entity.rb
CHANGED
@@ -1,25 +1,21 @@
|
|
1
1
|
require 'sire/struct'
|
2
|
-
require '
|
2
|
+
require 'sire/entity_builder'
|
3
|
+
require 'sire/aggregate'
|
3
4
|
|
4
5
|
module Sire
|
5
6
|
class Entity < Struct
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
hash, other_hash = to_h, other.to_h
|
10
|
-
|
11
|
-
other_hash.each do |k, other_value|
|
12
|
-
value = hash[k]
|
13
|
-
hash[k] = value.is_a?(Array) ? value + other_value : other_value
|
7
|
+
class << self
|
8
|
+
def inherited(child)
|
9
|
+
EntityBuilder.nestables << child
|
14
10
|
end
|
15
11
|
|
16
|
-
|
12
|
+
def new(arg = {})
|
13
|
+
arg.is_a?(self) ? arg : super
|
14
|
+
end
|
17
15
|
end
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
def nestable_classes
|
22
|
-
Entity.descendants
|
17
|
+
def initialize(hash)
|
18
|
+
super(EntityBuilder.build(hash))
|
23
19
|
end
|
24
20
|
end
|
25
21
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'inflecto'
|
2
|
+
|
3
|
+
module Sire
|
4
|
+
module EntityBuilder
|
5
|
+
class << self
|
6
|
+
def build(hash)
|
7
|
+
create_lookup unless lookup_up_to_date?
|
8
|
+
parse_values(hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
def nestables
|
12
|
+
@nestables ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def primitives
|
16
|
+
@primitves ||= [String, Numeric, Rational, Complex, Integer, Fixnum, Float, Bignum,
|
17
|
+
TrueClass, FalseClass, Symbol, Time, Regexp, NilClass]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_lookup
|
23
|
+
@nestable_lookup = Hash[nestables.map { |n| [to_sym(n), n] }]
|
24
|
+
end
|
25
|
+
|
26
|
+
def lookup_up_to_date?
|
27
|
+
@nestable_lookup.respond_to?(:values) && @nestable_lookup.values == nestables
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_values(hash)
|
31
|
+
Hash[hash.map { |k, v| nested(k, v) || primitive(k, v) || invalid(k) }]
|
32
|
+
end
|
33
|
+
|
34
|
+
def nested(k, v)
|
35
|
+
return unless nested_class = @nestable_lookup[k]
|
36
|
+
[k, nested_class.new(v)]
|
37
|
+
end
|
38
|
+
|
39
|
+
def primitive(k, v)
|
40
|
+
[k, v] if primitives.include?(v.class)
|
41
|
+
end
|
42
|
+
|
43
|
+
def invalid(k)
|
44
|
+
fail InvalidValueError, "#{k} is invalid. Accepted types: #{primitives | nestables}"
|
45
|
+
end
|
46
|
+
|
47
|
+
InvalidValueError = Class.new(StandardError)
|
48
|
+
|
49
|
+
def to_sym(klass)
|
50
|
+
Inflecto.underscore(Inflecto.demodulize(klass.to_s)).to_sym
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sire
|
2
|
+
class ImmutableDelegator < BasicObject
|
3
|
+
def initialize(obj)
|
4
|
+
@obj = obj
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(*args, &b)
|
8
|
+
return super unless obj.respond_to?(args.first)
|
9
|
+
res = obj.send(*args, &b)
|
10
|
+
res.class == obj.class ? create_new(res) : res
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :obj
|
16
|
+
|
17
|
+
def create_new(res)
|
18
|
+
self.class.new(res)
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(meth, privates = false)
|
22
|
+
obj.respond_to?(meth, privates) || super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'sire/entity_builder'
|
2
|
+
|
3
|
+
module Sire
|
4
|
+
class Relation < Module
|
5
|
+
class << self
|
6
|
+
alias_method :define, :new
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(*entity_types)
|
10
|
+
@entity_types = entity_types
|
11
|
+
extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
attr_reader :entity_types
|
16
|
+
|
17
|
+
def self.extended(child)
|
18
|
+
EntityBuilder.nestables << child
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: Should iterate over entity_types and find the best key match
|
22
|
+
def new(value)
|
23
|
+
acceptable?(value) ? value : default.new(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def acceptable?(value)
|
29
|
+
entity_types.include?(value.class)
|
30
|
+
end
|
31
|
+
|
32
|
+
def default
|
33
|
+
entity_types[0]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/sire/struct.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'sire/value_parser'
|
2
|
-
|
3
1
|
module Sire
|
4
2
|
class Struct
|
5
3
|
include Enumerable
|
@@ -7,12 +5,11 @@ module Sire
|
|
7
5
|
class << self
|
8
6
|
attr_reader :attributes
|
9
7
|
|
10
|
-
def define(*attributes
|
8
|
+
def define(*attributes)
|
11
9
|
Class.new(self) do
|
12
10
|
attr_accessor(*attributes)
|
13
11
|
private(*attributes.map { |a| "#{a}=" })
|
14
12
|
@attributes = [*attributes].freeze
|
15
|
-
class_eval(&b) if b
|
16
13
|
end
|
17
14
|
end
|
18
15
|
|
@@ -22,13 +19,15 @@ module Sire
|
|
22
19
|
end
|
23
20
|
|
24
21
|
def initialize(hash = {})
|
25
|
-
hash.each { |k, v| send("#{k}=",
|
22
|
+
hash.each { |k, v| send("#{k}=", v) }
|
26
23
|
freeze
|
27
24
|
end
|
28
25
|
|
29
26
|
def merge(other)
|
30
|
-
|
27
|
+
other_hash = other.respond_to?(:to_h) ? other.to_h : other
|
28
|
+
self.class.new(to_h.merge(other_hash))
|
31
29
|
end
|
30
|
+
alias_method :update, :merge
|
32
31
|
|
33
32
|
def attributes
|
34
33
|
self.class.attributes
|
@@ -50,14 +49,8 @@ module Sire
|
|
50
49
|
to_s
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
def parser
|
56
|
-
@parser ||= ValueParser.new(nestable_classes)
|
57
|
-
end
|
58
|
-
|
59
|
-
def nestable_classes
|
60
|
-
[]
|
52
|
+
def to_h
|
53
|
+
Hash[to_a]
|
61
54
|
end
|
62
55
|
end
|
63
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sire
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inflecto
|
@@ -79,10 +79,12 @@ files:
|
|
79
79
|
- LICENSE.txt
|
80
80
|
- README.md
|
81
81
|
- lib/sire.rb
|
82
|
-
- lib/sire/
|
82
|
+
- lib/sire/aggregate.rb
|
83
83
|
- lib/sire/entity.rb
|
84
|
+
- lib/sire/entity_builder.rb
|
85
|
+
- lib/sire/immutable_delegator.rb
|
86
|
+
- lib/sire/relation.rb
|
84
87
|
- lib/sire/struct.rb
|
85
|
-
- lib/sire/value_parser.rb
|
86
88
|
homepage:
|
87
89
|
licenses:
|
88
90
|
- MIT
|
data/lib/sire/delegator.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'delegate'
|
2
|
-
|
3
|
-
module Sire
|
4
|
-
class Delegator < SimpleDelegator
|
5
|
-
def method_missing(*args, &b)
|
6
|
-
result = super
|
7
|
-
result.class == __getobj__.class ? create_new(result) : result
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def create_new(result)
|
13
|
-
self.class.new(result)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/lib/sire/value_parser.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'inflecto'
|
2
|
-
|
3
|
-
module Sire
|
4
|
-
class ValueParser
|
5
|
-
def initialize(nestable_classes = [])
|
6
|
-
@nestable_classes = nestable_classes.reduce({}) do |h, c|
|
7
|
-
h.merge(demodulize_and_underscore(c) => c)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def parse_value(key, value)
|
12
|
-
@key, @value = key, value
|
13
|
-
return value.freeze if primitive?
|
14
|
-
return create_nested if single_nested_class
|
15
|
-
return create_many_nested if plural_nested_class
|
16
|
-
invalid
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def primitive?
|
22
|
-
primitives.include?(@value.class)
|
23
|
-
end
|
24
|
-
|
25
|
-
def primitives
|
26
|
-
@@primitives ||= [String, Numeric, Rational, Complex, Integer, Fixnum, Float, Bignum,
|
27
|
-
TrueClass, FalseClass, Symbol, Date, Time, Regexp, NilClass]
|
28
|
-
end
|
29
|
-
|
30
|
-
def create_nested(nested_class = single_nested_class, arg = @value)
|
31
|
-
arg.is_a?(nested_class) ? arg : nested_class.new(arg).freeze
|
32
|
-
end
|
33
|
-
|
34
|
-
def create_many_nested
|
35
|
-
[*@value.map { |arg| create_nested(plural_nested_class, arg) }].freeze
|
36
|
-
end
|
37
|
-
|
38
|
-
def single_nested_class
|
39
|
-
@single_nested_class ||= @nestable_classes[@key.to_s]
|
40
|
-
end
|
41
|
-
|
42
|
-
def plural_nested_class
|
43
|
-
@plural_nested_class ||= @nestable_classes[Inflecto.singularize(@key.to_s)]
|
44
|
-
end
|
45
|
-
|
46
|
-
def invalid
|
47
|
-
message = "#{@key} is invalid. Accepted types: #{primitives | @nestable_classes.values}"
|
48
|
-
fail InvalidValueError, message
|
49
|
-
end
|
50
|
-
|
51
|
-
def demodulize_and_underscore(word)
|
52
|
-
Inflecto.underscore(Inflecto.demodulize(word))
|
53
|
-
end
|
54
|
-
|
55
|
-
InvalidValueError = Class.new(StandardError)
|
56
|
-
end
|
57
|
-
end
|