json_factory 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c49c73f5c727e2f83caad602c203bd3a4003961
4
- data.tar.gz: 1b589f2f5ac1cb970f65cf741d6b5b7fa6013b16
3
+ metadata.gz: e4514793bfcddf9c571232e5cf6bef2f4f0e3604
4
+ data.tar.gz: e6370cc5a01e97d48237e9ae60b6f23f6acd6d13
5
5
  SHA512:
6
- metadata.gz: 04a4f6a100f963652dd97bfc74bdfab4c7e8ddd9c414422cf43767a44f9557178a93344e94958f64c7d6a356d362f4801fb4e32bcd25cb25dc272f4eb943ddcd
7
- data.tar.gz: 77002b8c955a39e6cd19635ec0e124f745bcef1cf546976b980d464af860fc1e32e04df61a0d193a86007bac76e006317ac37d6224481dc1f214c788a8c18947
6
+ metadata.gz: 7c49593eaacc118a094cec4ec95bd5a15c359507f870d2b7911e0445e1c2aeb5f87b065d38cfa67741368f187657dd623ba935bade271d0d7ea455e0ab3e9ce2
7
+ data.tar.gz: f4ed8f8663d43b36e5f5a4c23af22f6a151470655e4766c27869a12ac1594d62db271aafd67b6853c5760586248485f13e146b7b513b75dc131f9a1431cc7562
data/.rubocop.yml CHANGED
@@ -12,6 +12,9 @@ Style/ClassVars:
12
12
  Exclude:
13
13
  - lib/json_factory/json_builder.rb
14
14
 
15
+ Metrics/ClassLength:
16
+ Max: 150
17
+
15
18
  Metrics/LineLength:
16
19
  Max: 140
17
20
  Exclude:
data/README.md CHANGED
@@ -22,29 +22,33 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- | DSL Method | Description |
26
- | ------------- |:-------------------------------- |
27
- | json.object! | Create a json object |
28
- | json.array! | Create a json array |
29
- | json.member! | Create key value pair |
30
- | json.null! | Set object to "null" |
31
- | json.partial! | Load sub jfactory file |
32
- | json.cache! | Read and write from cache stores |
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! do |json|
39
- json.object!(:data) do |json|
40
- json.member!(:id, object.id)
41
- json.member!(:name, object.name)
42
-
43
- json.array!(object.test_objects, :test_array) do |json, test_object|
44
- json.member!(:id, test_object.id)
45
- json.member!(:name, test_object.name)
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
- # create context object
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.array! objects do |json, test_object|
80
- json.member!(:id, test_object.id)
81
- json.member!(:name, test_object.name)
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
- # create context object
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!(:id, test_object.id)
107
- json.member!(:name, test_object.name)
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
- # create context object
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!(:id, test_object.id)
125
- json.member!(:name, test_object.name)
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! do |json|
131
- json.partial!('tmp/test_partial', test_object: object)
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
- # create context object
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! do |json|
150
- json.object!(:data) do |json|
151
- json.cache! 'test-cache-key' do |json|
152
- json.member!(:id, object.id)
153
- json.member!(:name, object.name)
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
- # create context object
163
- context = JSONFactory::Context.new(object: test_object)
156
+ JSONFactory::Cache.instance.store = ActiveSupport::Cache::MemoryStore.new
164
157
 
165
- builder = JSONFactory::JSONBuilder.new(factory, context)
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
@@ -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
- require_relative '../lib/json_factory'
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(15) do |x|
34
+ Benchmark.bmbm(10) do |x|
32
35
  x.report(:json_factory) do
33
- builder = JSONFactory::JSONBuilder.load_factory_file('fixtures/test.jfactory')
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!(:id, context.id)
1
+ json.member :id, id
@@ -1,8 +1,10 @@
1
- json.array! objects do |json, 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.array!(test_object.test_array, :test_array) do |json, test_sub_object|
6
- json.partial! 'fixtures/test_partial', id: test_sub_object.id
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
@@ -1,19 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/json_factory'
2
4
 
3
5
  # test data
4
- test_object_1 = OpenStruct.new(id: '001', name: 'TestObject2')
5
- test_object_2 = OpenStruct.new(id: '002', name: 'TestObject3')
6
- test_object = OpenStruct.new(id: '1', name: 'TestObject1', description: 'Test2', test_objects: [test_object_1, test_object_2])
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
- json.object! do |json|
10
- json.object!(:data) do |json|
11
- json.member!(:id, object.id)
12
- json.member!(:name, object.name)
13
-
14
- json.array!(object.test_objects, :test_array) do |json, test_object|
15
- json.member!(:id, test_object.id)
16
- json.member!(:name, test_object.name)
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
- json.array! objects do |json, test_object|
31
- json.member!(:id, test_object.id)
32
- json.member!(:name, test_object.name)
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: [test_object_1, test_object_2])
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.authors = ['Alexander Klaiber']
11
- spec.email = ['alex.klaiber@gmail.com']
10
+ spec.author = 'Alexander Klaiber'
12
11
 
