Title: automatically add dependencies for server side files. · Issue #361 · hyperstack-org/hyperstack · GitHub
Open Graph Title: automatically add dependencies for server side files. · Issue #361 · hyperstack-org/hyperstack
X Title: automatically add dependencies for server side files. · Issue #361 · hyperstack-org/hyperstack
Description: You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition. This is especially useful for Server Operations and Active Record models. Here ...
Open Graph Description: You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition. This is especially useful for Server Op...
X Description: You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition. This is especially useful for Server Op...
Opengraph URL: https://github.com/hyperstack-org/hyperstack/issues/361
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"automatically add dependencies for server side files.","articleBody":"You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition.\r\n\r\nThis is especially useful for Server Operations and Active Record models.\r\n\r\nHere is a sample directory structure, containing an application component, a model called Person, and an operation called PalindromeChecker. We also have a model called Orderbut it is only visible on the server. More on that later.\r\n\r\n```\r\napp -\r\n hyperstack -\r\n components -\r\n app.rb\r\n models -\r\n application_record.rb\r\n person.rb\r\n operations\r\n palindrome_checker.rb\r\n models -\r\n person.rb\r\n order.rb\r\n operations\r\n palindrome_checker.rb\r\n```\r\n\r\nNote that the `person.rb` and `palindrome_checker.rb` files exist in two places: one in the `hyperstack` directory, and the other in the outer app directory.\r\n\r\nFiles in the `hyperstack` `models`, `operations` and `shared` directories will be executed on both the client and the server. This is how a method can be understood by both the client and the server.\r\n\r\nFiles in the outer app subdirectory are only recognized by the Rails server. So any information in those files, will not be seen on the client.\r\n\r\nAs a side note files in the `hyperstack` `/components`, `/libs`, and in fact, any other directories except `/models`, `/operations`, and `/shared` directories are only seen on the client. So there three kinds of files: Server-only (those in the outer `app` directories), server and client (those in `hyperstack` `/models`, `/operations`, and `/shared` directories, and client-only (any other `hyperstac`k subdirectories)\r\n\r\nHere is how we can use this difference to nicely structure our code that is sharing information between the server and client. We can put class definitions that are only relevant to the server in the outer `app` directories, and put class definitions that are used by both the client and server in the `hyperstack` directories.\r\n\r\nSo for example here are the two PalindromeChecker files:\r\n\r\n```ruby\r\n# app/hyperstack/operations/palindrome_checker.rb\r\nclass PalindromeChecker \u003c Hyperstack::ServerOp\r\n param :string \r\nend\r\n```\r\n\r\n```ruby\r\n# app/operations/palindrome_checker.rb\r\nclass PalindromeChecker \u003c Hyperstack::ServerOp\r\n param :acting_user, nils: true\r\n param :string \r\n\r\n step { params.string = params.string.gsub(/\\s+/, '').downcase }\r\n step { params.string.reverse == params.string }\r\nend\r\n```\r\n\r\nAll the client-side needs to know is that `PalindromeChecker` is a subclass of `Hyperstack::ServerOp`, and that it accepts a single param - a string to be checked. This definition is also available to the server, and will ensure that the type signature matches between the isomorphic (client and server) definition and the server-only definition.\r\n\r\nThe server-side file has all the implementation details including the acting_user declaration which can be used to validate who can be calling this operation. (In this case, we don't require an `acting_user` hence nils are allowed)\r\n\r\nLet's look at an Active Record example:\r\n\r\n```ruby\r\n# app/hyperstack/models/person.rb\r\nclass Person \u003c ApplicationRecord \r\n def full_name\r\n \"#{first_name} #{last_name}\"\r\n end\r\n \r\n scope :children, -\u003e() { where(\"birthday \u003e ?\", Time.now - 21.years) }\r\n scope :adults, -\u003e() { where(\"birthday \u003c= ?\", Time.now - 21.years) }\r\nend\r\n```\r\n\r\n```ruby\r\n# app/hyperstack/models/person.rb \r\nclass Person \u003c ApplicationRecord \r\n has_many :orders\r\nend\r\n```\r\n\r\nHere we have a helper method and some scopes defined in the isomorphic definition: These are available to both the client and the server. \r\n\r\nHowever the server-side file also has relationships with another model, and because these are defined on the server-side they are not visible to the client.\r\n\r\nIn order for this to work, you do need to add a small snippet of code to your `initializers/hyperstack.rb` file which trains Rails to look in both directories for any constant definition. This code will be released into Hyperstack soon, but in the meantime feel free to use this snippet and clean up your classes:\r\n\r\n```ruby\r\n# Add to your `initializers/hyperstack.rb file\r\nmodule ActiveSupport\r\n module Dependencies\r\n HYPERSTACK_DIR = 'hyperstack' \r\n class \u003c\u003c self\r\n alias original_require_or_load require_or_load\r\n\r\n # before requiring_or_loading a file, first check if\r\n # we have the same file in the server side directory\r\n # and add that as a dependency\r\n\r\n def require_or_load(file_name, const_path = nil)\r\n add_server_side_dependency(file_name)\r\n original_require_or_load(file_name, const_path)\r\n end\r\n\r\n # search the filename path from the end towards the beginning\r\n # for the HYPERSTACK_DIR directory. If found, remove it from\r\n # the filename, and if a ruby file exists at that location then\r\n # add it as a dependency\r\n\r\n def add_server_side_dependency(file_name)\r\n path = File.expand_path(file_name.chomp('.rb'))\r\n .split(File::SEPARATOR).reverse\r\n hs_index = path.find_index(HYPERSTACK_DIR)\r\n\r\n return unless hs_index # no hyperstack directory here\r\n\r\n new_path = (path[0..hs_index - 1] + path[hs_index + 1..-1]).reverse\r\n load_path = new_path.join(File::SEPARATOR)\r\n\r\n return unless File.exist? \"#{load_path}.rb\"\r\n\r\n require_dependency load_path\r\n end\r\n end\r\n end\r\nend\r\n```","author":{"url":"https://github.com/catmando","@type":"Person","name":"catmando"},"datePublished":"2021-02-25T01:51:45.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/361/hyperstack/issues/361"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:93e6949e-7cb2-5abd-f455-bcc849dd7749 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 8F50:73BCE:16B4270:1CEB8DE:6991838B |
| html-safe-nonce | 7ae6deb3bc78dbde9bbafd3eb0de9f7082607146ea18e4cc0a5a3ac724df4ed1 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4RjUwOjczQkNFOjE2QjQyNzA6MUNFQjhERTo2OTkxODM4QiIsInZpc2l0b3JfaWQiOiIxNjk1ODg2NzM2NDUyODQ2NDc1IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 62d8c7b697ad6a952df7e6bd1557cbbc101360aed39ac9be194dedaf47395ccb |
| hovercard-subject-tag | issue:815993178 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/hyperstack-org/hyperstack/361/issue_layout |
| twitter:image | https://opengraph.githubassets.com/b8f4cb67295df620b3f57ee7e7f3c683dc5b05da9bfbac2406335504fbd99e28/hyperstack-org/hyperstack/issues/361 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/b8f4cb67295df620b3f57ee7e7f3c683dc5b05da9bfbac2406335504fbd99e28/hyperstack-org/hyperstack/issues/361 |
| og:image:alt | You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition. This is especially useful for Server Op... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | catmando |
| hostname | github.com |
| expected-hostname | github.com |
| None | 42c603b9d642c4a9065a51770f75e5e27132fef0e858607f5c9cb7e422831a7b |
| turbo-cache-control | no-preview |
| go-import | github.com/hyperstack-org/hyperstack git https://github.com/hyperstack-org/hyperstack.git |
| octolytics-dimension-user_id | 34562730 |
| octolytics-dimension-user_login | hyperstack-org |
| octolytics-dimension-repository_id | 145879576 |
| octolytics-dimension-repository_nwo | hyperstack-org/hyperstack |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 145879576 |
| octolytics-dimension-repository_network_root_nwo | hyperstack-org/hyperstack |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 848bc6032dcc93a9a7301dcc3f379a72ba13b96e |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width