nm 0.5.4 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA512:
3
- metadata.gz: 31eb095487432e3c6b60cbb023624cce4b902371692cea5312d52d0bcd6a57d98ad2ca4171110f66e2bd4028fdf449c6eee00442d769fc260abd7ab76f8f15cb
4
- data.tar.gz: 43ec476c3b63ec379f30edc5b75989f56843eab905e90a0a2b4422022102f2cbd5931cc3b09bbe7f3aeeacf26280a130dc6a8875af9cfd83b4ed903dcfeda5c8
5
- SHA1:
6
- metadata.gz: 79b11e40bec9d04eed9c4590c06ee9acc2a74ba9
7
- data.tar.gz: 2a6984f912b917b14c9ab9ba5a524c310f8f7648
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc0aecffbcfc3c14162b53a334c10172f41946f3e3d9a524e98b223dcb5a64bb
4
+ data.tar.gz: 25559fce124e9cfb71d46e9eda6d79ab39585f94fff0bdfee7b591df540616f9
5
+ SHA512:
6
+ metadata.gz: 862701c2457f04f1479c124a0ba2d1d5c464b819baf035ba0779eb21f8253e2b0c133b5700268a20416b26a7e3629fc08b2187887f23f55c2b8cca78188b1c5d
7
+ data.tar.gz: acadad3b3ee918cf36e02975718cb0d7770d5b9f7b190451172c009ca90ad0b276efe970a697ecc3190d6f89bc1c91bc62a08049a09f6c40aaa59b3e36e248ae
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .rbx/
5
5
  .bundle
6
6
  .config
7
+ .rubocop-*-yml
7
8
  .yardoc
8
9
  Gemfile.lock
9
10
  InstalledFiles
data/.l.yml ADDED
@@ -0,0 +1,9 @@
1
+ # https://github.com/redding/l.rb
2
+
3
+ linters:
4
+ - name: "Rubocop"
5
+ cmd: "bundle exec rubocop"
6
+ autocorrect_cmd: "bundle exec rubocop -A"
7
+ extensions:
8
+ - ".rb"
9
+ cli_abbrev: "u"
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ inherit_gem:
2
+ much-style-guide:
3
+ - "lib/much-style-guide/rubocop.yml"
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7.3
data/.t.yml ADDED
@@ -0,0 +1,6 @@
1
+ # https://github.com/redding/t.rb
2
+
3
+ default_cmd: "bundle exec assert"
4
+ test_dir: "test"
5
+ test_file_suffixes:
6
+ - "_tests.rb"
data/Gemfile CHANGED
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
5
+ ruby "~> 2.5"
6
+
3
7
  gemspec
4
8
 
5
- gem 'pry', "~> 0.9.0"
9
+ gem "pry"
6
10
  gem "whysoslow"
7
- gem 'rabl'
11
+ gem "rabl"
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Nm
2
2
 
3
- Data templating system. Named for its two main markup methods: "node" and "map". Designed to template data objects for JSON/BSON/whatever/etc serialization.
3
+ Node-Map: a data templating DSL. Named for its two main markup methods: "node" and "map". Designed to template data objects for JSON/BSON/whatever/etc serialization.
4
4
 
5
5
  ## Usage
6
6
 
@@ -9,15 +9,15 @@ Template:
9
9
  ```ruby
10
10
  # in /path/to/views/slideshow.json.nm
11
11
 
12
- node 'slideshow' do
13
- node 'start_slide', start_slide
14
- node 'slides' do
12
+ node "slideshow" do
13
+ node "start_slide", start_slide
14
+ node "slides" do
15
15
  map slides do |slide|
16
- node 'id', slide.id
17
- node 'title', slide.title
18
- node 'image', slide.image_url
19
- node 'thumb', slide.thumb_url
20
- node 'url', slide.url
16
+ node "id", slide.id
17
+ node "title", slide.title
18
+ node "image", slide.image_url
19
+ node "thumb", slide.thumb_url
20
+ node "url", slide.url
21
21
  end
22
22
  end
23
23
  end
@@ -26,12 +26,15 @@ end
26
26
  Render:
27
27
 
28
28
  ```ruby
29
- require 'nm'
30
- source = Nm::Source.new('/path/to/views')
31
- source.render('slideshow.json', {
32
- :start_slide => 1,
33
- :slides => [ ... ] #=> list of slide objects 1, 2 and 3
34
- })
29
+ require "nm"
30
+ source = Nm::Source.new("/path/to/views")
31
+ source.render(
32
+ "slideshow.json",
33
+ locals: {
34
+ start_slide: 1,
35
+ slides: [ ... ] #=> list of slide objects 1, 2 and 3
36
+ }
37
+ )
35
38
  ```
36
39
 
37
40
  Output:
@@ -67,10 +70,10 @@ Output:
67
70
 
68
71
  ### Cache Templates
69
72
 
70
- By default the source doesn't cache template files. You can configure it to cache templates using the `:cache` option:
73
+ By default the source doesn't cache template files. You can configure it to cache templates using the `:cache` option:
71
74
 
72
75
  ```ruby