13
- spec.summary = 'JsonFactory is a Easy DSL to create JSON structures with the development focus on performance.'
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 'oj'
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/context'
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/json_object'
17
+ require_relative 'json_factory/builder'
12
18
 
13
19
  module JSONFactory
14
- autoload :Cache, 'json_factory/cache.rb'
15
- autoload :CacheError, 'json_factory/cache_error.rb'
16
- autoload :CacheStoreProxy, 'json_factory/cache_store_proxy.rb'
17
- autoload :BaseStoreProxy, 'json_factory/cache_store_proxy/base_store_proxy.rb'
18
- autoload :RedisStoreProxy, 'json_factory/cache_store_proxy/redis_store_proxy.rb'
19
- autoload :MemoryStoreProxy, 'json_factory/cache_store_proxy/memory_store_proxy.rb'
20
- autoload :FileStoreProxy, 'json_factory/cache_store_proxy/file_store_proxy.rb'
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
@@ -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
- attr_reader :store
16
- alias store_proxy store
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
- delegate :read, to: :store
23
- delegate :write, to: :store
24
- delegate :delete, to: :store
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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONFactory
4
+ class BuilderError < StandardError
5
+ end
6
+
7
+ class TypeNotAllowedError < BuilderError
8
+ end
9
+
10
+ class EmptyValueError < BuilderError
11
+ end
12
+ end
@@ -2,30 +2,161 @@
2
2
 
3
3
  module JSONFactory
4
4
  class JSONBuilder
5
- attr_accessor :output
6
- attr_reader :stream, :factory, :cache
5
+ BUILDER_VARIABLE_NAME = :json
7
6
 
8
- def self.load_factory_file(path, context = nil)
9
- raise "file format is invalid. #{path}" unless File.extname(path).eql?('.jfactory')
10
- raise "jfactory file #{path} not found" unless File.exist?(path)
11
- new(File.open(path).read, context)
12
- end
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(factory, context = nil)
15
- @factory = factory
14
+ def initialize(io, type = :value)
15
+ @stack = [State.new(io, type)]
16
16
  @cache = Cache.instance
17
- @context = context
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 build
21
- @output = StringIO.new
22
- @stream = Oj::StreamWriter.new(@output, indent: 0)
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
- JSONObject.new(self, @context).schema! { |json|
25
- json.instance_eval(@factory)
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
- @output.string.delete("\n").gsub('null}', 'null') if @output
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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONFactory
4
+ class State
5
+ attr_accessor :io, :type, :count
6
+
7
+ def initialize(io, type, count = 0)
8
+ @io = io
9
+ @type = type
10
+ @count = count
11
+ end
12
+ end
13
+ 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONFactory
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
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.3.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: 2017-12-08 00:00:00.000000000 Z
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: oj
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: JsonFactory is a Easy DSL to create JSON structures with the development
154
- focus on performance.
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/cache_error.rb
181
- - lib/json_factory/cache_store_proxy.rb
182
- - lib/json_factory/cache_store_proxy/base_store_proxy.rb
183
- - lib/json_factory/cache_store_proxy/file_store_proxy.rb
184
- - lib/json_factory/cache_store_proxy/memory_store_proxy.rb
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/json_object.rb
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 Easy DSL to create JSON structures with the development
214
- focus on performance.
212
+ summary: JsonFactory is a easy DSL to create JSON structures with a development focus
213
+ on performance.
215
214
  test_files: []
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONFactory
4
- class CacheError < StandardError
5
- end
6
- end
@@ -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
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONFactory
4
- module CacheStoreProxy
5
- class FileStoreProxy < BaseStoreProxy
6
- STORE_CLASS = ActiveSupport::Cache::FileStore.freeze
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONFactory
4
- module CacheStoreProxy
5
- class MemoryStoreProxy < BaseStoreProxy
6
- STORE_CLASS = ActiveSupport::Cache::MemoryStore.freeze
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONFactory
4
- module CacheStoreProxy
5
- class RedisStoreProxy < BaseStoreProxy
6
- STORE_CLASS = ActiveSupport::Cache::RedisStore.freeze
7
- end
8
- end
9
- end
@@ -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