sire 0.0.1 → 0.1.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 +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
|
-
[](https://travis-ci.org/mushishi78/sire)
|
4
|
-
[](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
|
+
[](https://travis-ci.org/mushishi78/sire)
|
4
|
+
[](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
|