trmnl_preview 0.1.1 → 0.2.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
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78557015c4d1a1169007c6981f6eb6c7772f8e18c1eaa6c83a1e69fd70b6dc6d
4
- data.tar.gz: e3b9e6974fd2e345fe76c6e3629d0ecb62a0d08014fe0b86ed9dc9fc4a71101d
3
+ metadata.gz: 1d511eb133cb9ee46d6d343f9ed1bef3c11c3e581ae193310d2d6f50d900c4a4
4
+ data.tar.gz: ed60374a348285201cd0f49396cd2995c5b87301866aec421cd3c21131c6fe98
5
5
  SHA512:
6
- metadata.gz: a7ed5310ffd9ad2192f011cbeb74f68618f3b2d51e2030fa377309002a10686320d51f609163393c4d54117f65f04d4bc1289bb263c9ff9db22850254a507aa0
7
- data.tar.gz: d84547ebb22977c2fc22617d5fd1c966fcd7b097350912c2cb7833b325e9538e2e6239bcc51592ce2ea84b1672245c62922abd426e1bf29e76eceba613be1726
6
+ metadata.gz: 063e9d97310fdc7c20609e83573006e18df1a081455d8d60d1694fbbc21a43c82c99290395b54e0b815d498884ebf69f984f4d153c3d50131c34b090c6752749
7
+ data.tar.gz: c44116ac437e7636c403481d6b5b773a5f0d0ccf425b6e2181bc987d272056bef407ce43919d967fa86da80b96b032534d423b43dd60ef71d56c96ec22e4b9ab
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - Add "commands" concept to `trmnlp` executable
6
+ - `trmnlp serve` improvements
7
+ - Add argument for plugin directory
8
+ - Add options `-b` and `-p` for host bind and port, respectively
9
+ - Add Dockerfile
10
+
11
+ ## 0.1.2
12
+
13
+ - Initial working release
data/README.md CHANGED
@@ -1,32 +1,53 @@
1
1
  # trmnl_preview
2
2
 
