Code Monkey home page Code Monkey logo

hyperstack's People

Contributors

adamcreekroad avatar ajjahn avatar anithri avatar barriehadfield avatar catmando avatar comexpressao avatar fkchang avatar fzingg avatar gabrielrios avatar gitter-badger avatar hayley avatar janbiedermann avatar jgaskins avatar johan-smits avatar johansmitsnl avatar josephgrossberg avatar kwhittington avatar mathisto avatar maxdignan avatar michaelsp avatar mpantel avatar noma4i avatar phaedryx avatar psmir avatar sampsoncrowley avatar sfcgeorge avatar tim-blokdijk avatar tsuka avatar wied03 avatar zetachang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hyperstack's Issues

Pre-render not working

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.

can the owl build process refer Hyper.valid_* for models?

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?

New syntax for state and params

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...

hot reloader doesn't deal well with lifecycle methods

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.

`Uncaught error: TypeError: Cannot read property '__opalInstance' of null` when React.render called

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)

get rid of params wrapper - directly access params via instance vars

instead of params.foo we should just say @foo

why?

  1. its 25 times faster
  2. its much more consistent - all component state is in instance vars regardless of how it is set
  3. the params wrapper method is not private (but it should be)
  4. less typing
  5. it just feels more natural to ruby.

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

consolidate OWL

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

Fails to pass props to a React::NativeLibrary nested component

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.

add nicer event declaration syntax

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

Top level project goals (which last beyond this first release)

Hyperstack like Hyperloop's Primary Goal is developer productivity. This is achieved through the following approaches:

  • One language for all code (which is Ruby for now)
  • Avoiding repetitive and boilerplate code.
  • Use convention over configuration where possible.
  • Class definitions that are internally consistent, and stable over time.
  • Class definitions that act like existing well known structures.
  • As friction free as possible configuration and development tool chain.
  • Good documentation, and a helpful community, both for users, and contributors.
  • A professional, well structured code base, complete test coverage, and automatic continuous integration.
  • Implementation should be as efficient both in speed and space as practical given the above.

merge all repos in the new tree structure

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

Refine Policies documentation

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.

allow `refs` special param to receive a symbol

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.)

better info server side on security breaches

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.

Use Webpacker for Rails integration

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

class rename details

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...

hyper-component is not adding extensions to opal-jquery Element class

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.

Implement Hyperloop style Policies in Ulysses branch to work with the new HyperModel

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:

  • Regulating access to data (based on Pundit)
  • Regulating data broadcasts

'Fetch failed' for a model

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.