nm 0.5.4 → 0.6.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
- ---
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