73
- source = Nm::Source.new('/path/to/views', :cache => true)
76
+ source = Nm::Source.new("/path/to/views", cache: true)
74
77
  ```
75
78
 
76
79
  ### Default locals
@@ -78,14 +81,13 @@ source = Nm::Source.new('/path/to/views', :cache => true)
78
81
  You can specify a set of default locals to use on all renders for a source using the `:locals` option:
79
82
 
80
83
  ```ruby
81
- source = Nm::Source.new('/path/to/views', :locals => {
82
- 'something' => 'value'
83
- })
84
+ source =
85
+ Nm::Source.new("/path/to/views", locals: { "something" => "value" })
84
86
  ```
85
87
 
86
88
  ### Render Format
87
89
 
88
- Rendering templates returns a data object (`::Hash` or `::Array`). To serialize, bring in your favorite JSON/BSON/whatever serializer and pass the rendered object to it.
90
+ Rendering templates returns a data object (`::Hash` or `::Array`). To serialize, bring in your favorite JSON/BSON/whatever serializer and pass the rendered object to it.
89
91
 
90
92
  ### Markup Methods
91
93
 
@@ -96,18 +98,18 @@ There are two main markup methods:
96
98
 
97
99
  ### Default render value
98
100
 
99
- Nm templates render an empty object (ie `::Hash.new`) if no source is given or no markup methods are called in the template source. The idea is that the templates should always return *something* and avoid `nil` values as much as possible.
101
+ Nm templates render an empty object (ie `::Hash.new`) if no source is given or no markup methods are called in the template source. The idea is that the templates should always return *something* and avoid `nil` values as much as possible.
100
102
 
101
103
  This is also more consistent with rendering mapped lists vs reduced objects. Say your are mapping a list of objects in your template (using the `map` markup method):
102
104
 
103
105
  ```ruby
104
106
  map incoming_list do |item|
105
- node 'name', item.name
106
- node 'value', item.value
107
+ node "name", item.name
108
+ node "value", item.value
107
109
  end
108
110
  ```
109
111
 
110
- If there are no items in the incoming list, the template render produces an empty list. Now say you are reducing an incoming list to a single object:
112
+ If there are no items in the incoming list, the template render produces an empty list. Now say you are reducing an incoming list to a single object:
111
113
 
112
114
  ```ruby
113
115
  incoming_list.each do |item|
@@ -126,47 +128,47 @@ If there are no items in the incoming list, no markup methods are called, but th
126
128
  ```ruby
127
129
  # in /path/to/views/slideshow.json.nm
128
130
 
129
- node 'slideshow' do
130
- node 'start_slide', start_slide
131
- node 'slides' do
131
+ node "slideshow" do
132
+ node "start_slide", start_slide
133
+ node "slides" do
132
134
  map slides do |slide|
133
- partial '_slide.json', :slide => slide
135
+ partial "_slide.json", slide: slide
134
136
  end
135
137
  end
136
138
  end
137
139
 
138
140
  # in /path/to/views/_slide.json.nm
139
141
 
140
- node 'id', slide.id
141
- node 'title', slide.title
142
- node 'image', slide.image_url
143
- node 'thumb', slide.thumb_url
144
- node 'url', slide.url
142
+ node "id", slide.id
143
+ node "title", slide.title
144
+ node "image", slide.image_url
145
+ node "thumb", slide.thumb_url
146
+ node "url", slide.url
145
147
  ```
146
148
 
147
149
  This will render the same output as above.
148
150
 
149
151
  ### Markup Aliases
150
152
 
151
- If you find you need to use a local named `node` or `map`, the markup methods are aliased as `n`, `_node`, `m`, and `_map` respectively. Any combination of aliases is valid:
153
+ If you find you need to use a local named `node` or `map`, the markup methods are aliased as `n`, `_node`, `m`, and `_map` respectively. Any combination of aliases is valid:
152
154
 
