babl-json 0.4.0 → 0.5.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: 03dff31c23ccad4de2970e0e9f1e8c0ac2df3257
4
- data.tar.gz: 88bfe61b0d2fcfc20ff71a4e009d6ddf15416e00
3
+ metadata.gz: 1bad3eaac421de5e967efa6a17096a9a95529174
4
+ data.tar.gz: e722e283eb9a3004a935a28bac8b09f8f94a10e6
5
5
  SHA512:
6
- metadata.gz: c9c04e5150dd7aeda65aefcb989f4a72eb5aed5b5afd0f18edae0495818e45b6fad658c1d693375ff711329c0c036e0c333d0d5ce99349f100c8a2da0b98f07c
7
- data.tar.gz: 720f05f879639374ab4a26bc7be49b4d32c6fb7c8534621643d09bb2d3a4f402cedc8af994682268760fcdc2418ac72e6cf7e9ffd9bda8dbf14d4d914683cec7
6
+ metadata.gz: fa8c6c330b936f4c1b4a3a455bba8491a43911c7c644d161b233215e8a436e2091722e6d85c0005d2b6aa89f5641d69dddb8d50ebd1cc49010368caa434633fb
7
+ data.tar.gz: 9a6a76cc67edcf4f7f2b952b645d5fa974f492294dde354925c9eb3aa794dd3c210689c55b082923ee4682eaaa960cc6db540dd78776cc10ac7fc6cb83758bc6
@@ -6,32 +6,84 @@ require 'babl/rendering'
6
6
  require 'babl/operators'
7
7
 
8
8
  module Babl
9
+ # There is basically two ways to use BABL: the Rails way, and the clean way.
10
+ #
11
+ # Rails way
12
+ # - BABL detects Rails and integrates with it automatically (only if BABL is required AFTER Rails).
13
+ # - BABL configuration is global (defined via Babl.configure).
14
+ # - Global configuration is also used by:
15
+ # Babl.template
16
+ # Babl.compile
17
+ # Babl.source
18
+ #
19
+ # Clean way
20
+ # - You can decide not to use global configuration. It will result in more verbosity
21
+ # but it protects you from global settings.
22
+ # - Equivalences:
23
+ # Babl.template ==> Babl::Template.new
24
+ # Babl.compile ==> Babl::Template#compile
25
+ # Babl.source ==> Babl::Template#source
26
+ #
9
27
  class Config
10
- attr_accessor :search_path, :preloader, :pretty, :cache_templates
28
+ attr_accessor :preloader, # No practical use outside Bannerman today.
29
+ :pretty, # Pretty format JSON output (boolean).
30
+ :cache_templates, # Enable or disable caching of compiled templates (Rails only, boolean).
31
+ :lookup_context, # Specify how to find templates.
32
+ :using # List of user-defined modules containing custom operators.
11
33
 
12
34
  def initialize
13
- @search_path = nil
14
35
  @preloader = Rendering::NoopPreloader
15
36
  @pretty = true
16
37
  @cache_templates = false
38
+ @lookup_context = nil
39
+ @using = []
40
+ end
41
+ end
42
+
43
+ class AbsoluteLookupContext
44
+ attr_reader :search_path
45
+
46
+ def initialize(search_path)
47
+ @search_path = search_path
48
+ raise Errors::InvalidTemplate, 'Missing search path' unless search_path
49
+ end
50
+
51
+ def find(current_template, partial_name)
52
+ query = File.join(search_path, "{#{partial_name}}{.babl,}")
53
+ path = Dir[query].first
54
+ return unless path
55
+ source = File.read(path)
56
+ [current_template.source(source, path, 0), self]
17
57
  end
18
58
  end
19
59
 
20
60
  class << self
