halitosis 0.1.0 → 0.3.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 +33 -19
- data/lib/halitosis/attributes/field.rb +8 -0
- data/lib/halitosis/attributes.rb +52 -0
- data/lib/halitosis/base.rb +27 -31
- data/lib/halitosis/collection/field.rb +11 -0
- data/lib/halitosis/collection.rb +40 -27
- data/lib/halitosis/context.rb +54 -0
- data/lib/halitosis/errors.rb +13 -0
- data/lib/halitosis/field.rb +8 -23
- data/lib/halitosis/fields.rb +6 -0
- data/lib/halitosis/hash_util.rb +31 -12
- data/lib/halitosis/{properties → identifiers}/field.rb +1 -1
- data/lib/halitosis/identifiers.rb +46 -0
- data/lib/halitosis/links/field.rb +1 -1
- data/lib/halitosis/links.rb +6 -6
- data/lib/halitosis/meta.rb +7 -7
- data/lib/halitosis/permissions.rb +7 -7
- data/lib/halitosis/railtie.rb +10 -1
- data/lib/halitosis/relationships/field.rb +2 -2
- data/lib/halitosis/relationships.rb +25 -17
- data/lib/halitosis/resource.rb +37 -11
- data/lib/halitosis/root_links/field.rb +8 -0
- data/lib/halitosis/root_links.rb +46 -0
- data/lib/halitosis/root_meta/field.rb +8 -0
- data/lib/halitosis/root_meta.rb +44 -0
- data/lib/halitosis/root_permissions/field.rb +8 -0
- data/lib/halitosis/root_permissions.rb +43 -0
- data/lib/halitosis/version.rb +1 -1
- data/lib/halitosis.rb +20 -3
- metadata +13 -4
- data/lib/halitosis/properties.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0576b2d1e7ce15303f1bdc4a845bc7e81d1f2e497d3e49b220f1dd68920938cb
|
4
|
+
data.tar.gz: 79a5d29e8ec21cf3f035719dab8ef719216424a8514c2ff045d8f0f0d1cbe552
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '02779ecf3fd87d66cab25c245c1ca82a2eee370241088d1af6fcc72bde1f3e7917687119260e4d5d04f22dfb363c1d8d9b54730caece6a82825d2ca65f6d9ef5'
|
7
|
+
data.tar.gz: 2e77dae400e7f5ece72aa1604898444b90c43cca04694b495f23dda4aaf38e4b62b05bc1cdd67b1f32d1e5c1014924756e2b242bd4c5a648df944bee18d61520
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ Need something more standardized ([JSON:API](https://jsonapi.org/), or [HAL](htt
|
|
21
21
|
Add this line to your application's Gemfile:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
gem "
|
24
|
+
gem "halitosis"
|
25
25
|
```
|
26
26
|
|
27
27
|
And then execute:
|
@@ -33,7 +33,7 @@ $ bundle install
|
|
33
33
|
Or install it yourself as:
|
34
34
|
|
35
35
|
```bash
|
36
|
-
$ gem install
|
36
|
+
$ gem install halitosis
|
37
37
|
```
|
38
38
|
|
39
39
|
### Basic usage
|
@@ -51,7 +51,7 @@ class DuckSerializer
|
|
51
51
|
|
52
52
|
resource :duck
|
53
53
|
|
54
|
-
|
54
|
+
attribute :name
|
55
55
|
|
56
56
|
link :self do
|
57
57
|
"/ducks/#{duck.code}"
|
@@ -68,11 +68,13 @@ serializer = DuckSerializer.new(duck)
|
|
68
68
|
|
69
69
|
Then call `serializer.render`:
|
70
70
|
|
71
|
-
```
|
71
|
+
```ruby
|
72
72
|
{
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
duck: {
|
74
|
+
name: 'Ferdi',
|
75
|
+
_links: {
|
76
|
+
self: { href: '/ducks/ferdi' }
|
77
|
+
}
|
76
78
|
}
|
77
79
|
}
|
78
80
|
```
|
@@ -80,7 +82,7 @@ Then call `serializer.render`:
|
|
80
82
|
Or `serializer.to_json`:
|
81
83
|
|
82
84
|
```ruby
|
83
|
-
'{"name": "Ferdi", "_links": {"self": {"href": "/ducks/ferdi"}}}'
|
85
|
+
'{"duck": {"name": "Ferdi", "_links": {"self": {"href": "/ducks/ferdi"}}}}'
|
84
86
|
```
|
85
87
|
|
86
88
|
|
@@ -117,10 +119,10 @@ When a resource is declared, `#initialize` expects the resource as the first arg
|
|
117
119
|
serializer = DuckSerializer.new(Duck.new, ...)
|
118
120
|
```
|
119
121
|
|
120
|
-
This makes
|
122
|
+
This makes attribute definitions cleaner:
|
121
123
|
|
122
124
|
```ruby
|
123
|
-
|
125
|
+
attribute :name # now calls Duck#name by default
|
124
126
|
```
|
125
127
|
|
126
128
|
#### 3. Collection
|
@@ -139,39 +141,51 @@ end
|
|
139
141
|
|
140
142
|
The block should return an array of Halitosis instances in order to be rendered.
|
141
143
|
|
142
|
-
### Defining
|
144
|
+
### Defining attributes, links, relationships, meta, and permissions
|
143
145
|
|
144
|
-
|
146
|
+
Attributes can be defined in several ways:
|
145
147
|
|
146
148
|
```ruby
|
147
|
-
|
149
|
+
attribute(:quacks) { "#{duck.quacks} per minute" }
|
150
|
+
```
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
attribute :quacks # => Duck#quacks, if resource is declared
|
148
154
|
```
|
149
155
|
|
150
156
|
```ruby
|
151
|
-
|
157
|
+
attribute :quacks, value: "many"
|
152
158
|
```
|
153
159
|
|
154
160
|
```ruby
|
155
|
-
|
161
|
+
attribute :quacks do
|
156
162
|
duck.quacks.round
|
157
163
|
end
|
158
164
|
```
|
159
165
|
|
160
166
|
```ruby
|
161
|
-
|
167
|
+
attribute(:quacks) { calculate_quacks }
|
162
168
|
|
163
169
|
def calculate_quacks
|
164
170
|
...
|
165
171
|
end
|
166
172
|
```
|
167
173
|
|
174
|
+
Attributes can also be implemented using the legacy `property` alias:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
property(:quacks) { "#{duck.quacks} per minute" }
|
178
|
+
property :quacks # Duck#quacks
|
179
|
+
property :quacks, value: "many"
|
180
|
+
```
|
181
|
+
|
168
182
|
#### Conditionals
|
169
183
|
|
170
|
-
The inclusion of
|
184
|
+
The inclusion of attributes can be determined by conditionals using `if` and
|
171
185
|
`unless` options. For example, with a method name:
|
172
186
|
|
173
187
|
```ruby
|
174
|
-
|
188
|
+
attribute :quacks, if: :include_quacks?
|
175
189
|
|
176
190
|
def include_quacks?
|
177
191
|
duck.quacks < 10
|
@@ -180,7 +194,7 @@ end
|
|
180
194
|
|
181
195
|
With a proc:
|
182
196
|
```ruby
|
183
|
-
|
197
|
+
attribute :quacks, unless: proc { duck.quacks.nil? }, value: ...
|
184
198
|
```
|
185
199
|
|
186
200
|
For links and relationships:
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Halitosis
|
4
|
+
module Attributes
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
|
8
|
+
base.send :include, InstanceMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# Legacy alias for attribute
|
13
|
+
#
|
14
|
+
# @param name [Symbol, String]
|
15
|
+
# @param options [nil, Hash]
|
16
|
+
#
|
17
|
+
# @return [Halitosis::Attributes::Field]
|
18
|
+
def property(...)
|
19
|
+
attribute(...)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Rails-style attribute definition
|
23
|
+
#
|
24
|
+
# @param name [Symbol, String]
|
25
|
+
# @param options [nil, Hash]
|
26
|
+
#
|
27
|
+
# @return [Halitosis::Attributes::Field]
|
28
|
+
#
|
29
|
+
def attribute(name, options = {}, &procedure)
|
30
|
+
fields.add(Field.new(name, options, procedure))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
# @return [Hash] the rendered hash with attributes, if any
|
36
|
+
#
|
37
|
+
def render_with_context(context)
|
38
|
+
super.merge(attributes(context))
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] attributes from fields
|
42
|
+
#
|
43
|
+
def attributes(context = build_context)
|
44
|
+
render_fields(Field, context) do |field, result|
|
45
|
+
result[field.name] = field.value(context)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require "halitosis/attributes/field"
|
data/lib/halitosis/base.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Halitosis
|
4
|
+
# Base module for all serializer classes.
|
5
|
+
#
|
6
|
+
# Include this module in your serializer class, and include any additional field-type modules
|
4
7
|
module Base
|
5
8
|
def self.included(base)
|
6
9
|
base.extend ClassMethods
|
7
10
|
|
8
11
|
base.send :include, InstanceMethods
|
9
|
-
base.send :include, Links
|
10
|
-
base.send :include, Meta
|
11
|
-
base.send :include, Permissions
|
12
|
-
base.send :include, Properties
|
13
|
-
base.send :include, Relationships
|
14
12
|
|
15
13
|
base.send :attr_reader, :options
|
14
|
+
|
15
|
+
base.class.send :attr_accessor, :resource_type
|
16
16
|
end
|
17
17
|
|
18
18
|
module ClassMethods
|
@@ -32,7 +32,7 @@ module Halitosis
|
|
32
32
|
# @return [Object] the serializer instance
|
33
33
|
#
|
34
34
|
def initialize(**options)
|
35
|
-
@options = Halitosis::HashUtil.
|
35
|
+
@options = Halitosis::HashUtil.symbolize_hash(options).freeze
|
36
36
|
end
|
37
37
|
|
38
38
|
# @return [Hash, Array] rendered JSON
|
@@ -48,21 +48,15 @@ module Halitosis
|
|
48
48
|
|
49
49
|
# @return [Hash] rendered representation
|
50
50
|
#
|
51
|
-
def render
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
# @return [nil, Object] the parent serializer, if this instance is an
|
56
|
-
# embedded child
|
57
|
-
#
|
58
|
-
def parent
|
59
|
-
@parent ||= options.fetch(:parent, nil)
|
51
|
+
def render(**options)
|
52
|
+
render_with_context(build_context(options))
|
60
53
|
end
|
61
54
|
|
62
|
-
# @
|
55
|
+
# @param context [Halitosis::Context] the context instance
|
56
|
+
# @return [Hash] the rendered hash
|
63
57
|
#
|
64
|
-
def
|
65
|
-
|
58
|
+
def render_with_context(_context)
|
59
|
+
{}
|
66
60
|
end
|
67
61
|
|
68
62
|
def collection?
|
@@ -71,6 +65,13 @@ module Halitosis
|
|
71
65
|
|
72
66
|
protected
|
73
67
|
|
68
|
+
# Build a new context instance using this serializer instance
|
69
|
+
#
|
70
|
+
# @return [Halitosis::Context] the context instance
|
71
|
+
def build_context(options = {})
|
72
|
+
Context.new(self, HashUtil.deep_merge(@options, options))
|
73
|
+
end
|
74
|
+
|
74
75
|
# Allow included modules to decorate rendered hash
|
75
76
|
#
|
76
77
|
# @param key [Symbol] the key (e.g. `embedded`, `links`)
|
@@ -78,9 +79,9 @@ module Halitosis
|
|
78
79
|
#
|
79
80
|
# @return [Hash] the decorated hash
|
80
81
|
#
|
81
|
-
def decorate_render(key, result)
|
82
|
+
def decorate_render(key, context, result)
|
82
83
|
result.tap do
|
83
|
-
value = send(key)
|
84
|
+
value = send(key, context)
|
84
85
|
|
85
86
|
result[:"_#{key}"] = value if value.any?
|
86
87
|
end
|
@@ -93,11 +94,11 @@ module Halitosis
|
|
93
94
|
#
|
94
95
|
# @return [Hash] the result
|
95
96
|
#
|
96
|
-
def render_fields(type)
|
97
|
-
fields = self.class.fields.
|
97
|
+
def render_fields(type, context)
|
98
|
+
fields = self.class.fields.for_type(type)
|
98
99
|
|
99
100
|
fields.each_with_object({}) do |field, result|
|
100
|
-
next unless field.enabled?(
|
101
|
+
next unless field.enabled?(context)
|
101
102
|
|
102
103
|
yield field, result
|
103
104
|
end
|
@@ -108,15 +109,10 @@ module Halitosis
|
|
108
109
|
#
|
109
110
|
# @return [nil, Hash] the rendered child
|
110
111
|
#
|
111
|
-
def render_child(child, opts)
|
112
|
-
return unless child.class.included_modules.include?(Halitosis)
|
113
|
-
|
114
|
-
child.options[:include] ||= {}
|
115
|
-
child.options[:include] = child.options[:include].merge(opts)
|
116
|
-
|
117
|
-
child.options[:parent] = self
|
112
|
+
def render_child(child, context, opts)
|
113
|
+
return unless child.class.included_modules.include?(Halitosis::Base)
|
118
114
|
|
119
|
-
child.
|
115
|
+
child.render_with_context child.build_context(parent: context, include: opts)
|
120
116
|
end
|
121
117
|
end
|
122
118
|
end
|
@@ -3,6 +3,17 @@
|
|
3
3
|
module Halitosis
|
4
4
|
module Collection
|
5
5
|
class Field < Halitosis::Field
|
6
|
+
# @return [true] if nothing is raised
|
7
|
+
#
|
8
|
+
# @raise [Halitosis::InvalidField] if the definition is invalid
|
9
|
+
#
|
10
|
+
def validate
|
11
|
+
super
|
12
|
+
|
13
|
+
return true if procedure
|
14
|
+
|
15
|
+
raise InvalidField, "Collection #{name} must be defined with a proc"
|
16
|
+
end
|
6
17
|
end
|
7
18
|
end
|
8
19
|
end
|
data/lib/halitosis/collection.rb
CHANGED
@@ -15,8 +15,6 @@ module Halitosis
|
|
15
15
|
base.send :include, InstanceMethods
|
16
16
|
|
17
17
|
base.send :attr_reader, :collection
|
18
|
-
|
19
|
-
base.class.send :attr_accessor, :collection_name
|
20
18
|
end
|
21
19
|
|
22
20
|
module ClassMethods
|
@@ -25,9 +23,9 @@ module Halitosis
|
|
25
23
|
# @return [Module] self
|
26
24
|
#
|
27
25
|
def define_collection(name, options = {}, &procedure)
|
28
|
-
raise InvalidCollection, "#{self.name} collection is already defined" if fields.
|
26
|
+
raise InvalidCollection, "#{self.name || Collection.name} collection is already defined" if fields.for_type(Field).any?
|
29
27
|
|
30
|
-
self.
|
28
|
+
self.resource_type = name.to_s
|
31
29
|
|
32
30
|
alias_method name, :collection
|
33
31
|
|
@@ -39,7 +37,22 @@ module Halitosis
|
|
39
37
|
end
|
40
38
|
|
41
39
|
def collection_field
|
42
|
-
fields
|
40
|
+
fields.for_type(Field).last || raise(InvalidCollection, "#{name || Collection.name} collection is not defined")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Provide an alias for root_link
|
44
|
+
def link(*, **, &)
|
45
|
+
root_link(*, **, &)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Provide an alias for root_meta
|
49
|
+
def meta(*, **, &)
|
50
|
+
root_meta(*, **, &)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Provide an alias for root_permission
|
54
|
+
def permission(*, **, &)
|
55
|
+
root_permission(*, **, &)
|
43
56
|
end
|
44
57
|
end
|
45
58
|
|
@@ -50,47 +63,47 @@ module Halitosis
|
|
50
63
|
#
|
51
64
|
def initialize(collection, **)
|
52
65
|
@collection = collection
|
66
|
+
@collection_field = self.class.collection_field
|
53
67
|
|
54
68
|
super(**)
|
55
69
|
end
|
56
70
|
|
57
|
-
# @return [Hash] the rendered hash with collection,
|
71
|
+
# @return [Hash, Array] the rendered hash with collection, as an array or a hash under a key
|
58
72
|
#
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
73
|
+
def render_with_context(context)
|
74
|
+
if (include_root = context.fetch(:include_root) { context.depth.zero? })
|
75
|
+
{
|
76
|
+
root_name(include_root) => render_collection_field(context)
|
77
|
+
}.merge(super)
|
63
78
|
else
|
64
|
-
render_collection_field(
|
79
|
+
render_collection_field(context)
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
68
|
-
# @return [Hash] collection from fields
|
69
|
-
#
|
70
|
-
def render_collection_field(field)
|
71
|
-
value = instance_eval(&field.procedure)
|
72
|
-
value.map { |child| render_child(child, collection_opts) }
|
73
|
-
end
|
74
|
-
|
75
83
|
def collection?
|
76
84
|
true
|
77
85
|
end
|
78
86
|
|
79
87
|
private
|
80
88
|
|
81
|
-
|
82
|
-
|
83
|
-
# @return [Hash]
|
89
|
+
attr_reader :collection_field
|
90
|
+
|
91
|
+
# @return [Hash] collection from fields
|
84
92
|
#
|
85
|
-
def
|
86
|
-
|
93
|
+
def render_collection_field(context)
|
94
|
+
value = collection_field.value(context)
|
87
95
|
|
88
|
-
|
96
|
+
return render_child(value, context, context.include_options) if value.is_a?(Halitosis::Collection)
|
97
|
+
|
98
|
+
value.reject { |child| child.is_a?(Halitosis::Collection) } # Skip nested collections in array
|
99
|
+
.map { |child| render_child(child, context, context.include_options) }
|
100
|
+
.compact
|
101
|
+
end
|
89
102
|
|
90
|
-
|
91
|
-
|
103
|
+
def root_name(include_root)
|
104
|
+
return include_root.to_sym if include_root.is_a?(String) || include_root.is_a?(Symbol)
|
92
105
|
|
93
|
-
|
106
|
+
self.class.resource_type.to_sym
|
94
107
|
end
|
95
108
|
end
|
96
109
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Halitosis
|
2
|
+
class Context
|
3
|
+
# @param instance [Halitosis::Base] the serializer instance
|
4
|
+
# @param options [Hash] hash of options
|
5
|
+
def initialize(instance, options = {})
|
6
|
+
@instance = instance
|
7
|
+
@options = HashUtil.symbolize_hash(options).freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
### Instance ###
|
11
|
+
|
12
|
+
# Evaluate guard procedure or method on the serializer instance
|
13
|
+
#
|
14
|
+
def call_instance(guard)
|
15
|
+
case guard
|
16
|
+
when Proc
|
17
|
+
instance.instance_exec(self, &guard)
|
18
|
+
when Symbol, String
|
19
|
+
instance.send(guard)
|
20
|
+
else
|
21
|
+
guard
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
### Options ###
|
26
|
+
|
27
|
+
def fetch(...)
|
28
|
+
options.fetch(...)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash] hash of options with top level string keys
|
32
|
+
#
|
33
|
+
def include_options
|
34
|
+
@include_options ||= HashUtil.hasherize_include_option(options[:include] || {})
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [nil, Halitosis::Context] the parent context, if this instance is an
|
38
|
+
# embedded child
|
39
|
+
#
|
40
|
+
def parent
|
41
|
+
options.fetch(:parent, nil)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Integer] the depth at which this serializer is embedded
|
45
|
+
#
|
46
|
+
def depth
|
47
|
+
@depth ||= parent ? parent.depth + 1 : 0
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :instance, :options
|
53
|
+
end
|
54
|
+
end
|
data/lib/halitosis/errors.rb
CHANGED
@@ -3,9 +3,22 @@
|
|
3
3
|
module Halitosis
|
4
4
|
class Error < StandardError; end
|
5
5
|
|
6
|
+
### Configuration Errors ###
|
7
|
+
|
6
8
|
class InvalidCollection < StandardError; end
|
7
9
|
|
8
10
|
class InvalidField < StandardError; end
|
9
11
|
|
10
12
|
class InvalidResource < StandardError; end
|
13
|
+
|
14
|
+
### Rendering Errors ###
|
15
|
+
|
16
|
+
class InvalidQueryParameter < Error
|
17
|
+
def initialize(message, parameter)
|
18
|
+
@parameter = parameter
|
19
|
+
super(message)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :parameter
|
23
|
+
end
|
11
24
|
end
|
data/lib/halitosis/field.rb
CHANGED
@@ -7,8 +7,6 @@ module Halitosis
|
|
7
7
|
class Field
|
8
8
|
attr_reader :name, :options
|
9
9
|
|
10
|
-
attr_accessor :procedure
|
11
|
-
|
12
10
|
# Construct a new Field instance
|
13
11
|
#
|
14
12
|
# @param name [Symbol, String] Field name
|
@@ -18,27 +16,25 @@ module Halitosis
|
|
18
16
|
#
|
19
17
|
def initialize(name, options, procedure)
|
20
18
|
@name = name.to_sym
|
21
|
-
@options = Halitosis::HashUtil.
|
19
|
+
@options = Halitosis::HashUtil.symbolize_hash(options)
|
22
20
|
@procedure = procedure
|
23
21
|
end
|
24
22
|
|
25
|
-
# @param
|
23
|
+
# @param context [Halitosis::Context] the serializer instance with which to evaluate
|
26
24
|
# the stored procedure
|
27
25
|
#
|
28
|
-
def value(
|
29
|
-
options.fetch(:value)
|
30
|
-
procedure ? instance.instance_eval(&procedure) : instance.send(name)
|
31
|
-
end
|
26
|
+
def value(context)
|
27
|
+
options.fetch(:value) { context.call_instance(procedure || name) }
|
32
28
|
end
|
33
29
|
|
34
30
|
# @return [true, false] whether this Field should be included based on
|
35
31
|
# its conditional guard, if any
|
36
32
|
#
|
37
|
-
def enabled?(
|
33
|
+
def enabled?(context)
|
38
34
|
if options.key?(:if)
|
39
|
-
!!
|
35
|
+
!!context.call_instance(options.fetch(:if))
|
40
36
|
elsif options.key?(:unless)
|
41
|
-
!
|
37
|
+
!context.call_instance(options.fetch(:unless))
|
42
38
|
else
|
43
39
|
true
|
44
40
|
end
|
@@ -57,17 +53,6 @@ module Halitosis
|
|
57
53
|
|
58
54
|
private
|
59
55
|
|
60
|
-
|
61
|
-
#
|
62
|
-
def eval_guard(instance, guard)
|
63
|
-
case guard
|
64
|
-
when Proc
|
65
|
-
instance.instance_eval(&guard)
|
66
|
-
when Symbol, String
|
67
|
-
instance.send(guard)
|
68
|
-
else
|
69
|
-
guard
|
70
|
-
end
|
71
|
-
end
|
56
|
+
attr_reader :procedure
|
72
57
|
end
|
73
58
|
end
|
data/lib/halitosis/fields.rb
CHANGED
data/lib/halitosis/hash_util.rb
CHANGED
@@ -4,23 +4,42 @@ module Halitosis
|
|
4
4
|
module HashUtil
|
5
5
|
module_function
|
6
6
|
|
7
|
-
# Transform
|
7
|
+
# Transform include params into a hash
|
8
8
|
#
|
9
|
-
# @param
|
9
|
+
# @param object [Hash, Array, String]
|
10
10
|
#
|
11
11
|
# @return [Hash]
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def hasherize_include_option(object)
|
13
|
+
case object
|
14
|
+
when Hash
|
15
|
+
object.transform_keys(&:to_s)
|
16
|
+
when String, Symbol
|
17
|
+
object.to_s.split(",").inject({}) do |output, key|
|
17
18
|
f, value = key.split(".", 2)
|
18
|
-
output
|
19
|
+
deep_merge(output, f => value ? hasherize_include_option(value) : {})
|
19
20
|
end
|
20
21
|
when Array
|
21
|
-
|
22
|
+
object.inject({}) do |output, value|
|
23
|
+
deep_merge(output, hasherize_include_option(value))
|
24
|
+
end
|
22
25
|
else
|
23
|
-
|
26
|
+
object
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Deep merge two hashes
|
31
|
+
#
|
32
|
+
# @param hash [Hash]
|
33
|
+
# @param other_hash [Hash]
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
def deep_merge(hash, other_hash)
|
37
|
+
hash.merge(other_hash) do |key, this_val, other_val|
|
38
|
+
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
39
|
+
deep_merge(this_val, other_val)
|
40
|
+
else
|
41
|
+
other_val
|
42
|
+
end
|
24
43
|
end
|
25
44
|
end
|
26
45
|
|
@@ -30,9 +49,9 @@ module Halitosis
|
|
30
49
|
#
|
31
50
|
# @return [Hash]
|
32
51
|
#
|
33
|
-
def
|
52
|
+
def symbolize_hash(hash)
|
34
53
|
if hash.respond_to?(:transform_keys)
|
35
|
-
hash.transform_keys(&:to_sym).transform_values(&method(:
|
54
|
+
hash.transform_keys(&:to_sym).transform_values(&method(:symbolize_hash))
|
36
55
|
else
|
37
56
|
hash
|
38
57
|
end
|