3
- A little self-hosted web server to ease the development and sharing of [TRMNL](https://usetrmnl.com/) plugins.
3
+ A basic self-hosted web server to ease the development and sharing of [TRMNL](https://usetrmnl.com/) plugins.
4
4
 
5
- ## Prerequisites
5
+ [Liquid](https://shopify.github.io/liquid/) templates are rendered locally as HTML, leveraging the [TRMNL Design System](https://usetrmnl.com/framework). This server does NOT generate a rendered BMP file. Hence, this is just a _preview_ of the final rendered dashboard.
6
6
 
7
- - Ruby 3.x
7
+ ![Screenshot](docs/preview.png)
8
8
 
9
- ## Getting Started
9
+ ## Creating a Plugin
10
10
 
11
- Clone a fork of https://github.com/schrockwell/trmnl-hello.
11
+ This is the structure of a plugin repository.
12
12
 
13
- Run `bundle`.
13
+ ```
14
+ views/
15
+ full.liquid
16
+ half_horizontal.liquid
17
+ half_vertical.liquid
18
+ quadrant.liquid
19
+ config.toml
20
+ ```
14
21
 
15
- Modify `config.toml` (see below for reference).
22
+ See [config.example.toml](config.example.toml) for an example config.
16
23
 
17
- Modify the four view templates in `views/*.liquid`
24
+ The [trmnl-hello](https://github.com/schrockwell/trmnl-hello) repository is provided as a jumping-off point for creating new plugins. Simply fork the repo, clone it, and start hacking.
18
25
 
19
- Run `trmnlp` to start the local web server.
26
+ ## Running the Server (Docker)
20
27
 
21
- Browse it locally at http://127.0.0.1:4567/
28
+ ```sh
29
+ docker run \
30
+ -p 4567:4567 \
31
+ -v /path/to/plugin/on/host:/plugin \
32
+ schrockwell/trmnlp
33
+ ```
34
+
35
+ ## Running the Server (Local Ruby)
36
+
37
+ Ruby 3.x is required. In the plugin repository:
38
+
39
+ ```sh
40
+ bundle add trmnl_preview # Creates Gemfile and Gemfile.lock
41
+ trmnlp serve # Starts the server
42
+ ```
22
43
 
23
44
  ## Usage Notes
24
45
 
25
46
  Simply refresh the page to re-render.
26
47
 
27
- When the polling strategy is "polling", the specified URL will be fetched once when the server starts.
48
+ When the strategy is "polling", the specified URL will be fetched once, when the server starts.
28
49
 
29
- When the polling strategy is "webhook", you can POST payloads to the `/webhook` endpoint. They are saved to `tmp/data.json` for future renders.
50
+ When the strategy is "webhook", payloads can be POSTed to the `/webhook` endpoint. They are saved to `tmp/data.json` for future renders.
30
51
 
31
52
  ## `config.toml` Reference
32
53
 
@@ -0,0 +1,11 @@
1
+ # strategy = "polling" ==> the data will be fetched once, at server start-up
2
+ # strategy = "webhook" ==> POST new data to /webhook
3
+ strategy = "polling"
4
+
5
+ # Poll URL (required for polling strategy)
6
+ url = "https://example.com/data.json"
7
+
8
+ # Polling headers (optional, for polling strategy)
9
+ [polling_headers]
10
+ authorization = "bearer 123"
11
+ content-type = "application/json"
data/docs/preview.png ADDED
Binary file
data/exe/trmnlp CHANGED
@@ -2,4 +2,9 @@
2
2
 
3
3
  require_relative '../lib/trmnl_preview'
4
4
 
5
- TRMNLPreview::App.run!
5
+ case ARGV[0].to_s.downcase
6
+ when 'serve'
7
+ require_relative '../lib/trmnl_preview/cmd/serve'
8
+ else
9
+ require_relative '../lib/trmnl_preview/cmd/usage'
10
+ end
@@ -5,93 +5,94 @@ require 'sinatra'
5
5
  require 'sinatra/base'
6
6
  require 'toml-rb'
7
7
 
8
- require_relative '../trmnl_preview'
9
8
  require_relative 'liquid_filters'
10
9
 
11
10
  class TRMNLPreview::App < Sinatra::Base
12
- set :views, File.join(File.dirname(__FILE__), '..', '..', 'views')
13
-
14
11
  # Constants
15
12
  VIEWS = %w{full half_horizontal half_vertical quadrant}
16
- CONFIG_PATH = File.join(Dir.pwd, 'config.toml')
17
- USER_VIEWS_DIR = File.join(Dir.pwd, 'views')
18
- TEMP_DIR = File.join(Dir.pwd, 'tmp')
19
- DATA_JSON_PATH = File.join(TEMP_DIR, 'data.json')
20
-
21
- unless File.exist?(CONFIG_PATH)
22
- puts "No config.toml found in #{Dir.pwd}"
23
- exit 1
24
- end
25
-
26
- unless Dir.exist?(USER_VIEWS_DIR)
27
- puts "No views found at #{USER_VIEWS_DIR}"
28
- exit 1
29
- end
30
13
 
31
- FileUtils.mkdir_p(TEMP_DIR)
14
+ # Sinatra settings
15
+ set :views, File.join(File.dirname(__FILE__), '..', '..', 'views')
16
+
17
+ def initialize(*args)
18
+ super
32
19
 
33
- config = TOML.load_file(CONFIG_PATH)
34
- strategy = config['strategy']
20
+ @config_path = File.join(settings.user_dir, 'config.toml')
21
+ @user_views_dir = File.join(settings.user_dir, 'views')
22
+ @temp_dir = File.join(settings.user_dir, 'tmp')
23
+ @data_json_path = File.join(@temp_dir, 'data.json')
35
24
 
36
- unless ['polling', 'webhook'].include?(strategy)
37
- puts "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
38
- exit 1
39
- end
25
+ unless File.exist?(@config_path)
26
+ puts "No config.toml found in #{settings.user_dir}"
27
+ exit 1
28
+ end
29
+
30
+ unless Dir.exist?(@user_views_dir)
31
+ puts "No views found at #{@user_views_dir}"
32
+ exit 1
33
+ end
40
34
 
41
- url = config['url']
42
- polling_headers = config['polling_headers'] || {}
35
+ FileUtils.mkdir_p(@temp_dir)
43
36
 
44
- if strategy == 'polling'
45
- if url.nil?
46
- puts "URL is required for polling strategy"
37
+ @config = TomlRB.load_file(@config_path)
38
+ strategy = @config['strategy']
39
+
40
+ unless ['polling', 'webhook'].include?(strategy)
41
+ puts "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
47
42
  exit 1
48
43
  end
49
-
50
- print "Fetching #{url}... "
51
- payload = URI.open(url, polling_headers).read
52
- File.write(DATA_JSON_PATH, payload)
53
- puts "got #{payload.size} bytes"
44
+
45
+ url = @config['url']
46
+ polling_headers = @config['polling_headers'] || {}
47
+
48
+ if strategy == 'polling'
49
+ if url.nil?
50
+ puts "URL is required for polling strategy"
51
+ exit 1
52
+ end
53
+
54
+ print "Fetching #{url}... "
55
+ payload = URI.open(url, polling_headers).read
56
+ File.write(@data_json_path, payload)
57
+ puts "got #{payload.size} bytes"
58
+ end
59
+
60
+ @liquid_environment = Liquid::Environment.build do |env|
61
+ env.register_filter(TRMNLPreview::LiquidFilters)
62
+ end
54
63
  end
55
64
 
56
- environment = Liquid::Environment.build do |env|
57
- env.register_filter(TRMNLPreview::LiquidFilters)
65
+ post '/webhook' do
66
+ body = request.body.read
67
+ File.write(@data_json_path, body)
68
+ "OK"
58
69
  end
59
-
70
+
60
71
  get '/' do
61
72
  redirect '/full'
62
73
  end
63
-
64
- if config['strategy'] == 'webhook'
65
- post '/webhook' do
66
- body = request.body.read
67
- File.write(DATA_JSON_PATH, body)
68
- "OK"
74
+
75
+ VIEWS.each do |view|
76
+ get "/#{view}" do
77
+ @view = view
78
+ erb :index
69
79
  end
70
80
 
71
- puts "Listening for POSTs to /webhook"
72
- end
73
-
74
- VIEWS.each do |view|
75
81
  get "/render/#{view}" do
76
- path = File.join(USER_VIEWS_DIR, "#{view}.liquid")
82
+ path = File.join(@user_views_dir, "#{view}.liquid")
77
83
  unless File.exist?(path)
78
84
  halt 404, "Plugin template not found: views/#{view}.liquid"
79
85
  end
80
86
 
81
- user_template = Liquid::Template.parse(File.read(path), environment: environment)
87
+ user_template = Liquid::Template.parse(File.read(path), environment: @liquid_environment)
82
88
 
83
89
  @view = view
84
90
  erb :render_view do
85
- data = JSON.parse(File.read(DATA_JSON_PATH))
91
+ data = JSON.parse(File.read(@data_json_path))
86
92
  data = { data: data } if data.is_a?(Array) # per TRMNL docs, bare array is wrapped in 'data' key
87
93
 
88
94
  user_template.render(data)
89
95
  end
90
96
  end
91
-
92
- get "/#{view}" do
93
- @view = view
94
- erb :index
95
- end
96
97
  end
97
98
  end
@@ -0,0 +1,31 @@
1
+ require 'optionparser'
2
+
3
+ options = {
4
+ bind: '127.0.0.1',
5
+ port: 4567
6
+ }
7
+
8
+ # Parse options BEFORE requiring the Sinatra app, since it has its own option-parsing behavior.
9
+ # This will remove the option items from ARGV, which is what we want.
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: trmnlp serve [directory] [options]"
12
+
13
+ opts.on("-b", "--bind [HOST]", "Bind to host address (default: 127.0.0.1)") do |host|
14
+ options[:bind] = host
15
+ end
16
+
17
+ opts.on("-p", "--port [PORT]", "Use port (default: 4567)") do |port|
18
+ options[:port] = port
19
+ end
20
+ end.parse!
21
+
22
+ # Must come AFTER parsing options
23
+ require_relative '../app'
24
+
25
+ # Now we can configure things
26
+ TRMNLPreview::App.set(:user_dir, ARGV[1] || Dir.pwd)
27
+ TRMNLPreview::App.set(:bind, options[:bind])
28
+ TRMNLPreview::App.set(:port, options[:port])
29
+
30
+ # Finally, start the app!
31
+ TRMNLPreview::App.run!
@@ -0,0 +1,9 @@
1
+ puts <<-USAGE
2
+ Usage:
3
+
4
+ trmnlp [command] [options]
5
+
6
+ Commands (-h for command-specific help):
7
+
8
+ serve Start the TRMNL Preview server
9
+ USAGE
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TRMNLPreview
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/trmnl_preview.rb CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  module TRMNLPreview; end
4
4
 
5
- require_relative "trmnl_preview/app"
6
- require_relative "trmnl_preview/liquid_filters"
5
+ # require_relative "trmnl_preview/app"
7
6
  require_relative "trmnl_preview/version"
8
7
 
9
8
  module TRMNLPreview
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.files = Dir.chdir(__dir__) do
25
25
  `git ls-files -z`.split("\x0").reject do |f|
26
26
  (File.expand_path(f) == __FILE__) ||
27
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
27
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile Dockerfile])
28
28
  end
29
29
  end
30
30
  spec.bindir = "exe"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trmnl_preview
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rockwell Schrock
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-01-05 00:00:00.000000000 Z
10
+ date: 2025-01-06 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: sinatra
@@ -88,15 +87,19 @@ executables:
88
87
  extensions: []
89
88
  extra_rdoc_files: []
90
89
  files:
90
+ - ".ruby-version"
91
+ - CHANGELOG.md
91
92
  - LICENSE.txt
92
93
  - README.md
93
- - Rakefile
94
+ - config.example.toml
95
+ - docs/preview.png
94
96
  - exe/trmnlp
95
97
  - lib/trmnl_preview.rb
96
98
  - lib/trmnl_preview/app.rb
99
+ - lib/trmnl_preview/cmd/serve.rb
100
+ - lib/trmnl_preview/cmd/usage.rb
97
101
  - lib/trmnl_preview/liquid_filters.rb
98
102
  - lib/trmnl_preview/version.rb
99
- - sig/trmnl_preview.rbs
100
103
  - trmnl_preview.gemspec
101
104
  - views/index.erb
102
105
  - views/render_view.erb
@@ -107,7 +110,6 @@ metadata:
107
110
  allowed_push_host: https://rubygems.org
108
111
  homepage_uri: https://github.com/schrockwell/trmnl_preview
109
112
  source_code_uri: https://github.com/schrockwell/trmnl_preview
110
- post_install_message:
111
113
  rdoc_options: []
112
114
  require_paths:
113
115
  - lib
@@ -122,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  - !ruby/object:Gem::Version
123
125
  version: '0'
124
126
  requirements: []
125
- rubygems_version: 3.4.10
126
- signing_key:
127
+ rubygems_version: 3.6.2
127
128
  specification_version: 4
128
129
  summary: Local web server to preview TRMNL plugins
129
130
  test_files: []
data/Rakefile DELETED
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- task default: %i[]
@@ -1,4 +0,0 @@
1
- module TRMNLPreview
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end