json_factory 0.3.0 → 0.4.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/.rubocop.yml +3 -0
- data/README.md +43 -52
- data/examples/benchmark.rb +6 -4
- data/examples/fixtures/_test_partial.jfactory +1 -1
- data/examples/fixtures/test.jfactory +8 -6
- data/examples/readme_examples.rb +18 -17
- data/json_factory.gemspec +3 -5
- data/lib/json_factory.rb +24 -11
- data/lib/json_factory/builder.rb +25 -0
- data/lib/json_factory/cache.rb +6 -12
- data/lib/json_factory/configuration.rb +19 -0
- data/lib/json_factory/converter.rb +16 -0
- data/lib/json_factory/dsl.rb +196 -0
- data/lib/json_factory/dsl/object_array.rb +34 -0
- data/lib/json_factory/errors.rb +12 -0
- data/lib/json_factory/json_builder.rb +148 -17
- data/lib/json_factory/railtie.rb +21 -0
- data/lib/json_factory/state.rb +13 -0
- data/lib/json_factory/template_store.rb +31 -0
- data/lib/json_factory/version.rb +1 -1
- metadata +16 -17
- data/lib/json_factory/cache_error.rb +0 -6
- data/lib/json_factory/cache_store_proxy.rb +0 -13
- data/lib/json_factory/cache_store_proxy/base_store_proxy.rb +0 -30
- data/lib/json_factory/cache_store_proxy/file_store_proxy.rb +0 -9
- data/lib/json_factory/cache_store_proxy/memory_store_proxy.rb +0 -9
- data/lib/json_factory/cache_store_proxy/redis_store_proxy.rb +0 -9
- data/lib/json_factory/context.rb +0 -22
- data/lib/json_factory/json_object.rb +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4514793bfcddf9c571232e5cf6bef2f4f0e3604
|
4
|
+
data.tar.gz: e6370cc5a01e97d48237e9ae60b6f23f6acd6d13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c49593eaacc118a094cec4ec95bd5a15c359507f870d2b7911e0445e1c2aeb5f87b065d38cfa67741368f187657dd623ba935bade271d0d7ea455e0ab3e9ce2
|
7
|
+
data.tar.gz: f4ed8f8663d43b36e5f5a4c23af22f6a151470655e4766c27869a12ac1594d62db271aafd67b6853c5760586248485f13e146b7b513b75dc131f9a1431cc7562
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -22,29 +22,33 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
| DSL Method
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
| DSL Method | Description |
|
26
|
+
| ---------- |:------------------------------------------------------------------- |
|
27
|
+
| value | Generates a JSON value. |
|
28
|
+
| object | Generates a JSON object structure. |
|
29
|
+
| member | Adds a key-value pair to a JSON object. |
|
30
|
+
| array | Generates a JSON array structure. |
|
31
|
+
| element | Adds a value to a JSON array. |
|
32
|
+
| partial | Loads the given partial and evaluates it using the local variables. |
|
33
|
+
| cache | Caches the given content under the specified key. |
|
33
34
|
|
34
35
|
##### Top level object JSON structure
|
35
36
|
|
36
37
|
```ruby
|
37
38
|
factory = <<-RUBY
|
38
|
-
json.object
|
39
|
-
json.
|
40
|
-
json.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
json.object do
|
40
|
+
json.member :data do
|
41
|
+
json.object do
|
42
|
+
json.member :id, object.id
|
43
|
+
json.member :name, object.name
|
44
|
+
json.member :test_array do
|
45
|
+
json.object_array(object.test_objects) do |test_object|
|
46
|
+
json.member :id, test_object.id)
|
47
|
+
json.member :name, test_object.name
|
48
|
+
end
|
49
|
+
end
|
46
50
|
end
|
47
|
-
end
|
51
|
+
end
|
48
52
|
end
|
49
53
|
RUBY
|
50
54
|
|
@@ -53,10 +57,7 @@ test_object_1 = OpenStruct.new(id: '001', name: 'TestObject2')
|
|
53
57
|
test_object_2 = OpenStruct.new(id: '002', name: 'TestObject3')
|
54
58
|
test_object = OpenStruct.new(id: '1', name: 'TestObject1', test_objects: [test_object_1, test_object_2])
|
55
59
|
|
56
|
-
|
57
|
-
context = JSONFactory::Context.new(object: test_object)
|
58
|
-
|
59
|
-
puts JSONFactory::JSONBuilder.new(factory, context).build
|
60
|
+
puts JSONFactory.build(factory, object: test_object)
|
60
61
|
```
|
61
62
|
|
62
63
|
```json
|
@@ -76,9 +77,9 @@ puts JSONFactory::JSONBuilder.new(factory, context).build
|
|
76
77
|
|
77
78
|
```ruby
|
78
79
|
factory = <<-RUBY
|
79
|
-
json.
|
80
|
-
json.member
|
81
|
-
json.member
|
80
|
+
json.object_array objects do |test_object|
|
81
|
+
json.member :id, test_object.id
|
82
|
+
json.member :name, test_object.name
|
82
83
|
end
|
83
84
|
RUBY
|
84
85
|
|
@@ -86,10 +87,7 @@ RUBY
|
|
86
87
|
test_object_1 = OpenStruct.new(id: '001', name: 'TestObject2')
|
87
88
|
test_object_2 = OpenStruct.new(id: '002', name: 'TestObject3')
|
88
89
|
|
89
|
-
|
90
|
-
context = JSONFactory::Context.new(objects: [test_object_1, test_object_2])
|
91
|
-
|
92
|
-
puts JSONFactory::JSONBuilder.new(factory, context).build
|
90
|
+
puts JSONFactory.build(factory, objects: [test_object_1, test_object_2])
|
93
91
|
```
|
94
92
|
|
95
93
|
```json
|
@@ -103,32 +101,29 @@ puts JSONFactory::JSONBuilder.new(factory, context).build
|
|
103
101
|
|
104
102
|
```ruby
|
105
103
|
# tmp/test.jfactory
|
106
|
-
json.member
|
107
|
-
json.member
|
104
|
+
json.member :id, test_object.id
|
105
|
+
json.member :name, test_object.name
|
108
106
|
```
|
109
107
|
|
110
108
|
```ruby
|
111
109
|
# test data
|
112
110
|
test_object = OpenStruct.new(id: '1', name: 'TestObject1')
|
113
111
|
|
114
|
-
#
|
115
|
-
context = JSONFactory::Context.new(object: test_object)
|
116
|
-
|
117
|
-
puts JSONFactory::JSONBuilder.load_factory_file('tmp/test.jfactory', context).build # => { "id": 1, name: "TestObject1" }
|
112
|
+
puts JSONFactory.build('tmp/test.jfactory', object: test_object).build # => { "id": 1, name: "TestObject1" }
|
118
113
|
```
|
119
114
|
|
120
115
|
##### Load partials
|
121
116
|
|
122
117
|
```ruby
|
123
118
|
# tmp/_test_partial.jfactory
|
124
|
-
json.member
|
125
|
-
json.member
|
119
|
+
json.member :id, test_object.id
|
120
|
+
json.member :name, test_object.name
|
126
121
|
```
|
127
122
|
|
128
123
|
```ruby
|
129
124
|
# tmp/test.jfactory
|
130
|
-
json.object
|
131
|
-
json.partial
|
125
|
+
json.object do
|
126
|
+
json.partial 'tmp/test_partial', test_object: object
|
132
127
|
end
|
133
128
|
```
|
134
129
|
|
@@ -136,21 +131,20 @@ end
|
|
136
131
|
# test data
|
137
132
|
test_object = OpenStruct.new(id: '1', name: 'TestObject1')
|
138
133
|
|
139
|
-
#
|
140
|
-
context = JSONFactory::Context.new(object: test_object)
|
141
|
-
|
142
|
-
puts JSONFactory::JSONBuilder.load_factory_file('tmp/test.jfactory', context).build # => { "id": 1, name: "TestObject1" }
|
134
|
+
puts JSONFactory.build('tmp/test.jfactory', object: test_object).build # => { "id": 1, name: "TestObject1" }
|
143
135
|
```
|
144
136
|
|
145
137
|
##### Use cache stores
|
146
138
|
|
147
139
|
```ruby
|
148
140
|
factory = <<-RUBY
|
149
|
-
json.object
|
150
|
-
json.
|
151
|
-
json.
|
152
|
-
json.
|
153
|
-
|
141
|
+
json.object do
|
142
|
+
json.member :data do
|
143
|
+
json.object do
|
144
|
+
json.cache 'test-cache-key' do
|
145
|
+
json.member :id, object.id
|
146
|
+
json.member :name, object.name
|
147
|
+
end
|
154
148
|
end
|
155
149
|
end
|
156
150
|
end
|
@@ -159,12 +153,9 @@ RUBY
|
|
159
153
|
# test data
|
160
154
|
test_object = OpenStruct.new(id: '1', name: 'TestObject1')
|
161
155
|
|
162
|
-
|
163
|
-
context = JSONFactory::Context.new(object: test_object)
|
156
|
+
JSONFactory::Cache.instance.store = ActiveSupport::Cache::MemoryStore.new
|
164
157
|
|
165
|
-
|
166
|
-
builder.cache.store = ActiveSupport::Cache::MemoryStore.new
|
167
|
-
puts builder.build # => { "data": { "id": "1", "name": "TestObject1" } }
|
158
|
+
puts JSONFactory.build(factory, object: test_object) # => { "data": { "id": "1", "name": "TestObject1" } }
|
168
159
|
```
|
169
160
|
|
170
161
|
## Development
|
data/examples/benchmark.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
|
3
6
|
require 'jbuilder'
|
4
7
|
require 'benchmark'
|
5
8
|
require 'forgery'
|
6
9
|
|
7
|
-
|
10
|
+
require 'json_factory'
|
8
11
|
|
9
12
|
objects = []
|
10
13
|
sub_array = []
|
@@ -28,9 +31,8 @@ end
|
|
28
31
|
end
|
29
32
|
|
30
33
|
# CURRENT RUNTIME WITH 1000 and 10 = 0.133410
|
31
|
-
Benchmark.bmbm(
|
34
|
+
Benchmark.bmbm(10) do |x|
|
32
35
|
x.report(:json_factory) do
|
33
|
-
|
34
|
-
builder.build(JSONFactory::Context.new(objects: objects))
|
36
|
+
JSONFactory.build('fixtures/test.jfactory', objects: objects)
|
35
37
|
end
|
36
38
|
end
|
@@ -1 +1 @@
|
|
1
|
-
json.member
|
1
|
+
json.member :id, id
|
@@ -1,8 +1,10 @@
|
|
1
|
-
json.
|
2
|
-
json.member
|
3
|
-
json.member
|
4
|
-
json.member
|
5
|
-
json.
|
6
|
-
json.
|
1
|
+
json.object_array(objects) do |test_object|
|
2
|
+
json.member :id, test_object.id
|
3
|
+
json.member :name, test_object.name
|
4
|
+
json.member :description, test_object.description
|
5
|
+
json.member :test_array do
|
6
|
+
json.object_array(test_object.test_array) do |test_sub_object|
|
7
|
+
json.partial 'fixtures/test_partial', id: test_sub_object.id
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
data/examples/readme_examples.rb
CHANGED
@@ -1,19 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../lib/json_factory'
|
2
4
|
|
3
5
|
# test data
|
4
|
-
|
5
|
-
|
6
|
-
test_object = OpenStruct.new(id: '1', name: 'TestObject1', description: 'Test2', test_objects: [
|
6
|
+
test_object1 = OpenStruct.new(id: '001', name: 'TestObject2')
|
7
|
+
test_object2 = OpenStruct.new(id: '002', name: 'TestObject3')
|
8
|
+
test_object = OpenStruct.new(id: '1', name: 'TestObject1', description: 'Test2', test_objects: [test_object1, test_object2])
|
7
9
|
|
8
10
|
factory = <<-RUBY
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
object! do
|
12
|
+
object!(:data) do
|
13
|
+
member!(:id, object.id)
|
14
|
+
member!(:name, object.name)
|
15
|
+
member!(:test_array) do
|
16
|
+
array!(object.test_objects) do |test_object|
|
17
|
+
member!(:id, test_object.id)
|
18
|
+
member!(:name, test_object.name)
|
19
|
+
end
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -24,15 +27,13 @@ context = JSONFactory::Context.new(object: test_object)
|
|
24
27
|
|
25
28
|
puts JSONFactory::JSONBuilder.new(factory, context).build
|
26
29
|
|
27
|
-
|
28
|
-
|
29
30
|
factory = <<-RUBY
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
array! objects do |test_object|
|
32
|
+
member!(:id, test_object.id)
|
33
|
+
member!(:name, test_object.name)
|
33
34
|
end
|
34
35
|
RUBY
|
35
36
|
# create context object
|
36
|
-
context = JSONFactory::Context.new(objects: [
|
37
|
+
context = JSONFactory::Context.new(objects: [test_object1, test_object2])
|
37
38
|
|
38
39
|
puts JSONFactory::JSONBuilder.new(factory, context).build
|
data/json_factory.gemspec
CHANGED
@@ -7,11 +7,9 @@ require 'json_factory/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'json_factory'
|
9
9
|
spec.version = JSONFactory::VERSION
|
10
|
-
spec.
|
11
|
-
spec.email = ['alex.klaiber@gmail.com']
|
10
|
+
spec.author = 'Alexander Klaiber'
|
12
11
|
|
13
|
-
spec.summary = 'JsonFactory is a
|
14
|
-
spec.description = 'JsonFactory is a Easy DSL to create JSON structures with the development focus on performance.'
|
12
|
+
spec.summary = 'JsonFactory is a easy DSL to create JSON structures with a development focus on performance.'
|
15
13
|
spec.homepage = 'https://github.com/aklaiber/json_factory'
|
16
14
|
spec.license = 'MIT'
|
17
15
|
|
@@ -19,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
19
17
|
spec.require_paths = ['lib']
|
20
18
|
|
21
19
|
spec.add_runtime_dependency 'activesupport', '>= 5.1.0'
|
22
|
-
spec.add_runtime_dependency '
|
20
|
+
spec.add_runtime_dependency 'json'
|
23
21
|
spec.add_runtime_dependency 'redis-activesupport', '>= 5.0.0'
|
24
22
|
|
25
23
|
spec.add_development_dependency 'bundler'
|
data/lib/json_factory.rb
CHANGED
@@ -1,21 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'oj'
|
4
|
-
|
5
3
|
require 'active_support'
|
6
4
|
require 'active_support/cache/redis_store'
|
7
5
|
|
6
|
+
require 'json_factory/railtie' if defined?(Rails)
|
7
|
+
|
8
8
|
require_relative 'json_factory/version'
|
9
|
-
require_relative 'json_factory/
|
9
|
+
require_relative 'json_factory/configuration'
|
10
|
+
require_relative 'json_factory/errors'
|
11
|
+
require_relative 'json_factory/state'
|
12
|
+
require_relative 'json_factory/converter'
|
13
|
+
require_relative 'json_factory/dsl'
|
14
|
+
require_relative 'json_factory/dsl/object_array'
|
15
|
+
require_relative 'json_factory/template_store'
|
10
16
|
require_relative 'json_factory/json_builder'
|
11
|
-
require_relative 'json_factory/
|
17
|
+
require_relative 'json_factory/builder'
|
12
18
|
|
13
19
|
module JSONFactory
|
14
|
-
autoload :Cache,
|
15
|
-
autoload :
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
autoload :Cache, 'json_factory/cache'
|
21
|
+
autoload :TemplateStore, 'json_factory/template_store.rb'
|
22
|
+
|
23
|
+
def self.build(template, local_variables = {})
|
24
|
+
Builder.new(template, local_variables).build
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.configure
|
28
|
+
if block_given?
|
29
|
+
yield Configuration.instance
|
30
|
+
else
|
31
|
+
Configuration.instance
|
32
|
+
end
|
33
|
+
end
|
21
34
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONFactory
|
4
|
+
class Builder
|
5
|
+
def initialize(template, local_variables = {})
|
6
|
+
@io = StringIO.new
|
7
|
+
@template = template
|
8
|
+
@local_variables = local_variables
|
9
|
+
end
|
10
|
+
|
11
|
+
def context
|
12
|
+
@local_variables
|
13
|
+
end
|
14
|
+
|
15
|
+
def build
|
16
|
+
json_builder = JSONBuilder.new(@io)
|
17
|
+
if File.exist?(@template)
|
18
|
+
json_builder.render_template(@template, @local_variables)
|
19
|
+
else
|
20
|
+
json_builder.render_string(@template, @local_variables)
|
21
|
+
end
|
22
|
+
@io.string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/json_factory/cache.rb
CHANGED
@@ -3,24 +3,18 @@
|
|
3
3
|
module JSONFactory
|
4
4
|
class Cache
|
5
5
|
include Singleton
|
6
|
-
|
7
|
-
attr_accessor :prefix
|
6
|
+
attr_accessor :store, :prefix
|
8
7
|
|
9
8
|
def initialize
|
10
9
|
@prefix = 'json_factory'
|
11
|
-
|
12
|
-
self.store = ::Rails.cache if defined?(::Rails.cache)
|
13
10
|
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
def store=(store)
|
19
|
-
@store = CacheStoreProxy.build(store)
|
12
|
+
def transform_key(key)
|
13
|
+
[prefix, key].compact.join(':')
|
20
14
|
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
def fetch(key, options = nil, &block)
|
17
|
+
store.fetch(transform_key(key), options, &block)
|
18
|
+
end
|
25
19
|
end
|
26
20
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module JSONFactory
|
6
|
+
class Configuration
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
attr_reader :helpers
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@helpers = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def include_helper(mod)
|
16
|
+
@helpers.push(mod)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module JSONFactory
|
6
|
+
module Converter
|
7
|
+
def self.json_key(object)
|
8
|
+
json_value(object.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.json_value(object)
|
12
|
+
raise "don't know how to convert #{object.inspect} (#{object.class})" unless object.respond_to?(:to_json)
|
13
|
+
object.to_json
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONFactory
|
4
|
+
class DSL
|
5
|
+
def self.check_arity(argc, expected)
|
6
|
+
return if expected === argc # rubocop:disable Style/CaseEquality
|
7
|
+
raise ArgumentError, "wrong number of arguments (given #{argc}, expected #{expected})"
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(builder)
|
11
|
+
@builder = builder
|
12
|
+
end
|
13
|
+
|
14
|
+
# :call-seq:
|
15
|
+
# json.value(value) -> nil
|
16
|
+
#
|
17
|
+
# Generates a JSON value.
|
18
|
+
#
|
19
|
+
# json.value 1 # generates: 1
|
20
|
+
# json.value nil # generates: null
|
21
|
+
# json.value :foo # generates: "foo"
|
22
|
+
def value(value)
|
23
|
+
warn 'given block not used' if block_given?
|
24
|
+
@builder.value(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
# :call-seq:
|
28
|
+
# json.array -> nil
|
29
|
+
# json.array { block } -> nil
|
30
|
+
#
|
31
|
+
# Generates a JSON array structure.
|
32
|
+
#
|
33
|
+
# The block is evaluated in order to add element to the array.
|
34
|
+
#
|
35
|
+
# If no block is given, an empty array is generated.
|
36
|
+
#
|
37
|
+
# json.array
|
38
|
+
# # generates: []
|
39
|
+
#
|
40
|
+
# json.array do
|
41
|
+
# json.element 1
|
42
|
+
# json.element 2
|
43
|
+
# end
|
44
|
+
# # generates: [1,2]
|
45
|
+
def array(&block)
|
46
|
+
@builder.array(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
# :call-seq:
|
50
|
+
# json.element(value) -> nil
|
51
|
+
# json.element { block } -> nil
|
52
|
+
#
|
53
|
+
# Adds a value to a JSON array. Results in an exception if called outside an
|
54
|
+
# array.
|
55
|
+
#
|
56
|
+
# If an argument is given, it is used as the element's value.
|
57
|
+
#
|
58
|
+
# If a block is given, it is evaluated in order to add nested structures.
|
59
|
+
#
|
60
|
+
# json.array do
|
61
|
+
# json.element 1
|
62
|
+
# json.element 2
|
63
|
+
# json.element do
|
64
|
+
# json.array do
|
65
|
+
# json.element 3
|
66
|
+
# json.element 4
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# # generates: [1,2,[3,4]]
|
71
|
+
def element(*args)
|
72
|
+
if block_given?
|
73
|
+
DSL.check_arity(args.length, 0..1)
|
74
|
+
warn 'block supersedes value argument' if args.length == 1 && block_given?
|
75
|
+
@builder.element { yield }
|
76
|
+
else
|
77
|
+
DSL.check_arity(args.length, 1)
|
78
|
+
@builder.element(*args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# :call-seq:
|
83
|
+
# json.object -> nil
|
84
|
+
# json.object { block } -> nil
|
85
|
+
#
|
86
|
+
# Generates a JSON object structure.
|
87
|
+
#
|
88
|
+
# The block is evaluated in order to add key-value pairs to the object.
|
89
|
+
#
|
90
|
+
# If no block is given, an empty object is generated.
|
91
|
+
#
|
92
|
+
# json.object
|
93
|
+
# # generates: {}
|
94
|
+
#
|
95
|
+
# json.object do
|
96
|
+
# json.member :foo, 1
|
97
|
+
# json.member :bar, 2
|
98
|
+
# end
|
99
|
+
# # generates: {"foo":1,"bar":2}
|
100
|
+
def object
|
101
|
+
if block_given?
|
102
|
+
@builder.object { yield }
|
103
|
+
else
|
104
|
+
@builder.object
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# :call-seq:
|
109
|
+
# json.member(key, value) -> nil
|
110
|
+
# json.member(key) { block } -> nil
|
111
|
+
#
|
112
|
+
# Adds a key-value pair to a JSON object. Results in an exception if called
|
113
|
+
# outside an object.
|
114
|
+
#
|
115
|
+
# The first argument is used as the key. If a second argument is given, it
|
116
|
+
# is used as the member's value.
|
117
|
+
#
|
118
|
+
# If a block is given, it is evaluated in order to add nested structures.
|
119
|
+
#
|
120
|
+
# json.object do
|
121
|
+
# json.member :foo, 1
|
122
|
+
# json.member :bar, 2
|
123
|
+
# json.member :baz do
|
124
|
+
# json.array do
|
125
|
+
# json.element 3
|
126
|
+
# json.element 4
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
# # generates: {"foo":1,"bar":2,"baz":[3,4]}
|
131
|
+
def member(*args)
|
132
|
+
if block_given?
|
133
|
+
DSL.check_arity(args.length, 1..2)
|
134
|
+
warn 'block supersedes value argument' if args.length == 2 && block_given?
|
135
|
+
@builder.member(*args) { yield }
|
136
|
+
else
|
137
|
+
DSL.check_arity(args.length, 2)
|
138
|
+
@builder.member(*args)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# :call-seq:
|
143
|
+
# json.cache(key) { block } -> nil
|
144
|
+
#
|
145
|
+
# Caches the given content under the specified key.
|
146
|
+
#
|
147
|
+
# If a cache entry is found, it is added to the output. Otherwise, the given
|
148
|
+
# block is evaluated and the result is stored in the cache.
|
149
|
+
#
|
150
|
+
# json.object do
|
151
|
+
# json.member :foo, 1
|
152
|
+
# cache 'test-key' do
|
153
|
+
# json.member :bar, 2 # will be stored in the cache
|
154
|
+
# end
|
155
|
+
# cache 'test-key' do
|
156
|
+
# json.member :baz, 3 # will be ignored
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
# # generates: {"foo":1,"bar":2,"bar":2}
|
160
|
+
#
|
161
|
+
# When caching object members or array elements, commas are inserted as
|
162
|
+
# needed. However, no further checks are performed, so improper use may
|
163
|
+
# result in invalid JSON, for example by adding cached object members to an
|
164
|
+
# array.
|
165
|
+
def cache(key)
|
166
|
+
@builder.cache(key) { yield }
|
167
|
+
end
|
168
|
+
|
169
|
+
# :call-seq:
|
170
|
+
# json.partial(file, local_variables = {})
|
171
|
+
#
|
172
|
+
# Loads the given partial and evaluates it using the local variables.
|
173
|
+
#
|
174
|
+
# # simple.jfactory
|
175
|
+
# json.object do
|
176
|
+
# json.member :name, name
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# json.array do
|
180
|
+
# json.element do
|
181
|
+
# json.partial 'simple.jfactory', name: 'foo'
|
182
|
+
# end
|
183
|
+
# json.element do
|
184
|
+
# json.partial 'simple.jfactory', name: 'bar'
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# # generates: [{"name":"foo"},{"name":"bar"}]
|
188
|
+
#
|
189
|
+
# Partial files are loaded only once to minimize file access.
|
190
|
+
def partial(file, local_variables = {})
|
191
|
+
path = Pathname.new(File.expand_path(file))
|
192
|
+
path = "#{path.dirname}/_#{path.basename}.jfactory" if path.extname.empty?
|
193
|
+
@builder.partial(path.to_s, local_variables)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONFactory
|
4
|
+
class DSL
|
5
|
+
# Helper method to generate an array of objects.
|
6
|
+
#
|
7
|
+
# json.object_array([1,2,3]) do |id|
|
8
|
+
# json.member :id, id
|
9
|
+
# end
|
10
|
+
# # generates: [{"id":1},{"id":2},{"id":2}]
|
11
|
+
#
|
12
|
+
# The above is equivalent to:
|
13
|
+
#
|
14
|
+
# json.array do
|
15
|
+
# [1,2,3].each do |id|
|
16
|
+
# json.object do
|
17
|
+
# json.member :id, id
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# # generates: [{"id":1},{"id":2},{"id":2}]
|
22
|
+
def object_array(collection)
|
23
|
+
array do
|
24
|
+
collection.each do |*values|
|
25
|
+
element do
|
26
|
+
object do
|
27
|
+
yield(*values)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -2,30 +2,161 @@
|
|
2
2
|
|
3
3
|
module JSONFactory
|
4
4
|
class JSONBuilder
|
5
|
-
|
6
|
-
attr_reader :stream, :factory, :cache
|
5
|
+
BUILDER_VARIABLE_NAME = :json
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
TOKEN_LEFT_SQUARE_BRACKET = '['
|
8
|
+
TOKEN_RIGHT_SQUARE_BRACKET = ']'
|
9
|
+
TOKEN_LEFT_CURLY_BRACKET = '{'
|
10
|
+
TOKEN_RIGHT_CURLY_BRACKET = '}'
|
11
|
+
TOKEN_COLON = ':'
|
12
|
+
TOKEN_COMMA = ','
|
13
13
|
|
14
|
-
def initialize(
|
15
|
-
@
|
14
|
+
def initialize(io, type = :value)
|
15
|
+
@stack = [State.new(io, type)]
|
16
16
|
@cache = Cache.instance
|
17
|
-
@
|
17
|
+
@template_store = TemplateStore.instance
|
18
|
+
end
|
19
|
+
|
20
|
+
def value(value = nil)
|
21
|
+
raise TypeNotAllowedError, 'Can only add value as a value' unless type == :value
|
22
|
+
raise TypeNotAllowedError, 'Cannot add multiple values' unless count.zero?
|
23
|
+
add_value(value)
|
24
|
+
increment_count
|
25
|
+
end
|
26
|
+
|
27
|
+
def array
|
28
|
+
raise TypeNotAllowedError, 'Can only add array as a value' unless type == :value
|
29
|
+
raise TypeNotAllowedError, 'Cannot add multiple values' unless count.zero?
|
30
|
+
|
31
|
+
io << TOKEN_LEFT_SQUARE_BRACKET
|
32
|
+
push_type(:array) { yield } if block_given?
|
33
|
+
io << TOKEN_RIGHT_SQUARE_BRACKET
|
34
|
+
increment_count
|
35
|
+
end
|
36
|
+
|
37
|
+
def element(value = nil)
|
38
|
+
raise TypeNotAllowedError, 'Can only add an element within an array' unless type == :array
|
39
|
+
|
40
|
+
add_separator
|
41
|
+
if block_given?
|
42
|
+
push_type(:value) { yield }
|
43
|
+
else
|
44
|
+
add_value(value)
|
45
|
+
end
|
46
|
+
increment_count
|
47
|
+
end
|
48
|
+
|
49
|
+
def object
|
50
|
+
raise TypeNotAllowedError, 'Can only add object as a value' unless type == :value
|
51
|
+
raise TypeNotAllowedError, 'Cannot add multiple values' unless count.zero?
|
52
|
+
|
53
|
+
io << TOKEN_LEFT_CURLY_BRACKET
|
54
|
+
push_type(:object) { yield } if block_given?
|
55
|
+
io << TOKEN_RIGHT_CURLY_BRACKET
|
56
|
+
increment_count
|
57
|
+
end
|
58
|
+
|
59
|
+
def member(key, value = nil)
|
60
|
+
raise TypeNotAllowedError, 'Can only add a member within an object' unless type == :object
|
61
|
+
|
62
|
+
add_separator
|
63
|
+
io << Converter.json_key(key)
|
64
|
+
io << TOKEN_COLON
|
65
|
+
if block_given?
|
66
|
+
push_type(:value) { yield }
|
67
|
+
else
|
68
|
+
add_value(value)
|
69
|
+
end
|
70
|
+
increment_count
|
71
|
+
end
|
72
|
+
|
73
|
+
def cache(key)
|
74
|
+
value = @cache.fetch(key) do
|
75
|
+
cache_io = StringIO.new
|
76
|
+
push_io(cache_io) { yield }
|
77
|
+
cache_io.string
|
78
|
+
end
|
79
|
+
raise EmptyValueError if value.empty?
|
80
|
+
|
81
|
+
add_separator
|
82
|
+
io << value
|
83
|
+
increment_count
|
84
|
+
end
|
85
|
+
|
86
|
+
def evaluate(string, local_variables, filename)
|
87
|
+
binding = jfactory
|
88
|
+
local_variables.each_pair do |key, value|
|
89
|
+
binding.local_variable_set(key, value)
|
90
|
+
end
|
91
|
+
binding.local_variable_set(BUILDER_VARIABLE_NAME, DSL.new(self))
|
92
|
+
eval(string, binding, filename.to_s) # rubocop:disable Security/Eval
|
93
|
+
end
|
94
|
+
|
95
|
+
def render_template(filename, local_variables)
|
96
|
+
template = @template_store.get(filename)
|
97
|
+
evaluate(template, local_variables, filename)
|
98
|
+
end
|
99
|
+
alias partial render_template
|
100
|
+
|
101
|
+
def render_string(string, local_variables)
|
102
|
+
evaluate(string, local_variables, '(inline)')
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def add_value(value)
|
108
|
+
io << Converter.json_value(value)
|
18
109
|
end
|
19
110
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
111
|
+
def add_separator
|
112
|
+
io << TOKEN_COMMA unless count.zero?
|
113
|
+
end
|
114
|
+
|
115
|
+
def io
|
116
|
+
@stack.last.io
|
117
|
+
end
|
118
|
+
|
119
|
+
def type
|
120
|
+
@stack.last.type
|
121
|
+
end
|
122
|
+
|
123
|
+
def count
|
124
|
+
@stack.last.count
|
125
|
+
end
|
126
|
+
|
127
|
+
def increment_count
|
128
|
+
@stack.last.count += 1
|
129
|
+
end
|
130
|
+
|
131
|
+
def push_io(io)
|
132
|
+
@stack.push(State.new(io, type))
|
133
|
+
yield
|
134
|
+
@stack.pop
|
135
|
+
end
|
136
|
+
|
137
|
+
def push_type(type)
|
138
|
+
@stack.push(State.new(io, type))
|
139
|
+
yield
|
140
|
+
raise EmptyValueError if type == :value && count.zero?
|
141
|
+
@stack.pop
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
23
145
|
|
24
|
-
|
25
|
-
|
26
|
-
|
146
|
+
JSONFactory::JSONBuilder.class_eval do
|
147
|
+
# Returns an empty evaluation context, similar to Ruby's main object.
|
148
|
+
def jfactory
|
149
|
+
Object.allocate.instance_eval do
|
150
|
+
class << self
|
151
|
+
JSONFactory.configure.helpers.each { |mod| include mod }
|
27
152
|
|
28
|
-
|
153
|
+
def to_s
|
154
|
+
'jfactory'
|
155
|
+
end
|
156
|
+
alias inspect to_s
|
157
|
+
end
|
158
|
+
return binding
|
29
159
|
end
|
30
160
|
end
|
161
|
+
private :jfactory
|
31
162
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONFactory
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer 'json_factory.cache' do
|
6
|
+
Cache.instance.store = Rails.cache
|
7
|
+
end
|
8
|
+
|
9
|
+
initializer 'json_factory.jfactory_watcher' do |app|
|
10
|
+
jfactory_reloader = app.config.file_watcher.new(Dir.glob(::Rails.root.join('app/views/**/*.jfactory'))) do
|
11
|
+
TemplateStore.instance.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
app.reloaders << jfactory_reloader
|
15
|
+
|
16
|
+
config.to_prepare do
|
17
|
+
jfactory_reloader.execute_if_updated
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONFactory
|
4
|
+
class TemplateStore
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@templates = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path)
|
12
|
+
if @templates.key? path
|
13
|
+
@templates.fetch(path)
|
14
|
+
else
|
15
|
+
@templates.store(path, read_template(path))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear
|
20
|
+
@templates.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def read_template(path)
|
26
|
+
raise "file format is invalid. #{path}" unless File.extname(path).eql?('.jfactory')
|
27
|
+
raise "jfactory file #{path} not found" unless File.exist?(path)
|
28
|
+
File.read(path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/json_factory/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_factory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Klaiber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 5.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: json
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -150,10 +150,8 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
-
description:
|
154
|
-
|
155
|
-
email:
|
156
|
-
- alex.klaiber@gmail.com
|
153
|
+
description:
|
154
|
+
email:
|
157
155
|
executables: []
|
158
156
|
extensions: []
|
159
157
|
extra_rdoc_files: []
|
@@ -176,16 +174,17 @@ files:
|
|
176
174
|
- examples/readme_examples.rb
|
177
175
|
- json_factory.gemspec
|
178
176
|
- lib/json_factory.rb
|
177
|
+
- lib/json_factory/builder.rb
|
179
178
|
- lib/json_factory/cache.rb
|
180
|
-
- lib/json_factory/
|
181
|
-
- lib/json_factory/
|
182
|
-
- lib/json_factory/
|
183
|
-
- lib/json_factory/
|
184
|
-
- lib/json_factory/
|
185
|
-
- lib/json_factory/cache_store_proxy/redis_store_proxy.rb
|
186
|
-
- lib/json_factory/context.rb
|
179
|
+
- lib/json_factory/configuration.rb
|
180
|
+
- lib/json_factory/converter.rb
|
181
|
+
- lib/json_factory/dsl.rb
|
182
|
+
- lib/json_factory/dsl/object_array.rb
|
183
|
+
- lib/json_factory/errors.rb
|
187
184
|
- lib/json_factory/json_builder.rb
|
188
|
-
- lib/json_factory/
|
185
|
+
- lib/json_factory/railtie.rb
|
186
|
+
- lib/json_factory/state.rb
|
187
|
+
- lib/json_factory/template_store.rb
|
189
188
|
- lib/json_factory/version.rb
|
190
189
|
homepage: https://github.com/aklaiber/json_factory
|
191
190
|
licenses:
|
@@ -210,6 +209,6 @@ rubyforge_project:
|
|
210
209
|
rubygems_version: 2.6.11
|
211
210
|
signing_key:
|
212
211
|
specification_version: 4
|
213
|
-
summary: JsonFactory is a
|
214
|
-
|
212
|
+
summary: JsonFactory is a easy DSL to create JSON structures with a development focus
|
213
|
+
on performance.
|
215
214
|
test_files: []
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSONFactory
|
4
|
-
module CacheStoreProxy
|
5
|
-
PROXIES = [RedisStoreProxy, MemoryStoreProxy, FileStoreProxy].freeze
|
6
|
-
|
7
|
-
def self.build(store)
|
8
|
-
store_proxy = PROXIES.find { |proxy| proxy.handle?(store) }
|
9
|
-
fail CacheError, "#{store.class} is a not supported cache" if store_proxy.nil?
|
10
|
-
store_proxy.new(store)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSONFactory
|
4
|
-
module CacheStoreProxy
|
5
|
-
class BaseStoreProxy
|
6
|
-
attr_reader :store
|
7
|
-
|
8
|
-
def self.handle?(store)
|
9
|
-
defined?(self::STORE_CLASS) && store.is_a?(self::STORE_CLASS)
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(store, prefix = 'json_factory')
|
13
|
-
@store = store
|
14
|
-
@prefix = prefix
|
15
|
-
end
|
16
|
-
|
17
|
-
def read(key, options = nil)
|
18
|
-
store.read("#{@prefix}:#{key}", options)
|
19
|
-
end
|
20
|
-
|
21
|
-
def write(key, value, options = nil)
|
22
|
-
store.write("#{@prefix}:#{key}", value, options)
|
23
|
-
end
|
24
|
-
|
25
|
-
def delete(key, options = nil)
|
26
|
-
store.delete("#{@prefix}:#{key}", options)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/json_factory/context.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSONFactory
|
4
|
-
class Context
|
5
|
-
def initialize(data = nil)
|
6
|
-
@data = data
|
7
|
-
end
|
8
|
-
|
9
|
-
def add(key, value)
|
10
|
-
@data[key] = value
|
11
|
-
end
|
12
|
-
|
13
|
-
def method_missing(method_name, *arguments, &block)
|
14
|
-
return @data[method_name] if @data.is_a?(Hash) && @data.key?(method_name)
|
15
|
-
super
|
16
|
-
end
|
17
|
-
|
18
|
-
def respond_to_missing?(method_name, include_private = false)
|
19
|
-
(@data.is_a?(Hash) && @data.key?(method_name)) || super
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
module JSONFactory
|
2
|
-
class JSONObject
|
3
|
-
attr_reader :json_builder
|
4
|
-
attr_accessor :context
|
5
|
-
|
6
|
-
@@partials = {}
|
7
|
-
|
8
|
-
def initialize(json_builder, context = nil)
|
9
|
-
@json_builder = json_builder
|
10
|
-
@context = context
|
11
|
-
end
|
12
|
-
|
13
|
-
def open_object
|
14
|
-
json_builder.stream.push_object
|
15
|
-
end
|
16
|
-
|
17
|
-
def close_object
|
18
|
-
json_builder.stream.pop
|
19
|
-
end
|
20
|
-
|
21
|
-
def schema!(object = nil)
|
22
|
-
yield self, object
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
|
-
def object!(key = nil, &block)
|
27
|
-
json_object = JSONObject.new(json_builder)
|
28
|
-
|
29
|
-
json_builder.stream.push_key(key.to_s) if key
|
30
|
-
|
31
|
-
json_object.open_object
|
32
|
-
json_object.schema!(&block)
|
33
|
-
json_object.close_object
|
34
|
-
end
|
35
|
-
|
36
|
-
def array!(array, key = nil, &block)
|
37
|
-
json_builder.stream.push_array(key ? key.to_s : nil)
|
38
|
-
array.each do |object|
|
39
|
-
builder = JSONObject.new(json_builder)
|
40
|
-
builder.open_object
|
41
|
-
builder.schema!(object, &block)
|
42
|
-
builder.close_object
|
43
|
-
end
|
44
|
-
json_builder.stream.pop
|
45
|
-
end
|
46
|
-
|
47
|
-
def member!(key, value = nil)
|
48
|
-
json_builder.stream.push_value(value, key.to_s)
|
49
|
-
end
|
50
|
-
|
51
|
-
def null!
|
52
|
-
json_builder.output.seek(json_builder.output.pos - 1)
|
53
|
-
json_builder.output.puts('null')
|
54
|
-
end
|
55
|
-
|
56
|
-
def cache!(cache_key, &block)
|
57
|
-
json = json_builder.cache.read(cache_key) if cache_key && json_builder.cache
|
58
|
-
return json_builder.output.puts(json) unless json.blank?
|
59
|
-
|
60
|
-
start_cache_pos = json_builder.output.pos
|
61
|
-
self.schema!(&block)
|
62
|
-
end_cache_pos = json_builder.output.pos
|
63
|
-
|
64
|
-
json_builder.cache.write(cache_key, json_builder.output.string[start_cache_pos..end_cache_pos])
|
65
|
-
end
|
66
|
-
|
67
|
-
def partial!(path, data = {})
|
68
|
-
path = Pathname.new(path)
|
69
|
-
|
70
|
-
if path.extname.eql?('.jfactory')
|
71
|
-
@@partials[path] ||= File.open(path).read
|
72
|
-
else
|
73
|
-
@@partials[path] ||= File.open("#{path.dirname}/_#{path.basename}.jfactory").read
|
74
|
-
end
|
75
|
-
|
76
|
-
JSONObject.new(json_builder, JSONFactory::Context.new(data)).schema! do |json|
|
77
|
-
json.instance_eval(@@partials[path])
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def method_missing(method_name, *arguments, &block)
|
82
|
-
return context.send(method_name) if context.respond_to?(method_name)
|
83
|
-
super
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|