hyperstack-org / hyperstack Goto Github PK
View Code? Open in Web Editor NEWHyperstack ALPHA https://hyperstack.org
Home Page: https://docs.hyperstack.org
License: MIT License
Hyperstack ALPHA https://hyperstack.org
Home Page: https://docs.hyperstack.org
License: MIT License
check orm, hopefully easy
Sample app which uses pre-rendering for a simple Component (using hyperloop-legacy gems)
Error: window is not defined
on startup.
Sample here: https://github.com/barriehadfield/prerender-not-working
To reproduce:
bundle
yarn
foreman start
Perhps there is a missing documentation step to stop the import of Jquery? I tried removing it from application.js but that made no difference.
rails keep models in app/models
hyper keeps models in app/hyperstack/models
we need to guard models anyway, Hyperstack.valid_* is used, can we use that in the build process, so that models can stay in app/models and only those in Hyperstack.valid_* get included on the client?
Take this example from the new website:
class UsingState < Hyperloop::Component
state :show # true means show the input field, false means hide it
state :value # current value of input field as user types
render(DIV) do
button.on(:click) { mutate.show !state.show }
DIV { example } if state.show
end
def button
BUTTON(class: 'ui primary button') do
state.show ? "Hide" : "Show"
end
end
def example
BR()
DIV(class: 'ui input') do
INPUT(type: :text).on(:change) do |e|
mutate.value e.target.value
end
end
H3 { "#{state.value}" }
end
end
Right now we consider each store and component to have a "state" object.
You access individual states in that object using the state
and mutate
methods.
This follows react closely, but from a ruby/object oriented perspective its backwards.
Rather we should consider each "state" to be an object, which has reader, writer and mutator (and possibly other) methods associated.
Here is the same code written that way:
class UsingState < Hyperloop::Component
state :show # true means show the input field, false means hide it
state :value # current value of input field as user types
render(DIV) do
button.on(:click) { show.state = !show.state }
DIV { example } if show.state
end
def button
BUTTON(class: 'ui primary button') do
show.state ? "Hide" : "Show"
end
end
def example
BR()
DIV(class: 'ui input') do
INPUT(type: :text).on(:change) do |e|
value.state = e.target.value
end
end
H3 { "#{value.state}" }
end
end
In detail:
currently there are basically three methods associated with each state:
a reader: state.show
for example
a writer: mutate.show(false)
a mutator: mutate.show
<- no parameter switches behavior under the hood.
New approach:
the reader: show.state
the writer: show.state=
the mutator: show.mutate
Other advantages:
now that the state is the object, it can handy methods associated cleanly. For example we can add set?
and toggle!
methods to nicely deal with boolean states:
class UsingState < Hyperloop::Component
state :show # true means show the input field, false means hide it
state :value # current value of input field as user types
render(DIV) do
button.on(:click) { show.toggle! }
DIV { example } if show.set?
end
def button
BUTTON(class: 'ui primary button') do
show.set? ? "Hide" : "Show"
end
end
def example
BR()
DIV(class: 'ui input') do
INPUT(type: :text).on(:change) do |e|
value.state = e.target.value
end
end
H3 { "#{value.state}" }
end
end
What about the mutator???
When dealing with scalar values like the above example you don't "mutate" the state, but rather just assign a whole new value to it. In cases where the state is not a scalar (like an array, hash or other object) and you want to mutate it, then indeed you would say list_of_items.mutate << new_item
.
Overall its much clearer, and under the hood the implementation will be much cleaner and more effecient.
Downside?
You have to declare the states. Currently you can just use a state on the fly and it gets created inside the state object. In the above approach the system has to know before hand that the state exists. The good news is this means if you mistype the name of a state then you would get the normal method missing error.
Not symetrical with params. I would at least like to try also getting rid of the params object and just declaring methods for each param. But its a separate issue.
Next Steps?
Both systems can coexist, so its a matter of providing a mixin. The question is how to try this out:
a. Provide a mixin that adds the new behavior
b. Build the new behavior in, and provide a mixin for the old behavior.
I think the choice will depend on how people vote for the above. If most people like it, we go with (a), if people are concerned we go with (b).
So please THUMBS UP/ THUMBS DOWN (if you have to :-) ) and comment...
easy
each time lifecycle methods are executed the add more blocks to the lifecycle stack.
When the hot reloader reloads a file, all the lifecycle methods are added again.
There is no easy solution as hot reloader only reloads the single file that changed. So if we remove all lifecycle methods then only the ones for the file being reloaded will get added back in.
The solution may be here: https://github.com/stacktracejs/stacktrace.js
Using this we could track which file defined a life cycle method, and then remove just those methods when the file was reloaded.
simplify, simplify, simplify and avoid webdrivers
i.e. ref: method(:foo)
React.render
tries to figure out how to return the component instance just rendered but it is no longer possible since ReactDOM.render no longer returns the rendered element.
If you need to get a hold of the component rendered you have to use a ref call back like this:
test_div.render(SPAN, id: :render_test_span, ref: method(:grab_ref)) { 'hello' }
your grab_ref
method will be called sometime after the component is mounted (its now asyncronous)
instead of params.foo
we should just say @foo
why?
Don't worry - you can get the legacy behavior by adding param_accessor_style :legacy
to any component (including the base component.) You can also say param_accessor_style :both
to get both behaviors. If you need to switch back to the now standard behavior say param_accessor_style :hyperstack
So for example you can say param_accessor_style :legacy
in your base component, and then as you update components to new style in that component say param_accessor_style :hyperstack
.
The param declaration macro is unchanged, but has a new alias
option.
param :foo, alias: :internal_name
If you like to distinguish between params and other instance variables you can use this feature like this:
param :foo, alias: :param_foo
for example
see #52 for a complete example
the owl build process has several components:
opal-webpack-loader npm
opal-webpack-resolver-plugin npm
opal-webpack-compile-server gem
opal-webpack asset view helpers (currently in the owl example app)
put them all together in a opal-webpack-loader repo
which build the npms and gems
and provides the helpers
the opal-webpack-resolver-plugin should just be another export of the opal-webpack-loader npm
piece of cake
We have several vulnerabilities from old gems, these must be updated when Hyperloop moves here
https://github.com/hyperstack-org/hyperstack/network/dependencies
Hyperloop Operations preform may function, and some find this confusing. Additionally, the Ulysses branch implements a new DSL.
We need to decide if we are keeping Operations as they are or simplifying them.
Discussion document here: https://docs.google.com/document/d/1bT0YBjjwW_lP6F3aVr5SyZrVjSUDsgK5EIXeVe7gZO8/edit#
this library (Polaris) has a component nested inside another component Polaris.Layout.Section
, so in hyperstack i think should translate to Polaris::Layout::Section
, right?
this is valid code
Polaris::Layout::Section() do
# more stuff...
end
and this code throws an error
Polaris::Layout::Section(some_prop: "can't handle it") do
# more stuff
end
the error
Uncaught error: Section: undefined method `Section' for Object
it handles the nested component but goes crazy when you try to pass props to it.
partially fixed, need to fix for relations
Currently to declare an event (i.e. a callback that can be attached via the .on
method you need some complicated param declaration syntax like this:
param :on_saved, allow_nil: true, type: Proc, default: nil
# declares the 'saved' event
Whew
Now you can say:
raises :saved
This will create a saved!
method within your component to raise the event.
You can override the internal method name using the alias
option:
`raises :saved, alias: :save_it # defines the save_it method instead of saved!
For non-standard event names you can use the same syntax as the .on
method uses, i.e.
raises '<SAVE>', alias: saved! # you must supply an alias for non-standard names
To attach a SAVE
handler you say `.on('') (as you do today)
For example here is a sample component (see #51 for why there are no more params.
prefixes)
class EditItem
include Hyperstack::Component
include Hyperstack::Observable
param :todo
others :others
raises :save
raises :cancel
after_mount { ::Element[dom_node].focus }
render do
INPUT(
@others,
defaultValue: @todo.title,
placeholder: "What is left to do today?",
key: @todo
)
.on(:key_down) do |evt|
next unless evt.key_code == 13
@todo.update(title: evt.target.value)
save!
end
.on(:blur) { cancel! }
end
end
Hyperstack like Hyperloop's Primary Goal is developer productivity. This is achieved through the following approaches:
What I'm testing at the moment is what if you want to include a gem from Git that is in a folder.
This can be done by using:
#https://bundler.io/guides/git.html
gem 'hyperstack-react', :git => 'https://github.com/hyperstack-org/hyperstack', :glob => 'ruby/hyperstact-react/*.gemspec', branch: 'edge'
Currently I'm checking how to manage the version number (share for all languages) and the CI setup.
In favour of the speed I want to create a matrix for each component, this allows it to test all gems in parallel but the whole job will be validated over all individual jobs.
Also want to test several Ruby versions to see if we might brake something in the future of a version in the past. Due to the project not being a Ruby only project I have to do some magic to resolve this, but all doable.
On tags all will be released to the appropriate destinations, gems, npm, etc... with all the same version for consistency and it will be a hard lock on the version '= 2.0.0' so that upgrades go in lockstep.
So some testing to do until I want to finalize it.
Also this needs to be explained in the README so that people that end up on the repo understands it.
Create Repo labels for each component
new file hot loading doesnt work so much for new files created
need to transfer the require tree knowledge of the compile server to webpack
The existing Policy documentation focuses on defining broadcast policy and it is not clear that it can also be used to define application (access to data Policy). The documentation needs to be clearer about this.
use node vm context based on request path
and translate to to self.method(symbol)
This should NOT be done in hyperloop legacy as currently you can say:
ref: :foo which means set the refs.foo to the ref. However this behavior is being deprecated in react, so in hyperstack we treat symbols as method names.
To implement this is a little trick, but we can use __opalInstance to find the opal instance of and apply the symbol to it.
and while we are at it if we say ref: foo
where foo is a state then we should be smart and take this to mean assign the ref to the state!!! (Note new state syntax again makes this possible.)
For example
mount 'Foo' do
class Foo
include Hyperstack::Component
render do
DIV()
end
end
end
The expression DIV()
gets "decompiled" to simply DIV
(constant not method call).
This must be a problem in the parser_gem. whitequark/parser#529
Create Rails template for installation which gets its gems from the Hyperstack edge branch
Add HelloWorld Component and tests
recently a developer accidentally created a server op named Foo, and also named the application Foo. This creates a module named Foo, and never autoloads the serverop.
The security system barfs, but the error message is useless to debug the problem.
When we have security violations, we should dump all possible information on the server if in dev mode, so the problem can be analyzed.
for speed
Our sample rails app and rails template bypass Webpacker and replace the Webpacker development.js and production.js with standard Webpack config.
Webpacker is a part of Rails now and our Rails integration should use it.
I spent a day trying to get this working but have not succeeded yet. If anyone cares to give this a try, my test repo is here:
https://github.com/barriehadfield/rails-webpacker-hyperstack
It should be as simple as:
const OpalWebpackResolverPlugin = require('opal-webpack-resolver-plugin'); // to resolve ruby files
environment.loaders.append('rb', {
test: /\.(rb|js.rb)$/,
use: 'opal-webpack-loader'
})
environment.plugins.append(
'rb',
new OpalWebpackResolverPlugin('resolve', 'resolved')
)
But it is not.
Example here: https://gist.github.com/andrewprogers/65f0228c262fbe8e1efe767527540aec
or hyperstack-rails-config or something
its just a few things in gems
maybe
check other frameworks to
Current direction:
Hyperstack::Component::Mixin # include this to create a component class
class MyComponent
include Hyperstack::Component::Mixin
end
# typically we expect the application to create a master HyperComponent base class
# using the mixin similar to ApplicationController or ApplicationRecord
Hyperstack::Component::ReactAPI # public module giving low level access to React methods
Hyperstack::Component::Internal::RenderingContext # a private internal module
# Above applies to store as well, i.e. Hyperstack::Store::Mixin
# Models are slightly different because we already have an ApplicationRecord structure:
class ApplicationRecord < ActiveRecord::Base
# makes ApplicationRecord and children isomorphic
include Hyperstack::Model::ActiveRecordAdapter
...
end
# in future we will have adapters for other ORMs and dbs as well.
# Hyperstack::Operation is also different, in that it is not sensible to use it as a mixin. Instead
# Hyperstack::Operation is the base class of all operations.
As I am converting current code to this format I am thinking that this name structure is slightly redundant and that we would do well to drop the Mixin, thus we would have:
Hyperstack::Component # include this to create a component class
class MyComponent
include Hyperstack::Component
end
# typically we expect the application to create a master HyperComponent base class
# using the mixin similar to ApplicationController or ApplicationRecord
Hyperstack::Component::ReactAPI # same as currently proposed but...
# because ReactAPI is now a direct descendent of the mixin inside of the application components
# you can directly say ReactAPI.
Hyperstack::Internal::Component::RenderingContext # swap Internal and Component to
# avoid polluting application components name space
# Above applies to store as well, i.e. Hyperstack::Store is the mixin
# Models don't change from current proposal
# Hyperstack::Operation could stay the same but because its a class (not a module) we could
# change it to Hyperstack::Operation::Base, and have its predefined subclasses be:
# Hyperstack::Operation::ServerOp and Hyperstack::Operation::ControllerOp
Its just an idea...
The desired state (that used to work) was if you required opal-jquery before loading hyper-component, then hyper-component would extend the opal-jquery Element class like this:
Element.class_eval do
... add stuff
end if Object.const_defined?('Element')
Between the new hyperstack loader, and opal 0.11 require mechanism Element is not getting defined until too late.
I can't find a fix, so will just make it so you can require this code manually after requiring jQuery.
Keep problem open unless we are sure a better solution can't be found.
monkey business
Policies are an important top-level Hyperstack concept (as a part of the Component, Operations, Models, Policies, Stores architecture).
The new HyperModel (based on Ulysses HyperResource) needs to use this Policy definition.
Policies need to support two important use cases:
hyper-model needs update, rest needs everything
Following the README, after setting things up:
rails -v
# Rails 5.2.1
rails new MyApp -m https://rawgit.com/hyperstack-org/hyperstack/edge/install/rails-webpacker.rb
cd MyApp
bundle exec rails g model thing name:string
bundle exec rails db:migrate
mv app/models/application_record.rb app/hyperloop/models/
mv app/models/thing.rb app/hyperloop/models/
cat app/hyperloop/components/hello_world.rb
:
class HelloWorld < Hyperloop::Component
render(DIV) do
H1 { "Hello this should be 0 - #{Thing.count}" }
end
end
This policy stuff is where it seems to be rails/js illiteracy on my part. Not so cursory examination of this subject (an hour with google/stackexchange) led me to believe that all should work.
cat config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.connect_src :self, :https, :data, :unsafe_inline, "http://localhost:*", "ws://localhost:*" if Rails.env.development?
end
PORT=3112 RAILS_ENV=development foreman start
Browsing to http://localhost:3112
Produces:
Hello this should be 0 - 1
Expected:
Hello this should be 0 - 0
Browser console logs (Just in-case it's a bug and not my own fault):
require 'opal'
require 'browser' # CLIENT ONLY
require 'hyperloop-config'
require 'hyperloop/autoloader'
require 'hyperloop/autoloader_starter'
require 'hyper-router/react-router-source'
require 'react_ujs'
require 'reactrb/auto-import'
require 'hyper-router'
require 'hyper-model'
require 'hyper-store'
require 'browser/delay' # CLIENT ONLY
require 'hyper-operation'
require 'opal_hot_reloader'
Object freezing is not supported by Opal
require 'components/hello_world'
require 'components/hot_loader'
Hot-Reloader connecting to localhost:25222
require 'models/application_record'
require 'models/thing'
************************ React Browser Context Initialized ****************************
Reactive record prerendered data being loaded: <unavailable>
Server Fetching: <unavailable>
Fetch failed isomorphic_helpers.rb:55:8
$$log isomorphic_helpers.rb:55:8
Opal.send http://localhost:3112/assets/corelib/runtime.self-...js:1605:14
$$log isomorphic_helpers.rb:200:8
TMP_20 http://localhost:3112/assets/reactive_record/active_record/reactive_record/isomorphic_base.self-...js:252:13
Opal.yieldX http://localhost:3112/assets/corelib/runtime.self-...js:1411:12
$$call http://localhost:3112/assets/corelib/proc.self-...js:66:20
$$reject promise.rb:140:16
TMP_20 promise.rb:160:23
Opal.yield1 http://localhost:3112/assets/corelib/runtime.self-...js:1388:14
$$each http://localhost:3112/assets/corelib/array.self-...js:1068:21
Opal.send http://localhost:3112/assets/corelib/runtime.self-...js:1605:14
TMP_Promise_reject$B_21 promise.rb:160:6
$$reject http://localhost:3112/assets/hyper-operation/promise.self-...js:226:11
TMP_20 promise.rb:160:23
Opal.yield1 http://localhost:3112/assets/corelib/runtime.self-...js:1388:14
$$each http://localhost:3112/assets/corelib/array.self-...js:1068:21
Opal.send http://localhost:3112/assets/corelib/runtime.self-...js:1605:14
TMP_Promise_reject$B_21 promise.rb:160:6
$$reject http://localhost:3112/assets/hyper-operation/promise.self-...js:226:11
TMP_20 promise.rb:160:23
Opal.yield1 http://localhost:3112/assets/corelib/runtime.self-...js:1388:14
$$each http://localhost:3112/assets/corelib/array.self-...js:1068:21
Opal.send http://localhost:3112/assets/corelib/runtime.self-...js:1605:14
TMP_Promise_reject$B_21 promise.rb:160:6
$$reject http://localhost:3112/assets/hyper-operation/promise.self-...js:226:11
TMP_20 promise.rb:160:23
Opal.yield1 http://localhost:3112/assets/corelib/runtime.self-...js:1388:14
$$each http://localhost:3112/assets/corelib/array.self-...js:1068:21
Opal.send http://localhost:3112/assets/corelib/runtime.self-...js:1605:14
TMP_Promise_reject$B_21 promise.rb:160:6
$$reject http://localhost:3112/assets/hyper-operation/promise.self-...js:226:11
TMP_15 http.rb:277:12
Opal.yieldX http://localhost:3112/assets/corelib/runtime.self-...js:1411:12
$$call http://localhost:3112/assets/corelib/proc.self-...js:66:20
$$fail http.rb:305:6
$$send/xhr.onreadystatechange http://localhost:3112/assets/hyper-operation/http.self-...js:130:22
All I'm trying to do is play around with hyperstack goodies, but this little snafu holds me back. Any and all help is appreciated!
in otherwords
ReactAPI.create_element(:div, &children) fails
but
ReactAPI.create_element(:div) { 'hello' }
works fine.
So does
Hyperstack::Component::Internal::RenderingContext.render(:div, &children)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.