├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── ast.coffee │ │ ├── cable.js │ │ ├── channels │ │ │ └── .keep │ │ ├── gist.coffee │ │ ├── sessions.coffee │ │ └── welcome.coffee │ └── stylesheets │ │ ├── application.css │ │ ├── ast.scss │ │ ├── gist.scss │ │ ├── sessions.scss │ │ ├── treeLinks.css │ │ └── welcome.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── ast_controller.rb │ ├── concerns │ │ └── .keep │ ├── gist_controller.rb │ ├── sessions_controller.rb │ └── welcome_controller.rb ├── helpers │ ├── application_helper.rb │ ├── ast_helper.rb │ ├── gist_helper.rb │ ├── sessions_helper.rb │ └── welcome_helper.rb ├── javascript │ └── packs │ │ ├── application.js │ │ ├── processorList.js │ │ ├── treeLinks.js │ │ ├── treeitemLinks.js │ │ └── utils.js ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ └── concerns │ │ └── .keep └── views │ ├── ast │ └── index.html.erb │ ├── gist │ ├── index.html.erb │ └── show.html.erb │ ├── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── shared │ ├── _output-view.html.erb │ ├── _source-view.html.erb │ ├── _transform-view.html.erb │ └── _tree-view.html.erb │ └── welcome │ └── index.html.erb ├── babel.config.js ├── bin ├── bundle ├── rails ├── rake ├── setup ├── update ├── webpack ├── webpack-dev-server └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── omniauth.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── codemirror.css ├── codemirror.js ├── favicon.ico ├── images │ ├── down-arrow-brown.png │ └── right-arrow-brown.png ├── normalize.css ├── robots.txt ├── screenshot.png └── solarized.css ├── storage └── .keep ├── test ├── application_system_test_case.rb ├── controllers │ ├── .keep │ ├── ast_controller_test.rb │ ├── gist_controller_test.rb │ ├── sessions_controller_test.rb │ └── welcome_controller_test.rb ├── fixtures │ ├── .keep │ └── files │ │ └── .keep ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── system │ └── .keep └── test_helper.rb ├── tmp └── .keep ├── vendor └── .keep └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "airbnb-base", 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaVersion": 2018, 13 | "sourceType": "module" 14 | }, 15 | "rules": { 16 | 'camelcase': 0, 17 | 'no-plusplus': 0 18 | } 19 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore uploaded files in development 21 | /storage/* 22 | !/storage/.keep 23 | 24 | /node_modules 25 | /yarn-error.log 26 | 27 | /public/assets 28 | .byebug_history 29 | 30 | # Ignore master key for decrypting credentials and more. 31 | /config/master.key 32 | 33 | /public/packs 34 | /public/packs-test 35 | /node_modules 36 | /yarn-error.log 37 | yarn-debug.log* 38 | .yarn-integrity 39 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.5.1 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.5.5' 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 7 | gem 'rails', '~> 5.2.3' 8 | # Use pg as the database for Active Record 9 | gem 'pg' 10 | # Use Puma as the app server 11 | gem 'puma', '~> 4.3' 12 | # Use SCSS for stylesheets 13 | gem 'sass-rails', '~> 5.0' 14 | # Use Uglifier as compressor for JavaScript assets 15 | gem 'uglifier', '>= 1.3.0' 16 | # See https://github.com/rails/execjs#readme for more supported runtimes 17 | # gem 'mini_racer', platforms: :ruby 18 | 19 | # Use CoffeeScript for .coffee assets and views 20 | gem 'coffee-rails', '~> 4.2' 21 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 22 | gem 'turbolinks', '~> 5' 23 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 24 | gem 'jbuilder', '~> 2.5' 25 | # Use Redis adapter to run Action Cable in production 26 | # gem 'redis', '~> 4.0' 27 | # Use ActiveModel has_secure_password 28 | # gem 'bcrypt', '~> 3.1.7' 29 | 30 | # Use ActiveStorage variant 31 | # gem 'mini_magick', '~> 4.8' 32 | 33 | # Use Capistrano for deployment 34 | # gem 'capistrano-rails', group: :development 35 | 36 | # Reduces boot times through caching; required in config/boot.rb 37 | gem 'bootsnap', '>= 1.1.0', require: false 38 | 39 | group :development, :test do 40 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 41 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 42 | end 43 | 44 | group :development do 45 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 46 | gem 'web-console', '>= 3.3.0' 47 | gem 'listen', '>= 3.0.5', '< 3.2' 48 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 49 | gem 'spring' 50 | gem 'spring-watcher-listen', '~> 2.0.0' 51 | end 52 | 53 | group :test do 54 | # Adds support for Capybara system testing and selenium driver 55 | gem 'capybara', '>= 2.15' 56 | gem 'selenium-webdriver' 57 | # Easy installation and use of chromedriver to run system tests with Chrome 58 | gem 'chromedriver-helper' 59 | end 60 | 61 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 62 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 63 | 64 | gem 'parser' 65 | gem 'jquery-rails' 66 | gem 'gist', git: 'https://github.com/rajasegar/gist.git', ref: '55c2247' 67 | gem 'omniauth-github' 68 | gem 'octokit' 69 | 70 | gem 'webpacker', '~> 4.x' 71 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/rajasegar/gist.git 3 | revision: 55c2247bfc59c29e5a6ba2f25f704b8fc86cc1de 4 | ref: 55c2247 5 | specs: 6 | gist (5.0.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (5.2.3) 12 | actionpack (= 5.2.3) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | actionmailer (5.2.3) 16 | actionpack (= 5.2.3) 17 | actionview (= 5.2.3) 18 | activejob (= 5.2.3) 19 | mail (~> 2.5, >= 2.5.4) 20 | rails-dom-testing (~> 2.0) 21 | actionpack (5.2.3) 22 | actionview (= 5.2.3) 23 | activesupport (= 5.2.3) 24 | rack (~> 2.0) 25 | rack-test (>= 0.6.3) 26 | rails-dom-testing (~> 2.0) 27 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 28 | actionview (5.2.3) 29 | activesupport (= 5.2.3) 30 | builder (~> 3.1) 31 | erubi (~> 1.4) 32 | rails-dom-testing (~> 2.0) 33 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 34 | activejob (5.2.3) 35 | activesupport (= 5.2.3) 36 | globalid (>= 0.3.6) 37 | activemodel (5.2.3) 38 | activesupport (= 5.2.3) 39 | activerecord (5.2.3) 40 | activemodel (= 5.2.3) 41 | activesupport (= 5.2.3) 42 | arel (>= 9.0) 43 | activestorage (5.2.3) 44 | actionpack (= 5.2.3) 45 | activerecord (= 5.2.3) 46 | marcel (~> 0.3.1) 47 | activesupport (5.2.3) 48 | concurrent-ruby (~> 1.0, >= 1.0.2) 49 | i18n (>= 0.7, < 2) 50 | minitest (~> 5.1) 51 | tzinfo (~> 1.1) 52 | addressable (2.8.0) 53 | public_suffix (>= 2.0.2, < 5.0) 54 | archive-zip (0.12.0) 55 | io-like (~> 0.3.0) 56 | arel (9.0.0) 57 | ast (2.4.0) 58 | bindex (0.8.1) 59 | bootsnap (1.4.5) 60 | msgpack (~> 1.0) 61 | builder (3.2.3) 62 | byebug (11.0.1) 63 | capybara (3.29.0) 64 | addressable 65 | mini_mime (>= 0.1.3) 66 | nokogiri (~> 1.8) 67 | rack (>= 1.6.0) 68 | rack-test (>= 0.6.3) 69 | regexp_parser (~> 1.5) 70 | xpath (~> 3.2) 71 | childprocess (2.0.0) 72 | rake (< 13.0) 73 | chromedriver-helper (2.1.1) 74 | archive-zip (~> 0.10) 75 | nokogiri (~> 1.8) 76 | coffee-rails (4.2.2) 77 | coffee-script (>= 2.2.0) 78 | railties (>= 4.0.0) 79 | coffee-script (2.4.1) 80 | coffee-script-source 81 | execjs 82 | coffee-script-source (1.12.2) 83 | concurrent-ruby (1.1.5) 84 | crass (1.0.5) 85 | erubi (1.8.0) 86 | execjs (2.7.0) 87 | faraday (0.15.4) 88 | multipart-post (>= 1.2, < 3) 89 | ffi (1.11.1) 90 | globalid (0.4.2) 91 | activesupport (>= 4.2.0) 92 | hashie (3.6.0) 93 | i18n (1.6.0) 94 | concurrent-ruby (~> 1.0) 95 | io-like (0.3.0) 96 | jbuilder (2.9.1) 97 | activesupport (>= 4.2.0) 98 | jquery-rails (4.3.5) 99 | rails-dom-testing (>= 1, < 3) 100 | railties (>= 4.2.0) 101 | thor (>= 0.14, < 2.0) 102 | jwt (2.2.1) 103 | listen (3.1.5) 104 | rb-fsevent (~> 0.9, >= 0.9.4) 105 | rb-inotify (~> 0.9, >= 0.9.7) 106 | ruby_dep (~> 1.2) 107 | loofah (2.3.1) 108 | crass (~> 1.0.2) 109 | nokogiri (>= 1.5.9) 110 | mail (2.7.1) 111 | mini_mime (>= 0.1.1) 112 | marcel (0.3.3) 113 | mimemagic (~> 0.3.2) 114 | method_source (0.9.2) 115 | mimemagic (0.3.10) 116 | nokogiri (~> 1) 117 | rake 118 | mini_mime (1.0.2) 119 | mini_portile2 (2.6.1) 120 | minitest (5.11.3) 121 | msgpack (1.3.1) 122 | multi_json (1.13.1) 123 | multi_xml (0.6.0) 124 | multipart-post (2.1.1) 125 | nio4r (2.5.8) 126 | nokogiri (1.12.5) 127 | mini_portile2 (~> 2.6.1) 128 | racc (~> 1.4) 129 | oauth2 (1.4.1) 130 | faraday (>= 0.8, < 0.16.0) 131 | jwt (>= 1.0, < 3.0) 132 | multi_json (~> 1.3) 133 | multi_xml (~> 0.5) 134 | rack (>= 1.2, < 3) 135 | octokit (4.14.0) 136 | sawyer (~> 0.8.0, >= 0.5.3) 137 | omniauth (1.9.0) 138 | hashie (>= 3.4.6, < 3.7.0) 139 | rack (>= 1.6.2, < 3) 140 | omniauth-github (1.3.0) 141 | omniauth (~> 1.5) 142 | omniauth-oauth2 (>= 1.4.0, < 2.0) 143 | omniauth-oauth2 (1.6.0) 144 | oauth2 (~> 1.1) 145 | omniauth (~> 1.9) 146 | parser (2.6.4.0) 147 | ast (~> 2.4.0) 148 | pg (1.1.4) 149 | public_suffix (4.0.6) 150 | puma (4.3.9) 151 | nio4r (~> 2.0) 152 | racc (1.5.2) 153 | rack (2.2.3) 154 | rack-proxy (0.6.5) 155 | rack 156 | rack-test (1.1.0) 157 | rack (>= 1.0, < 3) 158 | rails (5.2.3) 159 | actioncable (= 5.2.3) 160 | actionmailer (= 5.2.3) 161 | actionpack (= 5.2.3) 162 | actionview (= 5.2.3) 163 | activejob (= 5.2.3) 164 | activemodel (= 5.2.3) 165 | activerecord (= 5.2.3) 166 | activestorage (= 5.2.3) 167 | activesupport (= 5.2.3) 168 | bundler (>= 1.3.0) 169 | railties (= 5.2.3) 170 | sprockets-rails (>= 2.0.0) 171 | rails-dom-testing (2.0.3) 172 | activesupport (>= 4.2.0) 173 | nokogiri (>= 1.6) 174 | rails-html-sanitizer (1.2.0) 175 | loofah (~> 2.2, >= 2.2.2) 176 | railties (5.2.3) 177 | actionpack (= 5.2.3) 178 | activesupport (= 5.2.3) 179 | method_source 180 | rake (>= 0.8.7) 181 | thor (>= 0.19.0, < 2.0) 182 | rake (12.3.3) 183 | rb-fsevent (0.10.3) 184 | rb-inotify (0.10.0) 185 | ffi (~> 1.0) 186 | regexp_parser (1.6.0) 187 | ruby_dep (1.5.0) 188 | rubyzip (1.3.0) 189 | sass (3.7.4) 190 | sass-listen (~> 4.0.0) 191 | sass-listen (4.0.0) 192 | rb-fsevent (~> 0.9, >= 0.9.4) 193 | rb-inotify (~> 0.9, >= 0.9.7) 194 | sass-rails (5.1.0) 195 | railties (>= 5.2.0) 196 | sass (~> 3.1) 197 | sprockets (>= 2.8, < 4.0) 198 | sprockets-rails (>= 2.0, < 4.0) 199 | tilt (>= 1.1, < 3) 200 | sawyer (0.8.2) 201 | addressable (>= 2.3.5) 202 | faraday (> 0.8, < 2.0) 203 | selenium-webdriver (3.142.4) 204 | childprocess (>= 0.5, < 3.0) 205 | rubyzip (~> 1.2, >= 1.2.2) 206 | spring (2.1.0) 207 | spring-watcher-listen (2.0.1) 208 | listen (>= 2.7, < 4.0) 209 | spring (>= 1.2, < 3.0) 210 | sprockets (3.7.2) 211 | concurrent-ruby (~> 1.0) 212 | rack (> 1, < 3) 213 | sprockets-rails (3.2.1) 214 | actionpack (>= 4.0) 215 | activesupport (>= 4.0) 216 | sprockets (>= 3.0.0) 217 | thor (0.20.3) 218 | thread_safe (0.3.6) 219 | tilt (2.0.9) 220 | turbolinks (5.2.0) 221 | turbolinks-source (~> 5.2) 222 | turbolinks-source (5.2.0) 223 | tzinfo (1.2.5) 224 | thread_safe (~> 0.1) 225 | uglifier (4.1.20) 226 | execjs (>= 0.3.0, < 3) 227 | web-console (3.7.0) 228 | actionview (>= 5.0) 229 | activemodel (>= 5.0) 230 | bindex (>= 0.4.0) 231 | railties (>= 5.0) 232 | webpacker (4.0.7) 233 | activesupport (>= 4.2) 234 | rack-proxy (>= 0.6.1) 235 | railties (>= 4.2) 236 | websocket-driver (0.7.1) 237 | websocket-extensions (>= 0.1.0) 238 | websocket-extensions (0.1.5) 239 | xpath (3.2.0) 240 | nokogiri (~> 1.8) 241 | 242 | PLATFORMS 243 | ruby 244 | 245 | DEPENDENCIES 246 | bootsnap (>= 1.1.0) 247 | byebug 248 | capybara (>= 2.15) 249 | chromedriver-helper 250 | coffee-rails (~> 4.2) 251 | gist! 252 | jbuilder (~> 2.5) 253 | jquery-rails 254 | listen (>= 3.0.5, < 3.2) 255 | octokit 256 | omniauth-github 257 | parser 258 | pg 259 | puma (~> 4.3) 260 | rails (~> 5.2.3) 261 | sass-rails (~> 5.0) 262 | selenium-webdriver 263 | spring 264 | spring-watcher-listen (~> 2.0.0) 265 | turbolinks (~> 5) 266 | tzinfo-data 267 | uglifier (>= 1.3.0) 268 | web-console (>= 3.3.0) 269 | webpacker (~> 4.x) 270 | 271 | RUBY VERSION 272 | ruby 2.5.5p157 273 | 274 | BUNDLED WITH 275 | 2.0.2 276 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ruby-ast-explorer 2 | 3 | AST Explorer lets you to visualize your ruby code as AST(Abstract Syntax Trees). 4 | 5 | This uses the [parser](https://github.com/whitequark/parser) gem to convert the source code into ASTs 6 | and the [TreeWriter](https://www.rubydoc.info/github/whitequark/parser/Parser/TreeRewriter) class 7 | of the gem to apply transformations on the AST nodes and convert them back to source code. 8 | 9 | We can take the transformation logic and put into a ruby file like `transform.rb` and use 10 | the same to do large scale refactorings with your ruby code base using tools like [codeshift](https://github.com/rajasegar/codeshift) and [cybertron](https://github.com/rajasegar/cybertron). 11 | 12 | ```sh 13 | $ codeshift -t transform.rb app/models/**/*.rb 14 | ``` 15 | 16 | ## Export as gist 17 | You can also export the source and transform code as gist to your Github account by clicking 18 | the `Export as gist` button. But before that you need to sign in with Github and give permission to create gists for this app named ruby-ast-explorer. 19 | 20 | 21 | And with that you can directly use the transform from your gist. 22 | ```sh 23 | $ codeshift -t https://gist.githubusercontent.com/[user]/.../transform.rb app/models/**/*.rb 24 | ``` 25 | 26 | It has got four panes with the following content: 27 | 28 | * Top-Left => Original Source Code 29 | * Top-Right => AST in Tree and S-expressions formats 30 | * Bottom-Left => Transform Class 31 | * Bottom-Right => Output 32 | 33 | ![screenshot](https://github.com/rajasegar/ruby-ast-explorer/blob/master/public/screenshot.png) 34 | 35 | 36 | ## Reference: 37 | * https://whitequark.org/blog/2013/04/26/lets-play-with-ruby-code/ 38 | * https://blog.arkency.com/using-ruby-parser-and-ast-tree-to-find-deprecated-syntax/ 39 | * https://blog.arkency.com/rewriting-deprecated-apis-with-parser-gem/ 40 | * https://gist.github.com/jonatas/e70c874cbbd0cac5a9abd9f4a78fa816 41 | * https://github.com/jonatas/fast 42 | * http://www.zenspider.com/projects/ruby2ruby.html 43 | * [Parsing Ruby](https://whitequark.org/blog/2012/10/02/parsing-ruby/) 44 | * [awesome-ruby-ast](https://github.com/rajasegar/awesome-ruby-ast) 45 | 46 | Inspired by [AST Explorer](https://astexplorer.net) by [Felix Kling](https://github.com/fkling) 47 | 48 | ## Tools 49 | * [parser gem](https://github.com/whitequark/parser) 50 | * [CodeMirror](https://codemirror.net/) 51 | 52 | ## Other related tools 53 | * [Astrolabe](https://github.com/yujinakayama/astrolabe) 54 | * [codeshift](https://github.com/rajasegar/codeshift) 55 | * [cybertron](https://github.com/rajasegar/cybertron) 56 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require activestorage 15 | //= require turbolinks 16 | //= require_tree . 17 | //= require jquery 18 | //= require jquery_ujs 19 | -------------------------------------------------------------------------------- /app/assets/javascripts/ast.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/gist.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/welcome.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | body { 17 | font-family: Helvetica, sans-serif; 18 | font-size: 13px; 19 | padding-top: 2.5rem; 20 | padding-bottom: 2.5rem; 21 | overflow: hidden; 22 | } 23 | 24 | 25 | .dark-theme footer { 26 | background: #343a40; 27 | color: #fff; 28 | } 29 | 30 | .dark-theme .custom-control-label { 31 | color: #fff; 32 | } 33 | 34 | h1 { 35 | font-size: larger; 36 | } 37 | 38 | header { 39 | display: grid; 40 | grid-template-columns: auto 200px; 41 | } 42 | 43 | footer { 44 | text-align: center; 45 | position:fixed; 46 | bottom: 0; 47 | background: #f8f9fa; 48 | width: 100%; 49 | z-index: 9999; 50 | height: 30px; 51 | line-height: 30px; 52 | } 53 | 54 | footer p { 55 | margin-bottom: 0; 56 | } 57 | 58 | .navbar { 59 | padding: 0 1rem; 60 | } 61 | 62 | 63 | 64 | #tab-wrapper { 65 | border: 1px solid #dee2e6; 66 | background: #eee; 67 | } 68 | 69 | .tab-content { 70 | max-height: 40vh; 71 | overflow: auto; 72 | } 73 | 74 | .styled-background { 75 | background-color: #ff7; 76 | } 77 | 78 | 79 | .container-fluid { 80 | padding-left: 1em; 81 | padding-right: 0; 82 | } 83 | 84 | .CodeMirror { 85 | height: calc((100vh - 70px)/2); 86 | max-height: calc((100vh - 70px)/2); 87 | } 88 | 89 | 90 | .col-6 { 91 | border-bottom: 2px solid #ddd; 92 | padding: 0; 93 | } 94 | 95 | .col-6:nth-child(odd) { 96 | border-right: 2px solid #ddd; 97 | } 98 | 99 | .cm-s-solarized.CodeMirror { 100 | box-shadow: none; 101 | } 102 | 103 | .custom-control-label { 104 | color: white; 105 | } 106 | -------------------------------------------------------------------------------- /app/assets/stylesheets/ast.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the ast controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/gist.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the gist controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/treeLinks.css: -------------------------------------------------------------------------------- 1 | ul[role="tree"] { 2 | margin: 0 0 0 1em; 3 | padding: 0; 4 | list-style: none; 5 | font-family: monospace, serif; 6 | } 7 | 8 | ul[role="tree"] li { 9 | margin: 0; 10 | padding: 0; 11 | list-style: none; 12 | cursor: pointer; 13 | } 14 | 15 | ul[role="tree"] a { 16 | text-decoration: underline; 17 | border-color: transparent; 18 | } 19 | 20 | [role="treeitem"] ul { 21 | margin: 0; 22 | padding: 0; 23 | margin-left: 0.9em; 24 | } 25 | 26 | [role="treeitem"][aria-expanded="false"] > ul { 27 | display: none; 28 | } 29 | 30 | [role="treeitem"][aria-expanded="true"] > ul { 31 | display: block; 32 | } 33 | 34 | [role="treeitem"][aria-expanded="false"] > span::before { 35 | content: "+"; 36 | position: relative; 37 | right: 0.5em; 38 | color: green; 39 | } 40 | 41 | [role="treeitem"][aria-expanded="true"] > span::before { 42 | content: "-"; 43 | position: relative; 44 | right: 0.5em; 45 | color: red; 46 | } 47 | 48 | [role="treeitem"], 49 | [role="treeitem"] span { 50 | width: 16em; 51 | margin: 0; 52 | padding: 0.125em; 53 | display: block; 54 | color: #B58900; 55 | } 56 | 57 | /* disable default keyboard focus styling for treeitems 58 | Keyboard focus is styled with the following CSS */ 59 | [role="treeitem"]:focus { 60 | outline: 0; 61 | } 62 | 63 | [role="treeitem"].focus, 64 | [role="treeitem"] span.focus { 65 | border-color: black; 66 | background-color: #eee; 67 | } 68 | 69 | [role="treeitem"].hover, 70 | [role="treeitem"] span.hover { 71 | background-color: #ddd; 72 | } 73 | 74 | [role="treeitem"] span:hover { 75 | background-color: #ddd; 76 | } 77 | 78 | span.blue { 79 | color: blue; 80 | display: inline; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /app/assets/stylesheets/welcome.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the welcome controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/ast_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'parser/current' 4 | require 'json' 5 | 6 | # AST Controller 7 | class AstController < ApplicationController 8 | def index 9 | @ast = Parser::CurrentRuby.parse('2 + 2') 10 | p @ast 11 | respond_to do |format| 12 | format.json { render json: { ast: @ast } } 13 | end 14 | end 15 | 16 | def create 17 | @source_code = params[:code] 18 | @transform = params[:transform] 19 | 20 | ast = Parser::CurrentRuby.parse(params[:code]) 21 | 22 | # Doing eval is not that safe, need to sanitize 23 | eval(params[:transform]) 24 | 25 | buffer = Parser::Source::Buffer.new('(example)') 26 | buffer.source = params[:code] 27 | # begin 28 | temp = Parser::CurrentRuby.parse(params[:code]) 29 | rewriter = Transform.new 30 | 31 | # Rewrite the AST, returns a String with the new form. 32 | output = rewriter.rewrite(buffer, temp) 33 | # rescue StandardError 34 | # output = params[:code] 35 | # end 36 | 37 | respond_to do |format| 38 | format.json { render json: { ast: ast.to_s, output: output.to_s, treeData: ast.to_json } } 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/gist_controller.rb: -------------------------------------------------------------------------------- 1 | class GistController < ApplicationController 2 | 3 | def ast_explorer_gist?(files) 4 | if files 5 | files.has_key?("transform.rb") and files.has_key?("source.rb") 6 | else 7 | false 8 | end 9 | end 10 | 11 | def index 12 | response = Gist.list_all_gists("",{ :access_token => session[:github_token]}) 13 | @gists = JSON.parse(response.body).select { |gist| ast_explorer_gist?(gist['files']) } 14 | end 15 | 16 | def show 17 | gist_id = params[:id] 18 | 19 | # Also need to check if he is the owner of the gist 20 | if session[:user_id] 21 | session[:gist_id] = gist_id 22 | end 23 | @transform = Gist.read_gist( gist_id, 'transform.rb', { :access_token => session[:github_token]}) 24 | @source_code = Gist.read_gist( gist_id, 'source.rb', { :access_token => session[:github_token]}) 25 | end 26 | 27 | 28 | def create 29 | source = params[:code] 30 | transform = params[:transform] 31 | yml = "v:1\nparser:2.6.3.0\nrails:5.2.3\nruby:2.5.5p157" 32 | response = Gist.multi_gist({"source.rb" => source, "transform.rb" => transform, "ruby-ast-explorer.yml" => yml}, 33 | :public => true, 34 | :description => "A Gist generated by ruby-ast-explorer", 35 | :access_token => session[:github_token]) 36 | session[:gist_id] = response['id'] 37 | respond_to do |format| 38 | format.json { render :json => { message: "Exported to gist with name = ruby-ast-explorer.yml successfully.", gist: response['id'] } } 39 | end 40 | end 41 | 42 | def update 43 | source = params[:code] 44 | transform = params[:transform] 45 | response = Gist.multi_gist({"source.rb" => source, "transform.rb" => transform }, 46 | :access_token => session[:github_token], 47 | :update => params[:id]) 48 | respond_to do |format| 49 | format.json { render :json => { message: "Updated gist with name = ruby-ast-explorer.yml successfully.", gist: response['id'] } } 50 | end 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | def create 3 | auth = request.env["omniauth.auth"] 4 | #pp auth 5 | session[:user_id] = auth["uid"] 6 | session[:github_token] = auth.credentials.token 7 | redirect_to root_url, :notice => "Signed in!" 8 | end 9 | 10 | def destroy 11 | session[:user_id] = nil 12 | redirect_to root_url, :notice => "Signed out!" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | def index 3 | if session[:user_id] == nil 4 | redirect_to '/auth/github' 5 | end 6 | session[:gist_id] = nil 7 | @transform = %{# Your Transform Class should always extend from 8 | # Parser:: TreeRewriter 9 | class Transform < Parser::TreeRewriter 10 | def on_lvasgn(node) 11 | # Reverse the variable names 12 | replace(node.loc.name, node.children[0].to_s.reverse) 13 | end 14 | 15 | def on_def(node) 16 | replace(node.loc.name, node.children[0].to_s.reverse) 17 | end 18 | end} 19 | 20 | @source_code = %q(# Paste some ruby code here and 21 | # check the generated AST on the right 22 | tips = [ 23 | "Click on any AST node with a '+' to expand it", 24 | 25 | "Hovering over a node highlights the \ 26 | corresponding part in the source code", 27 | ] 28 | def print_tips 29 | tips.each { |key, value| print "Tip #{key}: #{value}" } 30 | end) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def flash_class(level) 3 | case level 4 | when :notice then "alert alert-info" 5 | when :success then "alert alert-success" 6 | when :error then "alert alert-error" 7 | when :alert then "alert alert-error" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/ast_helper.rb: -------------------------------------------------------------------------------- 1 | module AstHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/gist_helper.rb: -------------------------------------------------------------------------------- 1 | module GistHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/welcome_helper.rb: -------------------------------------------------------------------------------- 1 | module WelcomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | /* globals CodeMirror $ */ 3 | 4 | import { 5 | debounce, 6 | buildTreeView, 7 | indentCode, 8 | } from './utils'; 9 | 10 | document.addEventListener('turbolinks:load', () => { 11 | const markers = []; 12 | 13 | const editorOptions = { 14 | mode: 'text/x-ruby', 15 | matchBrackets: true, 16 | indentUnit: 2, 17 | lineNumbers: true, 18 | theme: 'solarized', 19 | styleSelectedText: true, 20 | }; 21 | 22 | const astEditorOptions = { 23 | mode: 'text/x-ruby', 24 | matchBrackets: true, 25 | indentUnit: 2, 26 | theme: 'solarized', 27 | readOnly: true, 28 | }; 29 | 30 | 31 | const editor = CodeMirror.fromTextArea(document.getElementById('editor'), editorOptions); 32 | const transformEditor = CodeMirror.fromTextArea(document.getElementById('transform-editor'), editorOptions); 33 | const outputEditor = CodeMirror.fromTextArea(document.getElementById('output-editor'), editorOptions); 34 | const astEditor = CodeMirror.fromTextArea(document.getElementById('ast-editor'), astEditorOptions); 35 | 36 | 37 | function indentAll() { 38 | indentCode(editor); 39 | indentCode(transformEditor); 40 | indentCode(outputEditor); 41 | } 42 | 43 | 44 | function updateAst(code, transform) { 45 | $.ajax({ 46 | url: '/ast', 47 | type: 'post', 48 | data: { code, transform }, 49 | success(data) { 50 | astEditor.setValue(data.ast); 51 | buildTreeView(data.treeData); 52 | outputEditor.setValue(data.output); 53 | }, 54 | error() {}, 55 | }); 56 | } 57 | 58 | function updateOutput(code, transform) { 59 | $.ajax({ 60 | url: '/ast', 61 | type: 'post', 62 | data: { code, transform }, 63 | success(data) { 64 | outputEditor.setValue(data.output); 65 | }, 66 | error() {}, 67 | }); 68 | } 69 | 70 | 71 | updateAst(editor.getValue(), transformEditor.getValue()); 72 | 73 | indentAll(); 74 | 75 | 76 | editor.on('change', debounce((cm) => { 77 | const code = cm.getValue(); 78 | const transform = transformEditor.getValue(); 79 | updateAst(code, transform); 80 | }, 250)); 81 | 82 | editor.on('cursorActivity', (e) => { 83 | // Collapse all items first 84 | const trees = document.querySelectorAll('[role="tree"]'); 85 | for (let i = 0; i < trees.length; i++) { 86 | trees[i].attributes['aria-expanded'] = 'false'; 87 | } 88 | 89 | 90 | const { line } = e.doc.getCursor(); // Cursor line 91 | const { ch } = e.doc.getCursor(); // Cursor character 92 | 93 | console.log({ line, ch }); 94 | 95 | const doc = editor.getDoc(); 96 | 97 | let pos = 0; 98 | for (let index = 0; index <= line; index++) { 99 | const lineLength = doc.getLine(index).length; // Adding 1 for new line 100 | 101 | pos += lineLength; 102 | } 103 | 104 | pos += ch; 105 | 106 | 107 | const nodes = Array.from(document.querySelectorAll('li')).filter((el) => { 108 | const { beginPos, endPos } = el.dataset; 109 | return pos > beginPos && pos < endPos; 110 | }); 111 | 112 | nodes.forEach((node) => { 113 | // HACK: to open full tree branch 114 | if (node.parentElement && node.parentElement.parentElement) { 115 | // node.parentElement.parentElement.click(); 116 | node.parentElement.parentElement.attributes['aria-expanded'] = 'true'; 117 | } 118 | // node.click(); 119 | // node.style.backgroundColor = 'yellow'; 120 | node.attributes['aria-expanded'] = 'true'; 121 | }); 122 | }); 123 | 124 | 125 | transformEditor.on('change', debounce((cm) => { 126 | const transform = cm.getValue(); 127 | const code = editor.getValue(); 128 | updateOutput(code, transform); 129 | }, 250)); 130 | 131 | $('#create-gist').click(function () { 132 | const _self = this; 133 | _self.disabled = true; 134 | $.ajax({ 135 | url: '/gist', 136 | type: 'post', 137 | data: { code: editor.getValue(), transform: transformEditor.getValue() }, 138 | success(data) { 139 | // window.alert(data.message); 140 | // _self.disabled = false; 141 | window.location = `/gist/${data.gist}`; 142 | }, 143 | error(data) { 144 | window.alert('Gist creation failed'); 145 | _self.disabled = false; 146 | }, 147 | }); 148 | }); 149 | 150 | $('#update-gist').click(function () { 151 | const _self = this; 152 | _self.disabled = true; 153 | const gistId = window.location.pathname.replace('/gist/', ''); 154 | $.ajax({ 155 | url: `/gist/${gistId}`, 156 | type: 'put', 157 | data: { code: editor.getValue(), transform: transformEditor.getValue() }, 158 | success(data) { 159 | window.alert(data.message); 160 | _self.disabled = false; 161 | }, 162 | error(data) { 163 | window.alert('Gist update failed'); 164 | _self.disabled = false; 165 | }, 166 | }); 167 | }); 168 | 169 | $('#switch-theme').on('click', (e) => { 170 | const theme = e.target.checked ? 'solarized dark' : 'solarized'; 171 | editor.setOption('theme', theme); 172 | astEditor.setOption('theme', theme); 173 | transformEditor.setOption('theme', theme); 174 | outputEditor.setOption('theme', theme); 175 | 176 | const $primarynav = document.getElementById('primary-nav'); 177 | 178 | if (e.target.checked) { 179 | $primarynav.classList.replace('navbar-light', 'navbar-dark'); 180 | $primarynav.classList.replace('bg-light', 'bg-dark'); 181 | document.body.classList.add('dark-theme'); 182 | } else { 183 | $primarynav.classList.replace('navbar-dark', 'navbar-light'); 184 | $primarynav.classList.replace('bg-dark', 'bg-light'); 185 | document.body.classList.remove('dark-theme'); 186 | } 187 | }); 188 | 189 | $(document).on('mouseover', '[role="treeitem"]', (event) => { 190 | const { beginPos, endPos } = event.currentTarget.dataset; 191 | 192 | // Clearing and pushing markers 193 | markers.forEach((marker) => marker.clear()); 194 | if (beginPos && endPos) { 195 | const doc = editor.getDoc(); 196 | const markFrom = doc.posFromIndex(beginPos); 197 | const markTo = doc.posFromIndex(endPos); 198 | 199 | const currentMark = editor.markText(markFrom, markTo, { className: 'styled-background' }); 200 | markers.push(currentMark); 201 | } 202 | 203 | event.stopPropagation(); 204 | }); 205 | 206 | $(document).on('mouseleave', '[role="treeitem"]', () => { 207 | // Clearing and pushing markers 208 | markers.forEach((marker) => marker.clear()); 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /app/javascript/packs/processorList.js: -------------------------------------------------------------------------------- 1 | const processorList = [ 2 | 'arg', 3 | 'argument', 4 | 'back_ref', 5 | 'blockarg', 6 | 'casgn', 7 | 'const', 8 | 'cvar', 9 | 'cvasgn', 10 | 'def', 11 | 'defs', 12 | 'gvar', 13 | 'gvasgn', 14 | 'ivar', 15 | 'ivasgn', 16 | 'kwarg', 17 | 'kwoptarg', 18 | 'kwrestarg', 19 | 'lvar', 20 | 'lvasgn', 21 | 'nth_ref', 22 | 'op_asgn', 23 | 'optarg', 24 | 'procarg0', 25 | 'restarg', 26 | 'send', 27 | 'shadowarg', 28 | 'var', 29 | 'vasgn', 30 | ]; 31 | 32 | export default processorList; 33 | -------------------------------------------------------------------------------- /app/javascript/packs/treeLinks.js: -------------------------------------------------------------------------------- 1 | import TreeitemLink from './treeitemLinks'; 2 | /* 3 | * This content is licensed according to the W3C Software License at 4 | * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 5 | * 6 | * File: TreeLinks.js 7 | * 8 | * Desc: Tree widget that implements ARIA Authoring Practices 9 | * for a tree being used as a file viewer 10 | */ 11 | 12 | /** 13 | * ARIA Treeview example 14 | * @function onload 15 | * @desc after page has loaded initialize all treeitems based on the role=treeitem 16 | */ 17 | 18 | window.addEventListener('load', () => { 19 | 20 | 21 | }); 22 | 23 | /* 24 | * @constructor 25 | * 26 | * @desc 27 | * Tree item object for representing the state and user interactions for a 28 | * tree widget 29 | * 30 | * @param node 31 | * An element with the role=tree attribute 32 | */ 33 | 34 | const TreeLinks = function (node) { 35 | // Check whether node is a DOM element 36 | if (typeof node !== 'object') { 37 | return; 38 | } 39 | 40 | this.domNode = node; 41 | 42 | this.treeitems = []; 43 | this.firstChars = []; 44 | 45 | this.firstTreeitem = null; 46 | this.lastTreeitem = null; 47 | }; 48 | 49 | TreeLinks.prototype.init = function () { 50 | function findTreeitems(node, tree, group) { 51 | let elem = node.firstElementChild; 52 | let ti = group; 53 | 54 | while (elem) { 55 | if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') { 56 | ti = new TreeitemLink(elem, tree, group); 57 | ti.init(); 58 | tree.treeitems.push(ti); 59 | tree.firstChars.push(ti.label.substring(0, 1).toLowerCase()); 60 | } 61 | 62 | if (elem.firstElementChild) { 63 | findTreeitems(elem, tree, ti); 64 | } 65 | 66 | elem = elem.nextElementSibling; 67 | } 68 | } 69 | 70 | // initialize pop up menus 71 | if (!this.domNode.getAttribute('role')) { 72 | this.domNode.setAttribute('role', 'tree'); 73 | } 74 | 75 | findTreeitems(this.domNode, this, false); 76 | 77 | this.updateVisibleTreeitems(); 78 | 79 | this.firstTreeitem.domNode.tabIndex = 0; 80 | }; 81 | 82 | TreeLinks.prototype.setFocusToItem = function (treeitem) { 83 | for (let i = 0; i < this.treeitems.length; i++) { 84 | const ti = this.treeitems[i]; 85 | 86 | if (ti === treeitem) { 87 | ti.domNode.tabIndex = 0; 88 | ti.domNode.focus(); 89 | } else { 90 | ti.domNode.tabIndex = -1; 91 | } 92 | } 93 | }; 94 | 95 | TreeLinks.prototype.setFocusToNextItem = function (currentItem) { 96 | let nextItem = false; 97 | 98 | for (let i = (this.treeitems.length - 1); i >= 0; i--) { 99 | const ti = this.treeitems[i]; 100 | if (ti === currentItem) { 101 | break; 102 | } 103 | if (ti.isVisible) { 104 | nextItem = ti; 105 | } 106 | } 107 | 108 | if (nextItem) { 109 | this.setFocusToItem(nextItem); 110 | } 111 | }; 112 | 113 | TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) { 114 | let prevItem = false; 115 | 116 | for (let i = 0; i < this.treeitems.length; i++) { 117 | const ti = this.treeitems[i]; 118 | if (ti === currentItem) { 119 | break; 120 | } 121 | if (ti.isVisible) { 122 | prevItem = ti; 123 | } 124 | } 125 | 126 | if (prevItem) { 127 | this.setFocusToItem(prevItem); 128 | } 129 | }; 130 | 131 | TreeLinks.prototype.setFocusToParentItem = function (currentItem) { 132 | if (currentItem.groupTreeitem) { 133 | this.setFocusToItem(currentItem.groupTreeitem); 134 | } 135 | }; 136 | 137 | TreeLinks.prototype.setFocusToFirstItem = function () { 138 | this.setFocusToItem(this.firstTreeitem); 139 | }; 140 | 141 | TreeLinks.prototype.setFocusToLastItem = function () { 142 | this.setFocusToItem(this.lastTreeitem); 143 | }; 144 | 145 | TreeLinks.prototype.expandTreeitem = function (currentItem) { 146 | if (currentItem.isExpandable) { 147 | currentItem.domNode.setAttribute('aria-expanded', true); 148 | this.updateVisibleTreeitems(); 149 | } 150 | }; 151 | 152 | TreeLinks.prototype.expandAllSiblingItems = function (currentItem) { 153 | for (let i = 0; i < this.treeitems.length; i++) { 154 | const ti = this.treeitems[i]; 155 | 156 | if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) { 157 | this.expandTreeitem(ti); 158 | } 159 | } 160 | }; 161 | 162 | TreeLinks.prototype.collapseTreeitem = function (currentItem) { 163 | let groupTreeitem = false; 164 | 165 | if (currentItem.isExpanded()) { 166 | groupTreeitem = currentItem; 167 | } else { 168 | groupTreeitem = currentItem.groupTreeitem; 169 | } 170 | 171 | if (groupTreeitem) { 172 | groupTreeitem.domNode.setAttribute('aria-expanded', false); 173 | this.updateVisibleTreeitems(); 174 | this.setFocusToItem(groupTreeitem); 175 | } 176 | }; 177 | 178 | TreeLinks.prototype.updateVisibleTreeitems = function () { 179 | this.firstTreeitem = this.treeitems[0]; 180 | 181 | for (let i = 0; i < this.treeitems.length; i++) { 182 | const ti = this.treeitems[i]; 183 | 184 | let parent = ti.domNode.parentNode; 185 | 186 | ti.isVisible = true; 187 | 188 | while (parent && (parent !== this.domNode)) { 189 | if (parent.getAttribute('aria-expanded') == 'false') { 190 | ti.isVisible = false; 191 | } 192 | parent = parent.parentNode; 193 | } 194 | 195 | if (ti.isVisible) { 196 | this.lastTreeitem = ti; 197 | } 198 | } 199 | }; 200 | 201 | TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, char) { 202 | let start; 203 | let index; 204 | char = char.toLowerCase(); 205 | 206 | // Get start index for search based on position of currentItem 207 | start = this.treeitems.indexOf(currentItem) + 1; 208 | if (start === this.treeitems.length) { 209 | start = 0; 210 | } 211 | 212 | // Check remaining slots in the menu 213 | index = this.getIndexFirstChars(start, char); 214 | 215 | // If not found in remaining slots, check from beginning 216 | if (index === -1) { 217 | index = this.getIndexFirstChars(0, char); 218 | } 219 | 220 | // If match was found... 221 | if (index > -1) { 222 | this.setFocusToItem(this.treeitems[index]); 223 | } 224 | }; 225 | 226 | TreeLinks.prototype.getIndexFirstChars = function (startIndex, char) { 227 | for (let i = startIndex; i < this.firstChars.length; i++) { 228 | if (this.treeitems[i].isVisible) { 229 | if (char === this.firstChars[i]) { 230 | return i; 231 | } 232 | } 233 | } 234 | return -1; 235 | }; 236 | 237 | 238 | export default TreeLinks; 239 | -------------------------------------------------------------------------------- /app/javascript/packs/treeitemLinks.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is licensed according to the W3C Software License at 3 | * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 4 | * 5 | * File: TreeitemLink.js 6 | * 7 | * Desc: Treeitem widget that implements ARIA Authoring Practices 8 | * for a tree being used as a file viewer 9 | */ 10 | 11 | /* 12 | * @constructor 13 | * 14 | * @desc 15 | * Treeitem object for representing the state and user interactions for a 16 | * treeItem widget 17 | * 18 | * @param node 19 | * An element with the role=tree attribute 20 | */ 21 | 22 | const TreeitemLink = function (node, treeObj, group) { 23 | // Check whether node is a DOM element 24 | if (typeof node !== 'object') { 25 | return; 26 | } 27 | 28 | node.tabIndex = -1; 29 | this.tree = treeObj; 30 | this.groupTreeitem = group; 31 | this.domNode = node; 32 | this.label = node.textContent.trim(); 33 | this.stopDefaultClick = false; 34 | 35 | if (node.getAttribute('aria-label')) { 36 | this.label = node.getAttribute('aria-label').trim(); 37 | } 38 | 39 | this.isExpandable = false; 40 | this.isVisible = false; 41 | this.inGroup = false; 42 | 43 | if (group) { 44 | this.inGroup = true; 45 | } 46 | 47 | let elem = node.firstElementChild; 48 | 49 | while (elem) { 50 | if (elem.tagName.toLowerCase() == 'ul') { 51 | elem.setAttribute('role', 'group'); 52 | this.isExpandable = true; 53 | break; 54 | } 55 | 56 | elem = elem.nextElementSibling; 57 | } 58 | 59 | this.keyCode = Object.freeze({ 60 | RETURN: 13, 61 | SPACE: 32, 62 | PAGEUP: 33, 63 | PAGEDOWN: 34, 64 | END: 35, 65 | HOME: 36, 66 | LEFT: 37, 67 | UP: 38, 68 | RIGHT: 39, 69 | DOWN: 40, 70 | }); 71 | }; 72 | 73 | TreeitemLink.prototype.init = function () { 74 | this.domNode.tabIndex = -1; 75 | 76 | if (!this.domNode.getAttribute('role')) { 77 | this.domNode.setAttribute('role', 'treeitem'); 78 | } 79 | 80 | this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); 81 | this.domNode.addEventListener('click', this.handleClick.bind(this)); 82 | this.domNode.addEventListener('focus', this.handleFocus.bind(this)); 83 | this.domNode.addEventListener('blur', this.handleBlur.bind(this)); 84 | 85 | /* 86 | if (this.isExpandable) { 87 | this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this)); 88 | this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this)); 89 | } 90 | else { 91 | this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this)); 92 | this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this)); 93 | } 94 | */ 95 | }; 96 | 97 | TreeitemLink.prototype.isExpanded = function () { 98 | if (this.isExpandable) { 99 | return this.domNode.getAttribute('aria-expanded') === 'true'; 100 | } 101 | 102 | return false; 103 | }; 104 | 105 | /* EVENT HANDLERS */ 106 | 107 | TreeitemLink.prototype.handleKeydown = function (event) { 108 | const tgt = event.currentTarget; 109 | let flag = false; 110 | const char = event.key; 111 | let clickEvent; 112 | 113 | function isPrintableCharacter(str) { 114 | return str.length === 1 && str.match(/\S/); 115 | } 116 | 117 | function printableCharacter(item) { 118 | if (char == '*') { 119 | item.tree.expandAllSiblingItems(item); 120 | flag = true; 121 | } else if (isPrintableCharacter(char)) { 122 | item.tree.setFocusByFirstCharacter(item, char); 123 | flag = true; 124 | } 125 | } 126 | 127 | this.stopDefaultClick = false; 128 | 129 | if (event.altKey || event.ctrlKey || event.metaKey) { 130 | return; 131 | } 132 | 133 | if (event.shift) { 134 | if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) { 135 | event.stopPropagation(); 136 | this.stopDefaultClick = true; 137 | } else if (isPrintableCharacter(char)) { 138 | printableCharacter(this); 139 | } 140 | } else { 141 | switch (event.keyCode) { 142 | case this.keyCode.SPACE: 143 | case this.keyCode.RETURN: 144 | if (this.isExpandable) { 145 | if (this.isExpanded()) { 146 | this.tree.collapseTreeitem(this); 147 | } else { 148 | this.tree.expandTreeitem(this); 149 | } 150 | flag = true; 151 | } else { 152 | event.stopPropagation(); 153 | this.stopDefaultClick = true; 154 | } 155 | break; 156 | 157 | case this.keyCode.UP: 158 | this.tree.setFocusToPreviousItem(this); 159 | flag = true; 160 | break; 161 | 162 | case this.keyCode.DOWN: 163 | this.tree.setFocusToNextItem(this); 164 | flag = true; 165 | break; 166 | 167 | case this.keyCode.RIGHT: 168 | if (this.isExpandable) { 169 | if (this.isExpanded()) { 170 | this.tree.setFocusToNextItem(this); 171 | } else { 172 | this.tree.expandTreeitem(this); 173 | } 174 | } 175 | flag = true; 176 | break; 177 | 178 | case this.keyCode.LEFT: 179 | if (this.isExpandable && this.isExpanded()) { 180 | this.tree.collapseTreeitem(this); 181 | flag = true; 182 | } else if (this.inGroup) { 183 | this.tree.setFocusToParentItem(this); 184 | flag = true; 185 | } 186 | break; 187 | 188 | case this.keyCode.HOME: 189 | this.tree.setFocusToFirstItem(); 190 | flag = true; 191 | break; 192 | 193 | case this.keyCode.END: 194 | this.tree.setFocusToLastItem(); 195 | flag = true; 196 | break; 197 | 198 | default: 199 | if (isPrintableCharacter(char)) { 200 | printableCharacter(this); 201 | } 202 | break; 203 | } 204 | } 205 | 206 | if (flag) { 207 | event.stopPropagation(); 208 | event.preventDefault(); 209 | } 210 | }; 211 | 212 | TreeitemLink.prototype.handleClick = function (event) { 213 | // only process click events that directly happened on this treeitem 214 | if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) { 215 | return; 216 | } 217 | 218 | if (this.isExpandable) { 219 | if (this.isExpanded()) { 220 | this.tree.collapseTreeitem(this); 221 | } else { 222 | this.tree.expandTreeitem(this); 223 | } 224 | // event.stopPropagation(); 225 | } 226 | }; 227 | 228 | TreeitemLink.prototype.handleFocus = function (event) { 229 | let node = this.domNode; 230 | if (this.isExpandable) { 231 | node = node.firstElementChild; 232 | } 233 | node.classList.add('focus'); 234 | }; 235 | 236 | TreeitemLink.prototype.handleBlur = function (event) { 237 | let node = this.domNode; 238 | if (this.isExpandable) { 239 | node = node.firstElementChild; 240 | } 241 | node.classList.remove('focus'); 242 | }; 243 | 244 | TreeitemLink.prototype.handleMouseOver = function (event) { 245 | // event.currentTarget.classList.add('hover'); 246 | console.log('hover'); 247 | }; 248 | 249 | TreeitemLink.prototype.handleMouseOut = function (event) { 250 | event.currentTarget.classList.remove('hover'); 251 | }; 252 | 253 | 254 | export default TreeitemLink; 255 | -------------------------------------------------------------------------------- /app/javascript/packs/utils.js: -------------------------------------------------------------------------------- 1 | import processorList from './processorList'; 2 | import TreeLinks from './treeLinks'; 3 | 4 | // https://davidwalsh.name/javascript-debounce-function 5 | function debounce(func, wait, immediate) { 6 | let timeout; 7 | return function () { // eslint-disable-line 8 | const context = this; const 9 | args = arguments;// eslint-disable-line 10 | const later = function () { // eslint-disable-line 11 | timeout = null; 12 | if (!immediate) func.apply(context, args); 13 | }; 14 | const callNow = immediate && !timeout; 15 | clearTimeout(timeout); 16 | timeout = setTimeout(later, wait); 17 | if (callNow) func.apply(context, args); 18 | }; 19 | } 20 | 21 | function initTree() { 22 | const trees = document.querySelectorAll('[role="tree"]'); 23 | 24 | for (let i = 0; i < trees.length; i++) { 25 | const t = new TreeLinks(trees[i]); 26 | t.init(); 27 | } 28 | } 29 | 30 | function buildTreeView(ast) { 31 | const treeData = JSON.parse(ast); 32 | // console.log(treeData); 33 | 34 | function traverse(node) { 35 | let treeHtml = ''; 36 | 37 | if (node) { 38 | const { location, type } = node; 39 | const { 40 | begin, end, expression, keyword, name, operator, 41 | } = location; 42 | 43 | if (expression) { 44 | const { begin_pos, end_pos } = expression; // eslint-disable-line 45 | const processorNode = (t) => (processorList.includes(t) ? `${t}: #on_${t}` : t); 46 | treeHtml += `'; 133 | } 134 | 135 | return treeHtml; 136 | } 137 | 138 | document.getElementById('ast-tree').innerHTML = traverse(treeData); 139 | 140 | initTree(); 141 | } 142 | 143 | function indentCode(ed) { 144 | ed.setSelection({ 145 | line: ed.firstLine(), 146 | ch: 0, 147 | sticky: null, 148 | }, { 149 | line: ed.lastLine(), 150 | ch: 0, 151 | sticky: null, 152 | }, 153 | { scroll: false }); 154 | // auto indent the selection 155 | ed.indentSelection('smart'); 156 | ed.setCursor({ line: ed.firstLine(), ch: 0 }); 157 | } 158 | 159 | 160 | export { 161 | debounce, 162 | buildTreeView, 163 | indentCode, 164 | }; 165 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/views/ast/index.html.erb: -------------------------------------------------------------------------------- 1 |