153
155
  ```ruby
154
- node 'slideshow' do
155
- n 'start_slide', start_slide
156
- _node 'slides' do
156
+ node "slideshow" do
157
+ n "start_slide", start_slide
158
+ _node "slides" do
157
159
  _map slides do |slide|
158
- _node 'id', slide.id
159
- node 'title', slide.title
160
- _node 'image', slide.image_url
161
- node 'thumb', slide.thumb_url
162
- _node 'url', slide.url
160
+ _node "id", slide.id
161
+ node "title", slide.title
162
+ _node "image", slide.image_url
163
+ node "thumb", slide.thumb_url
164
+ _node "url", slide.url
163
165
  end
164
166
  m other_slides do |slide|
165
- node 'id', slide.id
166
- _node 'title', slide.title
167
- node 'image', slide.image_url
168
- _node 'thumb', slide.thumb_url
169
- node 'url', slide.url
167
+ node "id", slide.id
168
+ _node "title", slide.title
169
+ node "image", slide.image_url
170
+ _node "thumb", slide.thumb_url
171
+ node "url", slide.url
170
172
  end
171
173
  end
172
174
  end
@@ -174,9 +176,9 @@ end
174
176
 
175
177
  ## Installation
176
178
 
177
- Add this line to your application's Gemfile:
179
+ Add this line to your application"s Gemfile:
178
180
 
179
- gem 'nm'
181
+ gem "nm"
180
182
 
181
183
  And then execute:
182
184
 
@@ -190,6 +192,6 @@ Or install it yourself as:
190
192
 
191
193
  1. Fork it
192
194
  2. Create your feature branch (`git checkout -b my-new-feature`)
193
- 3. Commit your changes (`git commit -am 'Added some feature'`)
195
+ 3. Commit your changes (`git commit -am "Added some feature"`)
194
196
  4. Push to the branch (`git push origin my-new-feature`)
195
197
  5. Create new Pull Request
data/lib/nm.rb CHANGED
@@ -1,4 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "nm/version"
2
- require 'nm/source'
4
+ require "nm/source"
5
+
6
+ module Nm
7
+ def self.default_context_class
8
+ @default_context_class ||= Class.new
9
+ end
3
10
 
4
- module Nm; end
11
+ def self.default_context
12
+ default_context_class.new
13
+ end
14
+ end
data/lib/nm/context.rb ADDED
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nm/template_behaviors"
4
+
5
+ module Nm; end
6
+
7
+ class Nm::Context
8
+ def initialize(context, source:, locals:)
9
+ @context = context
10
+ @source = source
11
+
12
+ # apply template behaviors to the meta-context
13
+ metacontext = class << context; self; end
14
+ metacontext.class_eval do
15
+ include Nm::TemplateBehaviors
16
+
17
+ locals.each do |key, value|
18
+ define_method(key){ value }
19
+ end
20
+ end
21
+ @context.__nm_context__ = self
22
+ end
23
+
24
+ def render(template_name, locals = {})
25
+ source_file_path = @source.file_path!(template_name)
26
+ render_content(
27
+ @source.data(source_file_path),
28
+ locals: locals,
29
+ file_path: source_file_path,
30
+ )
31
+ end
32
+
33
+ alias_method :partial, :render
34
+
35
+ def render_content(content, locals: {}, file_path: nil)
36
+ @context.__nm_push_render__(locals.to_h)
37
+ @context.instance_eval(
38
+ "#{locals_code_for(locals)};#{content}",
39
+ file_path,
40
+ 1,
41
+ )
42
+ @context.__nm_data__.tap{ |_data| @context.__nm_pop_render__ }
43
+ end
44
+
45
+ private
46
+
47
+ def locals_code_for(locals)
48
+ # Assign for the same variable is to suppress unused variable warning.
49
+ locals.reduce(+"") do |code, (key, _value)|
50
+ code <<
51
+ "#{key} = @__nm_locals__[:#{key}] || @__nm_locals__[\"#{key}\"]; "\
52
+ "#{key} = #{key};"
53
+ end
54
+ end
55
+ end
data/lib/nm/ext.rb CHANGED
@@ -1,31 +1,24 @@
1
- module Nm
2
-
3
- class InvalidError < RuntimeError; end
1
+ # frozen_string_literal: true
4
2
 
3
+ module Nm
4
+ class InvalidError < RuntimeError
5
+ end
5
6
  end
6
7
 
7
8
  class ::Hash
8
-
9
- def __nm_add_call_data__(call_name, data)
10
- if data.is_a?(::Array)
11
- raise Nm::InvalidError, "invalid `#{call_name}` call"
12
- end
13
- self.merge(data || {})
9
+ def __nm_add_partial_data__(data)
10
+ raise Nm::InvalidError, "invalid partial call" if data.is_a?(::Array)
11
+ merge(data || {})
14
12
  end
15
-
16
13
  end
17
14
 
18
15
  class ::Array
19
-
20
- def __nm_add_call_data__(call_name, data)
21
- if data.is_a?(::Hash)
22
- raise Nm::InvalidError, "invalid `#{call_name}` call"
23
- end
24
- self.concat(data || [])
16
+ def __nm_add_partial_data__(data)
17
+ raise Nm::InvalidError, "invalid partial call" if data.is_a?(::Hash)
18
+ concat(data || [])
25
19
  end
26
-
27
20
  end
28
21
 