21
- def compile(&block)
22
- source(&block).compile(
61
+ def compile(*args, &block)
62
+ raise ArgumentError, 'Wrong number of arguments' if args.size > 1
63
+ raise ArgumentError, 'Template or block expected' unless args.empty? ^ block.nil?
64
+
65
+ (args.empty? ? source(&block) : template.call(args.first)).compile(
23
66
  pretty: config.pretty,
24
- preloader: config.preloader
67
+ preloader: config.preloader,
68
+ lookup_context: config.lookup_context
25
69
  )
26
70
  end
27
71
 
28
- def source(&block)
29
- template = Template.new
30
- if config.search_path
31
- ctx = Operators::Partial::AbsoluteLookupContext.new(config.search_path)
32
- template = template.with_lookup_context(ctx)
33
- end
34
- template.source(&block)
72
+ def source(*args, &block)
73
+ template.source(*args, &block)
74
+ end
75
+
76
+ def template
77
+ cached = @cached_template
78
+ return cached.last if cached && config.using == cached.first
79
+ # Calling 'using' is a very inefficient operation, because
80
+ # it creates a new class. We can avoid that cost most of the
81
+ # time, assuming 'config.using' does not change often (typically
82
+ # it should only change once at startup)
83
+ modules = config.using.dup
84
+ template = Template.new.using(*modules)
85
+ @cached_template = [modules, template]
86
+ template
35
87
  end
36
88
 
37
89
  def configure
@@ -57,11 +57,7 @@ module Babl
57
57
  end
58
58
 
59
59
  def wrap
60
- rescope { |bound| yield bind(bound) }
61
- end
62
-
63
- def rescope(&block)
64
- dup.tap { |tb| tb.instance_variable_set(:@scope, block) }
60
+ self.class.new { |bound| yield bind(bound) }
65
61
  end
66
62
  end
67
63
 
@@ -8,17 +8,20 @@ module Babl
8
8
  module Builder
9
9
  # TemplateBase is a thin wrapper around Builder.
10
10
  #
11
- # Since the BABL code is run via #instance_eval within an instance of this class, we want to
11
+ # Since the BABL code is run via #instance_exec within an instance of this class, we want to
12
12
  # define as few methods as possible here.
13
- class TemplateBase < Utils::Value.new(:builder)
13
+ class TemplateBase
14
+ attr_reader :builder
15
+
14
16
  def initialize(builder = ChainBuilder.new(&:itself))
15
- super(builder)
17
+ @builder = builder
18
+ freeze
16
19
  end
17
20
 
18
- def compile(preloader: Rendering::NoopPreloader, pretty: true, optimize: true)
21
+ def compile(preloader: Rendering::NoopPreloader, pretty: true, optimize: true, lookup_context: nil)
19
22
  # Compute dependencies & schema on the non-simplified node tree in order
20
23
  # to catch all errors.
21
- tree = precompile
24
+ tree = precompile(lookup_context: lookup_context)
22
25
  dependencies = tree.dependencies
23
26
  schema = tree.schema
24
27
 
@@ -40,13 +43,13 @@ module Babl
40
43
  end
41
44
 
42
45
  def unscoped
43
- self.class.new builder.rescope(&:itself)
46
+ self.class.new
44
47
  end
45
48
 
46
49
  protected
47
50
 
48
- def precompile
49
- builder.precompile(Nodes::TerminalValue.instance)
51
+ def precompile(node = Nodes::TerminalValue.instance, **context)
52
+ builder.precompile(node, **context)
50
53
  end
51
54
 
52
55
  def construct_node(**new_context, &block)
@@ -21,4 +21,5 @@ require 'babl/operators/source'
21
21
  require 'babl/operators/static'
22
22
  require 'babl/operators/switch'
23
23
  require 'babl/operators/typed'
24
+ require 'babl/operators/using'
24
25
  require 'babl/operators/with'
@@ -13,7 +13,7 @@ module Babl
13
13
  protected
14
14
 
15
15
  # Override TemplateBase#precompile to add parent dependencies verification
16
- def precompile
16
+ def precompile(*)
17
17
  Nodes::Parent::Verifier.new(super)
18
18
  end
19
19
  end
@@ -8,40 +8,14 @@ module Babl
8
8
  # Load a partial template given its name
9
9
  # A 'lookup_context' must be defined
10
10
  def partial(partial_name)
11
- raise Errors::InvalidTemplate, 'Cannot use partial without lookup context' unless lookup_context
12
-
13
- path, source, partial_lookup_context = lookup_context.find(partial_name)
14
- raise Errors::InvalidTemplate, "Cannot find partial '#{partial_name}'" unless path
15
-
16
- with_lookup_context(partial_lookup_context)
17
- .source(source, path, 0)
18
- .with_lookup_context(lookup_context)
19
- end
20
-
21
- def with_lookup_context(lookup_context)
22
- self.class.new(builder.dup.tap { |inst| inst.instance_variable_set(:@lookup_context, lookup_context) })
23
- end
24
-
25
- def lookup_context
26
- builder.instance_variable_get(:@lookup_context)
27
- end
28
- end
29
-
30
- class AbsoluteLookupContext
31
- attr_reader :search_path
32
-
33
- def initialize(search_path)
34
- @search_path = search_path
35
- raise Errors::InvalidTemplate, 'Invalid search path' unless search_path
36
- end
37
-
38
- def find(partial_name)
39
- query = File.join(search_path, "{#{partial_name}}{.babl,}")
40
- path = Dir[query].first
41
- return unless path
42
-
43
- source = File.read(path)
44
- [path, source, self]
11
+ current_template = unscoped
12
+ construct_terminal { |ctx|
13
+ lookup_context = ctx[:lookup_context]
14
+ raise Errors::InvalidTemplate, 'Cannot use partial without lookup context' unless lookup_context
15
+ template, new_lookup_context = lookup_context.find(current_template, partial_name)
16
+ raise Errors::InvalidTemplate, "Cannot find partial '#{partial_name}'" unless template
17
+ template.precompile(Nodes::TerminalValue.instance, lookup_context: new_lookup_context)
18
+ }
45
19
  end
46
20
  end
47
21
  end
@@ -23,7 +23,7 @@ module Babl
23
23
  protected
24
24
 
25
25
  # Override TemplateBase#precompile to ensure that all pin dependencies are satisfied.
26
- def precompile
26
+ def precompile(*)
27
27
  super.tap do |node|
28
28
  raise Errors::InvalidTemplate, 'Unresolved pin' unless node.pinned_dependencies.empty?
29
29
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ module Babl
3
+ module Operators
4
+ module Using
5
+ module DSL
6
+ def using(*mods, &block)
7
+ extended_self =
8
+ if mods.empty?
9
+ self
10
+ else
11
+ ::Class.new(self.class) { mods.each { |mod| include mod } }.new(builder)
12
+ end
13
+
14
+ extended_self.source(&(block || -> { self }))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -51,7 +51,7 @@ module Babl
51
51
 
52
52
  # Return an array containing the navigation history
53
53
  def stack
54
- (parent ? parent.stack : []) + [key].compact
54
+ (parent ? parent.stack : Utils::Array::EMPTY) + [key].compact
55
55
  end
56
56
  end
57
57
  end
@@ -4,11 +4,11 @@ require 'babl/utils'
4
4
  module Babl
5
5
  module Schema
6
6
  class FixedArray < Utils::Value.new(:items)
7
- EMPTY = new([])
7
+ EMPTY = new(Utils::Array::EMPTY)
8
8
 
9
9
  def json
10
10
  if items.empty?
11
- { enum: [[]] }
11
+ { enum: [Utils::Array::EMPTY] }
12
12
  else
13
13
  { type: 'array', items: items.map(&:json), additionalItems: false }
14
14
  end
@@ -9,8 +9,8 @@ module Babl
9
9
  super(properties.to_set.freeze, additional)
10
10
  end
11
11
 
12
- EMPTY = new([], false)
13
- EMPTY_WITH_ADDITIONAL = new([], true)
12
+ EMPTY = new(Utils::Array::EMPTY, false)
13
+ EMPTY_WITH_ADDITIONAL = new(Utils::Array::EMPTY, true)
14
14
 
15
15
  class Property < Utils::Value.new(:name, :value, :required)
16
16
  def initialize(name, value, required)
@@ -24,6 +24,7 @@ module Babl
24
24
  include Operators::Pin::DSL
25
25
  include Operators::Source::DSL
26
26
  include Operators::Static::DSL
27
+ include Operators::Using::DSL
27
28
  include Operators::Switch::DSL
28
29
  include Operators::Typed::DSL
29
30
  include Operators::With::DSL
@@ -33,7 +33,7 @@ module Babl
33
33
  end
34
34
 
35
35
  def self.eval(dsl, &block)
36
- new(dsl, block.binding.receiver).instance_eval(&block)
36
+ new(dsl, block.binding.receiver).instance_exec(&block)
37
37
  end
38
38
 
39
39
  def initialize(receiver, fallback)
@@ -5,35 +5,44 @@ module Babl
5
5
  module Utils
6
6
  # Construct deeply immutable value objects
7
7
  # Similar to Struct, but:
8
- # - Properties are assumed deeply immutable (#hash is assumed constant)
8
+ # - Properties are assumed deeply immutable (#hash is assumed constant & store permanently)
9
9
  # - Constructor requires all arguments
10
10
  # - #== has the same meaning as #eql?
11
- class Value
11
+ # - The object is frozen
12
+ #
13
+ # Goals :
14
+ # - Create completely immutable value objects
15
+ # - Fast comparison between instances (using precomputed hash values)
16
+ # - Low overhead (relies on native Ruby Struct)
17
+ class Value < Struct
12
18
  def self.new(*fields)
13
- ::Class.new(::Struct.new(:_cached_hash, *fields)) do
14
- field_aliases = ::Array.new(fields.size) { |i| "v#{i}" }
15
- const_set(:FIELDS, fields.map(&:to_sym))
16
- class_eval <<-RUBY
17
- def initialize(#{field_aliases.join(',')})
18
- super(#{['nil', field_aliases].join(',')})
19
- hash
20
- freeze
21
- end
19
+ fields = fields.map(&:to_sym)
20
+ field_aliases = ::Array.new(fields.size) { |i| "v#{i}" }
22
21
 
23
- def hash
24
- self._cached_hash ||= super
25
- end
22
+ clazz = super(:_cached_hash, *fields)
23
+ clazz.const_set(:FIELDS, fields)
24
+ clazz.class_eval <<-RUBY
25
+ def initialize(#{field_aliases.join(',')})
26
+ super(#{['nil', field_aliases].join(',')})
27
+ hash
28
+ freeze
29
+ end
30
+ RUBY
26
31
 
27
- def ==(other)
28
- eql?(other)
29
- end
32
+ clazz
33
+ end
34
+
35
+ def hash
36
+ self._cached_hash ||= super
37
+ end
38
+
39
+ def ==(other)
40
+ eql?(other)
41
+ end
30
42
 
31
- def self.with(hash = Utils::Hash::EMPTY)
32
- raise ::ArgumentError unless ::Hash === hash && (hash.keys - FIELDS).empty?
33
- new(*FIELDS.map { |f| hash.fetch(f) })
34
- end
35
- RUBY
36
- end
43
+ def self.with(hash = Utils::Hash::EMPTY)
44
+ raise ::ArgumentError unless ::Hash === hash && (hash.keys - self::FIELDS).empty?
45
+ new(*self::FIELDS.map { |f| hash.fetch(f) })
37
46
  end
38
47
  end
39
48
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Babl
3
- VERSION = '0.4.0'
3
+ VERSION = '0.5.0'
4
4
  end
metadata CHANGED
@@ -1,113 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: babl-json
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frederic Terrazzoni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-03 00:00:00.000000000 Z
11
+ date: 2017-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pry
14
+ name: coveralls
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '0.8'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '0.8'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rspec
28
+ name: jbuilder
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3'
33
+ version: '2'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3'
40
+ version: '2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop
42
+ name: json-schema
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.50'
47
+ version: '2.8'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.50'
54
+ version: '2.8'
55
55
  - !ruby/object:Gem::Dependency
56
- name: json-schema
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.8'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2.8'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: coveralls
70
+ name: rabl
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.8'
75
+ version: '0.13'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.8'
82
+ version: '0.13'
83
83
  - !ruby/object:Gem::Dependency
84
- name: jbuilder
84
+ name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '2'
89
+ version: '3'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '2'
96
+ version: '3'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rabl
98
+ name: rubocop
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: '0.13'
103
+ version: '0.51'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: '0.13'
110
+ version: '0.51'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: multi_json
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -175,6 +175,7 @@ files:
175
175
  - lib/babl/operators/static.rb
176
176
  - lib/babl/operators/switch.rb
177
177
  - lib/babl/operators/typed.rb
178
+ - lib/babl/operators/using.rb
178
179
  - lib/babl/operators/with.rb
179
180
  - lib/babl/railtie.rb
180
181
  - lib/babl/rendering.rb