Ast#index

2 |

Find me in app/views/ast/index.html.erb

3 | -------------------------------------------------------------------------------- /app/views/gist/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Import from Gist

3 |
4 | <% @gists.each do |gist| %> 5 | <%= gist['description'] %> : <%= gist['files'].keys.join(', ') %> 6 | <% end %> 7 | 8 |
9 | -------------------------------------------------------------------------------- /app/views/gist/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= render 'shared/source-view' %> 5 |
6 |
7 | <%= render 'shared/tree-view' %> 8 |
9 |
10 |
11 |
12 |
13 |
14 | <%= render 'shared/transform-view' %> 15 |
16 |
17 | <%= render 'shared/output-view' %> 18 |
19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ruby-ast-explorer 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 18 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 19 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> 20 | 21 | 22 | 23 | 60 | <% flash.each do |key, value| %> 61 | 67 | <% end %> 68 | <%= yield %> 69 | 74 | 75 | 76 | 77 | 78 | 79 | <%#%> 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/shared/_output-view.html.erb: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /app/views/shared/_source-view.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/views/shared/_transform-view.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/views/shared/_tree-view.html.erb: -------------------------------------------------------------------------------- 1 | 10 |
11 |
12 |
    13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /app/views/welcome/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= render 'shared/source-view' %> 5 |