29
- def nil.__nm_add_call_data__(call_name, data)
22
+ def nil.__nm_add_partial_data__(data)
30
23
  data
31
24
  end
data/lib/nm/render.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nm; end
4
+
5
+ class Nm::Render
6
+ attr_reader :dstack, :locals
7
+
8
+ def initialize(dstack:, locals:)
9
+ @dstack = dstack
10
+ @locals = locals
11
+ end
12
+ end
data/lib/nm/source.rb CHANGED
@@ -1,70 +1,83 @@
1
- require 'pathname'
2
- require 'nm/template'
1
+ # frozen_string_literal: true
3
2
 
4
- module Nm
3
+ require "pathname"
4
+ require "nm/context"
5
5
 
6
- class Source
6
+ module Nm; end
7
7
 
8
- attr_reader :root, :ext, :cache, :template_class
8
+ class Nm::Source
9
+ attr_reader :root, :extension, :cache, :locals
9
10
 
10
- def initialize(root, opts = nil)
11
- opts ||= {}
12
- @root = Pathname.new(root.to_s)
13
- @ext = opts[:ext] ? ".#{opts[:ext]}" : nil
14
- @cache = opts[:cache] ? Hash.new : NullCache.new
15
-
16
- @template_class = Class.new(Template) do
17
- (opts[:locals] || {}).each{ |key, value| define_method(key){ value } }
18
- end
19
- end
11
+ def initialize(root, extension: nil, cache: false, locals: {})
12
+ @root = Pathname.new(root.to_s)
13
+ @extension = extension ? ".#{extension}" : nil
14
+ @cache = cache ? {} : NullCache.new
15
+ @locals = locals
16
+ end
20
17
 
21
- def inspect
22
- "#<#{self.class}:#{'0x0%x' % (object_id << 1)} @root=#{@root.inspect}>"
23
- end
18
+ def inspect
19
+ "#<#{self.class}:#{"0x0%x" % (object_id << 1)} @root=#{@root.inspect}>"
20
+ end
24
21
 
25
- def data(file_path)
26
- @cache[file_path] ||= begin
22
+ def data(file_path)
23
+ @cache[file_path] ||=
24
+ begin
27
25
  File.send(File.respond_to?(:binread) ? :binread : :read, file_path)
28
26
  end
29
- end
27
+ end
30
28
 
31
- def render(template_name, locals = nil)
32
- if (filename = source_file_path(template_name)).nil?
33
- template_desc = "a template file named #{template_name.inspect}"
34
- if !@ext.nil?
35
- template_desc += " that ends in #{@ext.inspect}"
36
- end
37
- raise ArgumentError, "#{template_desc} does not exist"
38
- end
39
- @template_class.new(self, filename, locals || {}).__data__
29
+ def render(template_name, context: Nm.default_context, locals: {})
30
+ Nm::Context
31
+ .new(context, source: self, locals: @locals)
32
+ .render(template_name, locals)
33
+ end
34
+
35
+ def file_path!(template_name)
36
+ if (path = file_path(template_name)).nil?
37
+ message = "a template file named #{template_name.inspect}"
38
+ message += " that ends in #{@extension.inspect}" unless @extension.nil?
39
+ message += " does not exist"
40
+ raise ArgumentError, message
40
41
  end
42
+ path
43
+ end
41
44
 
42
- alias_method :partial, :render
45
+ def ==(other)
46
+ return super unless other.is_a?(self.class)
43
47
 
44
- private
48
+ root == other.root &&
49
+ extension == other.extension &&
50
+ cache == other.cache &&
51
+ locals == other.locals
52
+ end
45
53
 
46
- def source_file_path(name)
47
- Dir.glob(self.root.join(source_file_glob_string(name))).first
48
- end
54
+ private
49
55
 
50
- def source_file_glob_string(name)
51
- !@ext.nil? && name.end_with?(@ext) ? name : "#{name}*#{@ext}"
52
- end
56
+ def file_path(template_name)
57
+ Dir.glob(root.join(file_glob_string(template_name))).first
58
+ end
53
59
 
54
- class NullCache
55
- def [](template_name); end
56
- def []=(template_name, value); end
57
- def keys; []; end
60
+ def file_glob_string(template_name)
61
+ if !@extension.nil? && template_name.end_with?(@extension)
62
+ template_name
63
+ else
64
+ "#{template_name}*#{@extension}"
58
65
  end
59
-
60
66
  end
61
67
 
62
- class DefaultSource < Source
68
+ class NullCache
69
+ def [](template_name)
70
+ end
63
71
 
64
- def initialize
65
- super('/')
72
+ def []=(template_name, value)
66
73
  end
67
74
 
68
- end
75
+ def keys
76
+ []
77
+ end
69
78
 
79
+ def ==(other)
80
+ other.is_a?(self.class)
81
+ end
82
+ end
70
83
  end