mx-rabl-extend-compiler 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8e399d0228a70571515fabbe1dee74b45fdab8a64ca0ffa4f6f589199e61b099
4
+ data.tar.gz: 875001bde915ba85e0a2078372ff7d58c294cb90328e1e0ded7d2411f08828f3
5
+ SHA512:
6
+ metadata.gz: a156d17f5530b7eb9265ec11dd8b73274851667e9385f030c37b5b91101338ce9cb4e4c0402ba49d62ebf0b7f989259f09d8eb38d44485069cb640f56456e7f9
7
+ data.tar.gz: e3cb0e7c4833bf9c05c39d4c3542a4defbd84e14938ed7536dc542593c3c3bb6fb275c9c363229e873ed2c20e312f41107b2990649308838997a7ef9985a1697
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .ruby*
2
+ *.lock
3
+ /.bundle/
4
+ /.yardoc
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.16.2
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in rabl-extend-compiler.gemspec
8
+ gemspec
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # MX::Rabl::Extend::Compiler
2
+ ## Installation
3
+
4
+ Add this line to your application's Gemfile:
5
+
6
+ ```ruby
7
+ gem 'mx-rabl-extend-compiler'
8
+ ```
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install mx-rabl-extend-compiler
17
+
18
+ ## Usage
19
+
20
+ When using [Rabl](https://github.com/nesquena/rabl) there is a DSL method called `extend` which embeds another template in the current template and serves as a
21
+ mechanism to be DRY and maintain object definitions in a single place.
22
+
23
+ One signficant downside of such a pattern is that the template rendering for large number of collections can be significantly lower (we have measured from 10-25% slower on collections up to 1000 objects)
24
+
25
+ In order to help facilitate still using Rabl (it's a great library) we decided to write a few rake tasks that allow us the benefits of then extension system
26
+ without the drawbacks. Attempting to emulate something like what is mentioned in this [issue](https://github.com/nesquena/rabl/issues/500) and running rake tasks to verify or compile the extensions before moving to production.
27
+
28
+ This gem merely outlines the components and they may be used as you see fit at varying times in your infrastructure (we run the verification rake task in our CI pipeline)
29
+
30
+ The rake tasks are:
31
+
32
+ `rake rabl:extend:compiler:all` - Runs all steps.
33
+
34
+ `rake rabl:extend:compiler:reset` - Will reset all signatures from the extended files.
35
+
36
+ `rake rabl:extend:compiler:compile` - Will compile and sign each use of `extend` in the code base.
37
+
38
+ `rake rabl:extend:compiler:verify` - Will `exit(1)` if the extensions are not compiled or the signatures do not match on the compiled extensions.
39
+
40
+
41
+ The "signatures" encompass the attributes or files from an extension and are merely a SHA256 digest of the file contents during the compilation step and creates an easy and fast mechanism to determine if the extended files have been altered without updating the extensions.
42
+
43
+ The rake tasks above will create the signature and file below when extending another file (check spec directory for more examples).
44
+
45
+ ```ruby
46
+ # rabl-extend-compiler extends "extend" => 9ebbe5e06dc6163365ca3d550b757464f0cdfe4b4227a7d52d4e6c1cc11af484
47
+ #
48
+ # This file segment is generated by rabl-extend-compiler rake task
49
+ # and should not be edited. To edit the generated extension
50
+ # edit the file at: ["extend"]
51
+ attributes :id, :derp, :body
52
+ # 9ebbe5e06dc6163365ca3d550b757464f0cdfe4b4227a7d52d4e6c1cc11af484
53
+ ```
54
+
55
+ ## Development
56
+
57
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
58
+
59
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
60
+
61
+ ## Contributing
62
+
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mxenabled/mx-rabl-extend-compiler.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'mx/rabl/extend/compiler'
6
+
7
+ desc 'Run specs'
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ desc 'Run RuboCop'
11
+ task :rubocop do
12
+ RuboCop::RakeTask.new
13
+ end
14
+
15
+ task default: %i[rubocop spec]
16
+
17
+ Dir['lib/tasks/**/*.rake'].each { |ext| load ext } if defined?(Rake)
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'rabl/extend/compiler'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mx
4
+ module Rabl
5
+ module Extend
6
+ module Compiler
7
+ VERSION = '0.2.0'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rabl'
4
+ require 'mx/rabl/extend/compiler/version'
5
+
6
+ module Mx
7
+ module Rabl
8
+ module Extend
9
+ module Compiler
10
+ if defined?(Rake)
11
+ ::Dir[::File.join(::File.dirname(__FILE__), '..', '..', '..', 'tasks', '**', '*.rake')].each do |rake_file|
12
+ load rake_file
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rabl'
5
+ require 'digest'
6
+ require 'stringio'
7
+ require 'mx/rabl/extend/compiler'
8
+
9
+ namespace :rabl do
10
+ namespace :extend do
11
+ namespace :compiler do
12
+ desc 'compile all extensions into rabl files'
13
+ task :all do
14
+ ::Rake::Task['rabl:extend:compiler:reset'].invoke
15
+ ::Rake::Task['rabl:extend:compiler:compile'].invoke
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rabl'
5
+ require 'digest'
6
+ require 'stringio'
7
+
8
+ namespace :rabl do
9
+ namespace :extend do
10
+ namespace :compiler do
11
+ desc 'compile all uncompbiled extensions into rabl files'
12
+ task :compile do
13
+ ::Rake.application['environment'].invoke if ::Rake::Task.task_defined?('environment')
14
+ view_paths = ::Rabl.configuration.view_paths
15
+
16
+ loop do
17
+ did_compile_a_file = false
18
+ view_paths.each do |view_path|
19
+ ::Dir.glob("#{view_path}/**/*.rabl").each do |rabl_file|
20
+ # Rewrite all of the file for extensions
21
+ file_contents = ::File.read(rabl_file)
22
+ new_file_contents = ::StringIO.new
23
+ file_contents.each_line do |file_line|
24
+ extension_file = file_line.scan(/\A[[:space:]]*extends[([:space:]]*['"]+([^"]*)['"]+/).flatten
25
+
26
+ if extension_file.empty?
27
+ new_file_contents.puts file_line
28
+ next
29
+ end
30
+
31
+ extension_filename = "#{view_path}/#{extension_file.first}.rabl"
32
+ extension_file_digest = ::Digest::SHA256.file(extension_filename).hexdigest
33
+ extension_contents = ::File.read(extension_filename)
34
+
35
+ new_file_contents.puts <<~EXTENDS_MESSAGE
36
+ # rabl-extend-compiler #{file_line.strip} => #{extension_file_digest}
37
+ #
38
+ # This file segment is generated by rabl-extend-compiler rake task
39
+ # and should not be edited. To edit the generated extension
40
+ # edit the file at: #{extension_file}
41
+ EXTENDS_MESSAGE
42
+
43
+ new_file_contents.puts extension_contents.gsub(/# frozen_string_literal: true[\r\n]*/, '')
44
+ new_file_contents.puts "# #{extension_file_digest}"
45
+ end
46
+
47
+ # Replace the file if the contents changed and the following will rewrite it again
48
+ if file_contents != new_file_contents.string
49
+ did_compile_a_file = true
50
+ ::File.write(rabl_file, new_file_contents.string)
51
+ end
52
+ end
53
+ end
54
+
55
+ if did_compile_a_file
56
+ did_compile_a_file = false
57
+ else
58
+ break
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rabl'
5
+ require 'digest'
6
+ require 'stringio'
7
+
8
+ namespace :rabl do
9
+ namespace :extend do
10
+ namespace :compiler do
11
+ desc 'reset extensions to be compiled'
12
+ task :reset do
13
+ ::Rake.application['environment'].invoke if ::Rake::Task.task_defined?('environment')
14
+ view_paths = ::Rabl.configuration.view_paths
15
+
16
+ view_paths.each do |view_path|
17
+ ::Dir.glob("#{view_path}/**/*.rabl").each do |rabl_file|
18
+ file_contents = ::File.read(rabl_file)
19
+ new_file_contents = ::StringIO.new
20
+
21
+ ##
22
+ # Run through each line and verify the Sha256 digest of the extension file contents
23
+ # against the digest that is already stored in the file that extends
24
+ #
25
+ waiting = false
26
+ waiting_hash = ''
27
+ file_contents.each_line do |file_line|
28
+ if waiting
29
+ waiting = !file_line.include?(waiting_hash) # if we are in a waiting state then we throw the line away
30
+ next
31
+ end
32
+
33
+ if file_line.strip.gsub(/[[:space:]]+/, ' ').start_with?('# rabl-extend-compiler extends')
34
+ extension_file = file_line.scan(/\A[[:space:]]*#[[:space:]]+rabl-extend-compiler[[:space:]]*extends[([:space:]]*['"]+([^"]*)['"]+/).flatten.first
35
+ extension_filename = "#{view_path}/#{extension_file}.rabl"
36
+ extension_file_digest = file_line.split('=>')[1]&.gsub(/[[:space:]]/, '')
37
+
38
+ if extension_file_digest == ::Digest::SHA256.file(extension_filename).hexdigest
39
+ new_file_contents.puts file_line
40
+ elsif extension_file_digest.nil?
41
+ new_file_contents.puts file_line.split('=>').first.gsub('# rabl-extend-compiler ', '')
42
+ else
43
+ new_file_contents.puts file_line.split('=>').first.gsub('# rabl-extend-compiler ', '')
44
+ waiting = true
45
+ waiting_hash = extension_file_digest
46
+ end
47
+ else
48
+ new_file_contents.puts file_line
49
+ end
50
+ end
51
+
52
+ # Replace the file if the contents changed and the following will rewrite it again
53
+ ::File.write(rabl_file, new_file_contents.string) if file_contents != new_file_contents.string
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rabl'
5
+ require 'digest'
6
+ require 'stringio'
7
+
8
+ namespace :rabl do
9
+ namespace :extend do
10
+ namespace :compiler do
11
+ desc 'verify that all extensions are created and the signatures match; return exit(1) if not verifiable'
12
+ task :verify do
13
+ ::Rake.application['environment'].invoke if ::Rake::Task.task_defined?('environment')
14
+ view_paths = ::Rabl.configuration.view_paths
15
+
16
+ view_paths.each do |view_path|
17
+ ::Dir.glob("#{view_path}/**/*.rabl").each do |rabl_file|
18
+ file_contents = ::File.read(rabl_file)
19
+
20
+ ##
21
+ # Run through each line and verify the Sha256 digest of the extension file contents
22
+ # against the digest that is already stored in the file that extends
23
+ #
24
+ waiting = false
25
+ waiting_hash = ''
26
+
27
+ file_contents.each_line do |file_line|
28
+ if waiting
29
+ waiting = !file_line.include?(waiting_hash) # if we are in a waiting state then we throw the line away
30
+ next
31
+ end
32
+
33
+ next unless file_line.strip.gsub(/[[:space:]]+/, ' ').start_with?('# rabl-extend-compiler extends')
34
+
35
+ extension_file = file_line.scan(/\A[[:space:]]*#[[:space:]]+rabl-extend-compiler[[:space:]]*extends[([:space:]]*['"]+([^"]*)['"]+/).flatten.first
36
+ extension_filename = "#{view_path}/#{extension_file}.rabl"
37
+ extension_file_digest = file_line.split('=>').last.gsub(/[[:space:]]/, '')
38
+
39
+ next if extension_file_digest == ::Digest::SHA256.file(extension_filename).hexdigest
40
+
41
+ $stderr << "rabl-extend-compiler: Compiled extension digest mismatch #{extension_filename}"
42
+ $stderr << 'rabl-extend-compiler: Run rake rabl:extend:compiler:all to reset'
43
+
44
+ exit(1)
45
+ end
46
+
47
+ file_contents.each_line do |file_line|
48
+ extension_file = file_line.scan(/\A[[:space:]]*extends[([:space:]]*['"]+([^"]*)['"]+/).flatten
49
+ next if extension_file.empty?
50
+
51
+ $stderr << "rabl-extend-compiler: Uncompiled extenion at #{extension_file.first}"
52
+
53
+ exit(1)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'mx/rabl/extend/compiler/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'mx-rabl-extend-compiler'
9
+ spec.version = ::Mx::Rabl::Extend::Compiler::VERSION
10
+ spec.authors = ['Brandon Dewitt', 'MXDevExperience', 'Bryant Morrill']
11
+ spec.email = ['devexperience@mx.com', 'bryantreadmorrill@gmail.com']
12
+
13
+ spec.summary = ' a set of rake tasks to compile rabl templates for performance '
14
+ spec.description = ' a set of rake tasks to compile and verify rabl templates that use `extend` for moooooaaar performance '
15
+ spec.homepage = 'https://github.com/mxenabled/mx-rabl-extend-compiler'
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ else
22
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
23
+ 'public gem pushes.'
24
+ end
25
+
26
+ # Specify which files should be added to the gem when it is released.
27
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
+ end
31
+ spec.bindir = 'exe'
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ['lib']
34
+
35
+ spec.add_dependency 'rabl'
36
+ spec.add_dependency 'rake', '>= 10'
37
+
38
+ spec.add_development_dependency 'bundler', '~> 2.6'
39
+ spec.add_development_dependency 'mad_rubocop'
40
+ spec.add_development_dependency 'pry'
41
+ spec.add_development_dependency 'rspec'
42
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mx-rabl-extend-compiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Dewitt
8
+ - MXDevExperience
9
+ - Bryant Morrill
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2025-04-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rabl
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '10'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '10'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.6'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2.6'
56
+ - !ruby/object:Gem::Dependency
57
+ name: mad_rubocop
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: pry
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: " a set of rake tasks to compile and verify rabl templates that use `extend`\
99
+ \ for moooooaaar performance "
100
+ email:
101
+ - devexperience@mx.com
102
+ - bryantreadmorrill@gmail.com
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - ".gitignore"
108
+ - ".travis.yml"
109
+ - Gemfile
110
+ - README.md
111
+ - Rakefile
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/mx/rabl/extend/compiler.rb
115
+ - lib/mx/rabl/extend/compiler/version.rb
116
+ - lib/tasks/all.rake
117
+ - lib/tasks/compile_extensions.rake
118
+ - lib/tasks/reset_extensions.rake
119
+ - lib/tasks/verify_extensions.rake
120
+ - mx-rabl-extend-compiler.gemspec
121
+ homepage: https://github.com/mxenabled/mx-rabl-extend-compiler
122
+ licenses: []
123
+ metadata:
124
+ allowed_push_host: https://rubygems.org
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubygems_version: 3.6.3
140
+ specification_version: 4
141
+ summary: a set of rake tasks to compile rabl templates for performance
142
+ test_files: []