6 |
7 | <%= render 'shared/tree-view' %> 8 |
9 |
10 |
11 |
12 |
13 |
14 | <%= render 'shared/transform-view' %> 15 |
16 |
17 | <%= render 'shared/output-view' %> 18 |
19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | var validEnv = ['development', 'test', 'production'] 3 | var currentEnv = api.env() 4 | var isDevelopmentEnv = api.env('development') 5 | var isProductionEnv = api.env('production') 6 | var isTestEnv = api.env('test') 7 | 8 | if (!validEnv.includes(currentEnv)) { 9 | throw new Error( 10 | 'Please specify a valid `NODE_ENV` or ' + 11 | '`BABEL_ENV` environment variables. Valid values are "development", ' + 12 | '"test", and "production". Instead, received: ' + 13 | JSON.stringify(currentEnv) + 14 | '.' 15 | ) 16 | } 17 | 18 | return { 19 | presets: [ 20 | isTestEnv && [ 21 | require('@babel/preset-env').default, 22 | { 23 | targets: { 24 | node: 'current' 25 | } 26 | } 27 | ], 28 | (isProductionEnv || isDevelopmentEnv) && [ 29 | require('@babel/preset-env').default, 30 | { 31 | forceAllTransforms: true, 32 | useBuiltIns: 'entry', 33 | corejs: 3, 34 | modules: false, 35 | exclude: ['transform-typeof-symbol'] 36 | } 37 | ] 38 | ].filter(Boolean), 39 | plugins: [ 40 | require('babel-plugin-macros'), 41 | require('@babel/plugin-syntax-dynamic-import').default, 42 | isTestEnv && require('babel-plugin-dynamic-import-node'), 43 | require('@babel/plugin-transform-destructuring').default, 44 | [ 45 | require('@babel/plugin-proposal-class-properties').default, 46 | { 47 | loose: true 48 | } 49 | ], 50 | [ 51 | require('@babel/plugin-proposal-object-rest-spread').default, 52 | { 53 | useBuiltIns: true 54 | } 55 | ], 56 | [ 57 | require('@babel/plugin-transform-runtime').default, 58 | { 59 | helpers: false, 60 | regenerator: true, 61 | corejs: false 62 | } 63 | ], 64 | [ 65 | require('@babel/plugin-transform-regenerator').default, 66 | { 67 | async: false 68 | } 69 | ] 70 | ].filter(Boolean) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a starting point to setup your application. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:setup' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/webpack_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::WebpackRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/dev_server_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::DevServerRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module RubyAstExplorer 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.2 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration can go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded after loading 17 | # the framework and any gems in your application. 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: ruby-ast-explorer_production 11 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | WJxrdqspe0B9dpjlccb2ViPj/ByT9UmdqFaYKxd+OpY3o2OLvdmW8SUuDu5iPbNZIRgqFAtZuVjbaRCRaCXLg+Gs43T9epKAhZdaerLgW8pc2zUBFsGQaYJ/yVbWe6UECMPOOSUq3Kee00drOiIU+dCsHh2Woi1PgxPrtxkgneBVjzUtNi6bNKr3ipBk8QKh/VNOGnsLlN9ElbuJQPnFFrSDa7YW1gPgaUgHcMIpZPeKcb5AEfhSgiKKt1McJ7YW0V33aVbjb6+XwO04ETZyEtguCmTckk9tlBC/cxioGKUWN42T74ohCPFRnRx983YeqrWaZC8Yyt04Re6Acb8ZMUlt1jBA1Ow79JKHrTLvKVAzWhJD8VWZ7ap88+QCL5zWEKdHdlcsb/XWr/Q7yazIL4mFQ2zTwDJh3zUD--aLiUjnC9AP8kBxBB--bCXgpy0x0M8FAxN4LyOauw== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: postgresql 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | #database: my_development_db 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: my_development_db 22 | 23 | production: 24 | <<: *default 25 | database: my_development_db 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | 20 | config.cache_store = :memory_store 21 | config.public_file_server.headers = { 22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 23 | } 24 | else 25 | config.action_controller.perform_caching = false 26 | 27 | config.cache_store = :null_store 28 | end 29 | 30 | # Store uploaded files on the local file system (see config/storage.yml for options) 31 | config.active_storage.service = :local 32 | 33 | # Don't care if the mailer can't send. 34 | config.action_mailer.raise_delivery_errors = false 35 | 36 | config.action_mailer.perform_caching = false 37 | 38 | # Print deprecation notices to the Rails logger. 39 | config.active_support.deprecation = :log 40 | 41 | # Raise an error on page load if there are pending migrations. 42 | config.active_record.migration_error = :page_load 43 | 44 | # Highlight code that triggered database queries in logs. 45 | config.active_record.verbose_query_logs = true 46 | 47 | # Debug mode disables concatenation and preprocessing of assets. 48 | # This option may cause significant delays in view rendering with a large 49 | # number of complex assets. 50 | config.assets.debug = true 51 | 52 | # Suppress logger output for asset requests. 53 | config.assets.quiet = true 54 | 55 | # Raises error for missing translations 56 | # config.action_view.raise_on_missing_translations = true 57 | 58 | # Use an evented file watcher to asynchronously detect changes in source code, 59 | # routes, locales, etc. This feature depends on the listen gem. 60 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 61 | 62 | ENV['GITHUB_KEY'] = '23b493911b3a796d843b' 63 | ENV['GITHUB_SECRET'] = 'a10756c33d3a06cd3b534d2aa4bb6aff93fa14c3' 64 | end 65 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 24 | 25 | # Compress JavaScripts and CSS. 26 | #config.assets.js_compressor = :uglifier 27 | config.assets.js_compressor = Uglifier.new(:harmony => true) 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Store uploaded files on the local file system (see config/storage.yml for options) 43 | config.active_storage.service = :local 44 | 45 | # Mount Action Cable outside main process or domain 46 | # config.action_cable.mount_path = nil 47 | # config.action_cable.url = 'wss://example.com/cable' 48 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 49 | 50 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 51 | # config.force_ssl = true 52 | 53 | # Use the lowest log level to ensure availability of diagnostic information 54 | # when problems arise. 55 | config.log_level = :debug 56 | 57 | # Prepend all log lines with the following tags. 58 | config.log_tags = [ :request_id ] 59 | 60 | # Use a different cache store in production. 61 | # config.cache_store = :mem_cache_store 62 | 63 | # Use a real queuing backend for Active Job (and separate queues per environment) 64 | # config.active_job.queue_adapter = :resque 65 | # config.active_job.queue_name_prefix = "ruby-ast-explorer_#{Rails.env}" 66 | 67 | config.action_mailer.perform_caching = false 68 | 69 | # Ignore bad email addresses and do not raise email delivery errors. 70 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 71 | # config.action_mailer.raise_delivery_errors = false 72 | 73 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 74 | # the I18n.default_locale when a translation cannot be found). 75 | config.i18n.fallbacks = true 76 | 77 | # Send deprecation notices to registered listeners. 78 | config.active_support.deprecation = :notify 79 | 80 | # Use default logging formatter so that PID and timestamp are not suppressed. 81 | config.log_formatter = ::Logger::Formatter.new 82 | 83 | # Use a different logger for distributed setups. 84 | # require 'syslog/logger' 85 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 86 | 87 | if ENV["RAILS_LOG_TO_STDOUT"].present? 88 | logger = ActiveSupport::Logger.new(STDOUT) 89 | logger.formatter = config.log_formatter 90 | config.logger = ActiveSupport::TaggedLogging.new(logger) 91 | end 92 | 93 | # Do not dump schema after migrations. 94 | config.active_record.dump_schema_after_migration = false 95 | 96 | ENV['GITHUB_KEY'] = '79ad14545cc1877b4ed8' 97 | ENV['GITHUB_SECRET'] = '7e8de7e18eb6722c5bc9673b69b4cf7dfb5d6e86' 98 | end 99 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Store uploaded files on the local file system in a temporary directory 32 | config.active_storage.service = :test 33 | 34 | config.action_mailer.perform_caching = false 35 | 36 | # Tell Action Mailer not to deliver emails to the real world. 37 | # The :test delivery method accumulates sent emails in the 38 | # ActionMailer::Base.deliveries array. 39 | config.action_mailer.delivery_method = :test 40 | 41 | # Print deprecation notices to the stderr. 42 | config.active_support.deprecation = :stderr 43 | 44 | # Raises error for missing translations 45 | # config.action_view.raise_on_missing_translations = true 46 | end 47 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Report CSP violations to a specified URI 23 | # For further information see the following documentation: 24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # Rails.application.config.content_security_policy_report_only = true 26 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # config/initializers/omniauth.rb 4 | Rails.application.config.middleware.use OmniAuth::Builder do 5 | provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], 6 | scope: 'user:email,user:follow,gist' 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. 30 | # 31 | # preload_app! 32 | 33 | # Allow puma to be restarted by `rails restart` command. 34 | plugin :tmp_restart 35 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get 'gist/show' 3 | get 'ast/index' 4 | resources :ast 5 | resources :gist 6 | get 'welcome/index' 7 | post 'ast/gist' => 'ast#gist' 8 | get "/auth/github/callback" => "sessions#create" 9 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 10 | root 'welcome#index' 11 | end 12 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | module.exports = environment 4 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpacker.yml: -------------------------------------------------------------------------------- 1 | # Note: You must restart bin/webpack-dev-server for changes to take effect 2 | 3 | default: &default 4 | source_path: app/javascript 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | check_yarn_integrity: false 10 | webpack_compile_output: false 11 | 12 | # Additional paths webpack should lookup modules 13 | # ['app/assets', 'engine/foo/app/assets'] 14 | resolved_paths: [] 15 | 16 | # Reload manifest.json on all requests so we reload latest compiled packs 17 | cache_manifest: false 18 | 19 | # Extract and emit a css file 20 | extract_css: false 21 | 22 | static_assets_extensions: 23 | - .jpg 24 | - .jpeg 25 | - .png 26 | - .gif 27 | - .tiff 28 | - .ico 29 | - .svg 30 | - .eot 31 | - .otf 32 | - .ttf 33 | - .woff 34 | - .woff2 35 | 36 | extensions: 37 | - .mjs 38 | - .js 39 | - .sass 40 | - .scss 41 | - .css 42 | - .module.sass 43 | - .module.scss 44 | - .module.css 45 | - .png 46 | - .svg 47 | - .gif 48 | - .jpeg 49 | - .jpg 50 | 51 | development: 52 | <<: *default 53 | compile: true 54 | 55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules 56 | check_yarn_integrity: true 57 | 58 | # Reference: https://webpack.js.org/configuration/dev-server/ 59 | dev_server: 60 | https: false 61 | host: localhost 62 | port: 3035 63 | public: localhost:3035 64 | hmr: false 65 | # Inline should be set to true if using HMR 66 | inline: true 67 | overlay: true 68 | compress: true 69 | disable_host_check: true 70 | use_local_ip: false 71 | quiet: false 72 | headers: 73 | 'Access-Control-Allow-Origin': '*' 74 | watch_options: 75 | ignored: '**/node_modules/**' 76 | 77 | 78 | test: 79 | <<: *default 80 | compile: true 81 | 82 | # Compile test packs to a separate directory 83 | public_output_path: packs-test 84 | 85 | production: 86 | <<: *default 87 | 88 | # Production depends on precompilation of packs prior to booting for performance. 89 | compile: true 90 | 91 | # Extract and emit a css file 92 | extract_css: true 93 | 94 | # Cache manifest.json for performance 95 | cache_manifest: true 96 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ruby-ast-explorer", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/webpacker": "^4.0.7" 6 | }, 7 | "devDependencies": { 8 | "eslint": "^6.3.0", 9 | "eslint-config-airbnb-base": "^14.0.0", 10 | "eslint-plugin-import": "^2.18.2", 11 | "webpack-dev-server": "^3.8.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | .cm-fat-cursor-mark { 63 | background-color: rgba(20, 255, 20, 0.5); 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-rulers { 98 | position: absolute; 99 | left: 0; right: 0; top: -50px; bottom: -20px; 100 | overflow: hidden; 101 | } 102 | .CodeMirror-ruler { 103 | border-left: 1px solid #ccc; 104 | top: 0; bottom: 0; 105 | position: absolute; 106 | } 107 | 108 | /* DEFAULT THEME */ 109 | 110 | .cm-s-default .cm-header {color: blue;} 111 | .cm-s-default .cm-quote {color: #090;} 112 | .cm-negative {color: #d44;} 113 | .cm-positive {color: #292;} 114 | .cm-header, .cm-strong {font-weight: bold;} 115 | .cm-em {font-style: italic;} 116 | .cm-link {text-decoration: underline;} 117 | .cm-strikethrough {text-decoration: line-through;} 118 | 119 | .cm-s-default .cm-keyword {color: #708;} 120 | .cm-s-default .cm-atom {color: #219;} 121 | .cm-s-default .cm-number {color: #164;} 122 | .cm-s-default .cm-def {color: #00f;} 123 | .cm-s-default .cm-variable, 124 | .cm-s-default .cm-punctuation, 125 | .cm-s-default .cm-property, 126 | .cm-s-default .cm-operator {} 127 | .cm-s-default .cm-variable-2 {color: #05a;} 128 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-qualifier {color: #555;} 134 | .cm-s-default .cm-builtin {color: #30a;} 135 | .cm-s-default .cm-bracket {color: #997;} 136 | .cm-s-default .cm-tag {color: #170;} 137 | .cm-s-default .cm-attribute {color: #00c;} 138 | .cm-s-default .cm-hr {color: #999;} 139 | .cm-s-default .cm-link {color: #00c;} 140 | 141 | .cm-s-default .cm-error {color: #f00;} 142 | .cm-invalidchar {color: #f00;} 143 | 144 | .CodeMirror-composing { border-bottom: 2px solid; } 145 | 146 | /* Default styles for common addons */ 147 | 148 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 149 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 150 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 151 | .CodeMirror-activeline-background {background: #e8f2ff;} 152 | 153 | /* STOP */ 154 | 155 | /* The rest of this file contains styles related to the mechanics of 156 | the editor. You probably shouldn't touch them. */ 157 | 158 | .CodeMirror { 159 | position: relative; 160 | overflow: hidden; 161 | background: white; 162 | } 163 | 164 | .CodeMirror-scroll { 165 | overflow: scroll !important; /* Things will break if this is overridden */ 166 | /* 30px is the magic margin used to hide the element's real scrollbars */ 167 | /* See overflow: hidden in .CodeMirror */ 168 | margin-bottom: -30px; margin-right: -30px; 169 | padding-bottom: 30px; 170 | height: 100%; 171 | outline: none; /* Prevent dragging from highlighting the element */ 172 | position: relative; 173 | } 174 | .CodeMirror-sizer { 175 | position: relative; 176 | border-right: 30px solid transparent; 177 | } 178 | 179 | /* The fake, visible scrollbars. Used to force redraw during scrolling 180 | before actual scrolling happens, thus preventing shaking and 181 | flickering artifacts. */ 182 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 183 | position: absolute; 184 | z-index: 6; 185 | display: none; 186 | } 187 | .CodeMirror-vscrollbar { 188 | right: 0; top: 0; 189 | overflow-x: hidden; 190 | overflow-y: scroll; 191 | } 192 | .CodeMirror-hscrollbar { 193 | bottom: 0; left: 0; 194 | overflow-y: hidden; 195 | overflow-x: scroll; 196 | } 197 | .CodeMirror-scrollbar-filler { 198 | right: 0; bottom: 0; 199 | } 200 | .CodeMirror-gutter-filler { 201 | left: 0; bottom: 0; 202 | } 203 | 204 | .CodeMirror-gutters { 205 | position: absolute; left: 0; top: 0; 206 | min-height: 100%; 207 | z-index: 3; 208 | } 209 | .CodeMirror-gutter { 210 | white-space: normal; 211 | height: 100%; 212 | display: inline-block; 213 | vertical-align: top; 214 | margin-bottom: -30px; 215 | } 216 | .CodeMirror-gutter-wrapper { 217 | position: absolute; 218 | z-index: 4; 219 | background: none !important; 220 | border: none !important; 221 | } 222 | .CodeMirror-gutter-background { 223 | position: absolute; 224 | top: 0; bottom: 0; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-elt { 228 | position: absolute; 229 | cursor: default; 230 | z-index: 4; 231 | } 232 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 233 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: contextual; 256 | font-variant-ligatures: contextual; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-rtl pre { direction: rtl; } 279 | 280 | .CodeMirror-code { 281 | outline: none; 282 | } 283 | 284 | /* Force content-box sizing for the elements where we expect it */ 285 | .CodeMirror-scroll, 286 | .CodeMirror-sizer, 287 | .CodeMirror-gutter, 288 | .CodeMirror-gutters, 289 | .CodeMirror-linenumber { 290 | -moz-box-sizing: content-box; 291 | box-sizing: content-box; 292 | } 293 | 294 | .CodeMirror-measure { 295 | position: absolute; 296 | width: 100%; 297 | height: 0; 298 | overflow: hidden; 299 | visibility: hidden; 300 | } 301 | 302 | .CodeMirror-cursor { 303 | position: absolute; 304 | pointer-events: none; 305 | } 306 | .CodeMirror-measure pre { position: static; } 307 | 308 | div.CodeMirror-cursors { 309 | visibility: hidden; 310 | position: relative; 311 | z-index: 3; 312 | } 313 | div.CodeMirror-dragcursors { 314 | visibility: visible; 315 | } 316 | 317 | .CodeMirror-focused div.CodeMirror-cursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-selected { background: #d9d9d9; } 322 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 323 | .CodeMirror-crosshair { cursor: crosshair; } 324 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 325 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 326 | 327 | .cm-searching { 328 | background-color: #ffa; 329 | background-color: rgba(255, 255, 0, .4); 330 | } 331 | 332 | /* Used to force a border model for a node */ 333 | .cm-force-border { padding-right: .1px; } 334 | 335 | @media print { 336 | /* Hide the cursor when printing */ 337 | .CodeMirror div.CodeMirror-cursors { 338 | visibility: hidden; 339 | } 340 | } 341 | 342 | /* See issue #2901 */ 343 | .cm-tab-wrap-hack:after { content: ''; } 344 | 345 | /* Help users use markselection to safely style text background */ 346 | span.CodeMirror-selectedtext { background: none; } 347 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/favicon.ico -------------------------------------------------------------------------------- /public/images/down-arrow-brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/images/down-arrow-brown.png -------------------------------------------------------------------------------- /public/images/right-arrow-brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/images/right-arrow-brown.png -------------------------------------------------------------------------------- /public/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/public/screenshot.png -------------------------------------------------------------------------------- /public/solarized.css: -------------------------------------------------------------------------------- 1 | /* 2 | Solarized theme for code-mirror 3 | http://ethanschoonover.com/solarized 4 | */ 5 | 6 | /* 7 | Solarized color palette 8 | http://ethanschoonover.com/solarized/img/solarized-palette.png 9 | */ 10 | 11 | .solarized.base03 { color: #002b36; } 12 | .solarized.base02 { color: #073642; } 13 | .solarized.base01 { color: #586e75; } 14 | .solarized.base00 { color: #657b83; } 15 | .solarized.base0 { color: #839496; } 16 | .solarized.base1 { color: #93a1a1; } 17 | .solarized.base2 { color: #eee8d5; } 18 | .solarized.base3 { color: #fdf6e3; } 19 | .solarized.solar-yellow { color: #b58900; } 20 | .solarized.solar-orange { color: #cb4b16; } 21 | .solarized.solar-red { color: #dc322f; } 22 | .solarized.solar-magenta { color: #d33682; } 23 | .solarized.solar-violet { color: #6c71c4; } 24 | .solarized.solar-blue { color: #268bd2; } 25 | .solarized.solar-cyan { color: #2aa198; } 26 | .solarized.solar-green { color: #859900; } 27 | 28 | /* Color scheme for code-mirror */ 29 | 30 | .cm-s-solarized { 31 | line-height: 1.45em; 32 | color-profile: sRGB; 33 | rendering-intent: auto; 34 | } 35 | .cm-s-solarized.cm-s-dark { 36 | color: #839496; 37 | background-color: #002b36; 38 | text-shadow: #002b36 0 1px; 39 | } 40 | .cm-s-solarized.cm-s-light { 41 | background-color: #fdf6e3; 42 | color: #657b83; 43 | text-shadow: #eee8d5 0 1px; 44 | } 45 | 46 | .cm-s-solarized .CodeMirror-widget { 47 | text-shadow: none; 48 | } 49 | 50 | .cm-s-solarized .cm-header { color: #586e75; } 51 | .cm-s-solarized .cm-quote { color: #93a1a1; } 52 | 53 | .cm-s-solarized .cm-keyword { color: #cb4b16; } 54 | .cm-s-solarized .cm-atom { color: #d33682; } 55 | .cm-s-solarized .cm-number { color: #d33682; } 56 | .cm-s-solarized .cm-def { color: #2aa198; } 57 | 58 | .cm-s-solarized .cm-variable { color: #839496; } 59 | .cm-s-solarized .cm-variable-2 { color: #b58900; } 60 | .cm-s-solarized .cm-variable-3, .cm-s-solarized .cm-type { color: #6c71c4; } 61 | 62 | .cm-s-solarized .cm-property { color: #2aa198; } 63 | .cm-s-solarized .cm-operator { color: #6c71c4; } 64 | 65 | .cm-s-solarized .cm-comment { color: #586e75; font-style:italic; } 66 | 67 | .cm-s-solarized .cm-string { color: #859900; } 68 | .cm-s-solarized .cm-string-2 { color: #b58900; } 69 | 70 | .cm-s-solarized .cm-meta { color: #859900; } 71 | .cm-s-solarized .cm-qualifier { color: #b58900; } 72 | .cm-s-solarized .cm-builtin { color: #d33682; } 73 | .cm-s-solarized .cm-bracket { color: #cb4b16; } 74 | .cm-s-solarized .CodeMirror-matchingbracket { color: #859900; } 75 | .cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; } 76 | .cm-s-solarized .cm-tag { color: #93a1a1; } 77 | .cm-s-solarized .cm-attribute { color: #2aa198; } 78 | .cm-s-solarized .cm-hr { 79 | color: transparent; 80 | border-top: 1px solid #586e75; 81 | display: block; 82 | } 83 | .cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; } 84 | .cm-s-solarized .cm-special { color: #6c71c4; } 85 | .cm-s-solarized .cm-em { 86 | color: #999; 87 | text-decoration: underline; 88 | text-decoration-style: dotted; 89 | } 90 | .cm-s-solarized .cm-error, 91 | .cm-s-solarized .cm-invalidchar { 92 | color: #586e75; 93 | border-bottom: 1px dotted #dc322f; 94 | } 95 | 96 | .cm-s-solarized.cm-s-dark div.CodeMirror-selected { background: #073642; } 97 | .cm-s-solarized.cm-s-dark.CodeMirror ::selection { background: rgba(7, 54, 66, 0.99); } 98 | .cm-s-solarized.cm-s-dark .CodeMirror-line::-moz-selection, .cm-s-dark .CodeMirror-line > span::-moz-selection, .cm-s-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(7, 54, 66, 0.99); } 99 | 100 | .cm-s-solarized.cm-s-light div.CodeMirror-selected { background: #eee8d5; } 101 | .cm-s-solarized.cm-s-light .CodeMirror-line::selection, .cm-s-light .CodeMirror-line > span::selection, .cm-s-light .CodeMirror-line > span > span::selection { background: #eee8d5; } 102 | .cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-ligh .CodeMirror-line > span::-moz-selection, .cm-s-ligh .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; } 103 | 104 | /* Editor styling */ 105 | 106 | 107 | 108 | /* Little shadow on the view-port of the buffer view */ 109 | .cm-s-solarized.CodeMirror { 110 | -moz-box-shadow: inset 7px 0 12px -6px #000; 111 | -webkit-box-shadow: inset 7px 0 12px -6px #000; 112 | box-shadow: inset 7px 0 12px -6px #000; 113 | } 114 | 115 | /* Remove gutter border */ 116 | .cm-s-solarized .CodeMirror-gutters { 117 | border-right: 0; 118 | } 119 | 120 | /* Gutter colors and line number styling based of color scheme (dark / light) */ 121 | 122 | /* Dark */ 123 | .cm-s-solarized.cm-s-dark .CodeMirror-gutters { 124 | background-color: #073642; 125 | } 126 | 127 | .cm-s-solarized.cm-s-dark .CodeMirror-linenumber { 128 | color: #586e75; 129 | text-shadow: #021014 0 -1px; 130 | } 131 | 132 | /* Light */ 133 | .cm-s-solarized.cm-s-light .CodeMirror-gutters { 134 | background-color: #eee8d5; 135 | } 136 | 137 | .cm-s-solarized.cm-s-light .CodeMirror-linenumber { 138 | color: #839496; 139 | } 140 | 141 | /* Common */ 142 | .cm-s-solarized .CodeMirror-linenumber { 143 | padding: 0 5px; 144 | } 145 | .cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; } 146 | .cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; } 147 | .cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; } 148 | 149 | .cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text { 150 | color: #586e75; 151 | } 152 | 153 | /* Cursor */ 154 | .cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; } 155 | 156 | /* Fat cursor */ 157 | .cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #77ee77; } 158 | .cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #77ee77; } 159 | .cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; } 160 | .cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; } 161 | 162 | /* Active line */ 163 | .cm-s-solarized.cm-s-dark .CodeMirror-activeline-background { 164 | background: rgba(255, 255, 255, 0.06); 165 | } 166 | .cm-s-solarized.cm-s-light .CodeMirror-activeline-background { 167 | background: rgba(0, 0, 0, 0.06); 168 | } 169 | -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/storage/.keep -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/ast_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AstControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get ast_index_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/gist_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GistControllerTest < ActionDispatch::IntegrationTest 4 | test "should get show" do 5 | get gist_show_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/welcome_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class WelcomeControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get welcome_index_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/models/.keep -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/test/system/.keep -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require_relative '../config/environment' 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/tmp/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajasegar/ruby-ast-explorer/e5c3a402bd1195248d0d2d7b97856c1fd286e0d3/vendor/.keep --------------------------------------------------------------------------------