react.rb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +30 -0
 - data/Gemfile +2 -0
 - data/Gemfile.lock +48 -0
 - data/LICENSE +19 -0
 - data/README.md +166 -0
 - data/config.ru +15 -0
 - data/example/react-tutorial/Gemfile +6 -0
 - data/example/react-tutorial/Gemfile.lock +45 -0
 - data/example/react-tutorial/README.md +8 -0
 - data/example/react-tutorial/_comments.json +6 -0
 - data/example/react-tutorial/config.ru +55 -0
 - data/example/react-tutorial/example.rb +104 -0
 - data/example/react-tutorial/public/base.css +62 -0
 - data/example/todos/Gemfile +11 -0
 - data/example/todos/Gemfile.lock +84 -0
 - data/example/todos/README.md +37 -0
 - data/example/todos/Rakefile +8 -0
 - data/example/todos/app/application.rb +22 -0
 - data/example/todos/app/components/app.react.rb +61 -0
 - data/example/todos/app/components/footer.react.rb +31 -0
 - data/example/todos/app/components/todo_item.react.rb +46 -0
 - data/example/todos/app/components/todo_list.react.rb +25 -0
 - data/example/todos/app/models/todo.rb +19 -0
 - data/example/todos/config.ru +14 -0
 - data/example/todos/index.html.haml +16 -0
 - data/example/todos/spec/todo_spec.rb +28 -0
 - data/example/todos/vendor/base.css +410 -0
 - data/example/todos/vendor/bg.png +0 -0
 - data/example/todos/vendor/jquery.js +4 -0
 - data/lib/react.rb +16 -0
 - data/lib/react/api.rb +95 -0
 - data/lib/react/callbacks.rb +35 -0
 - data/lib/react/component.rb +197 -0
 - data/lib/react/element.rb +63 -0
 - data/lib/react/event.rb +76 -0
 - data/lib/react/ext/hash.rb +9 -0
 - data/lib/react/ext/string.rb +8 -0
 - data/lib/react/top_level.rb +50 -0
 - data/lib/react/validator.rb +65 -0
 - data/lib/react/version.rb +3 -0
 - data/react.rb.gemspec +24 -0
 - data/spec/callbacks_spec.rb +107 -0
 - data/spec/component_spec.rb +556 -0
 - data/spec/element_spec.rb +60 -0
 - data/spec/event_spec.rb +22 -0
 - data/spec/react_spec.rb +168 -0
 - data/spec/reactjs/index.html.erb +10 -0
 - data/spec/spec_helper.rb +29 -0
 - data/spec/validator_spec.rb +79 -0
 - data/vendor/active_support/core_ext/array/extract_options.rb +29 -0
 - data/vendor/active_support/core_ext/class/attribute.rb +127 -0
 - data/vendor/active_support/core_ext/kernel/singleton_class.rb +13 -0
 - data/vendor/active_support/core_ext/module/remove_method.rb +11 -0
 - metadata +189 -0
 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            body {
         
     | 
| 
      
 2 
     | 
    
         
            +
              background: #fff;
         
     | 
| 
      
 3 
     | 
    
         
            +
              font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;;
         
     | 
| 
      
 4 
     | 
    
         
            +
              font-size: 15px;
         
     | 
| 
      
 5 
     | 
    
         
            +
              line-height: 1.7;
         
     | 
| 
      
 6 
     | 
    
         
            +
              margin: 0;
         
     | 
| 
      
 7 
     | 
    
         
            +
              padding: 30px;
         
     | 
| 
      
 8 
     | 
    
         
            +
            }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            a {
         
     | 
| 
      
 11 
     | 
    
         
            +
              color: #4183c4;
         
     | 
| 
      
 12 
     | 
    
         
            +
              text-decoration: none;
         
     | 
| 
      
 13 
     | 
    
         
            +
            }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            a:hover {
         
     | 
| 
      
 16 
     | 
    
         
            +
              text-decoration: underline;
         
     | 
| 
      
 17 
     | 
    
         
            +
            }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            code {
         
     | 
| 
      
 20 
     | 
    
         
            +
              background-color: #f8f8f8;
         
     | 
| 
      
 21 
     | 
    
         
            +
              border: 1px solid #ddd;
         
     | 
| 
      
 22 
     | 
    
         
            +
              border-radius: 3px;
         
     | 
| 
      
 23 
     | 
    
         
            +
              font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
         
     | 
| 
      
 24 
     | 
    
         
            +
              font-size: 12px;
         
     | 
| 
      
 25 
     | 
    
         
            +
              margin: 0 2px;
         
     | 
| 
      
 26 
     | 
    
         
            +
              padding: 0px 5px;
         
     | 
| 
      
 27 
     | 
    
         
            +
            }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            h1, h2, h3, h4 {
         
     | 
| 
      
 30 
     | 
    
         
            +
              font-weight: bold;
         
     | 
| 
      
 31 
     | 
    
         
            +
              margin: 0 0 15px;
         
     | 
| 
      
 32 
     | 
    
         
            +
              padding: 0;
         
     | 
| 
      
 33 
     | 
    
         
            +
            }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            h1 {
         
     | 
| 
      
 36 
     | 
    
         
            +
              border-bottom: 1px solid #ddd;
         
     | 
| 
      
 37 
     | 
    
         
            +
              font-size: 2.5em;
         
     | 
| 
      
 38 
     | 
    
         
            +
              font-weight: bold;
         
     | 
| 
      
 39 
     | 
    
         
            +
              margin: 0 0 15px;
         
     | 
| 
      
 40 
     | 
    
         
            +
              padding: 0;
         
     | 
| 
      
 41 
     | 
    
         
            +
            }
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            h2 {
         
     | 
| 
      
 44 
     | 
    
         
            +
              border-bottom: 1px solid #eee;
         
     | 
| 
      
 45 
     | 
    
         
            +
              font-size: 2em;
         
     | 
| 
      
 46 
     | 
    
         
            +
            }
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            h3 {
         
     | 
| 
      
 49 
     | 
    
         
            +
              font-size: 1.5em;
         
     | 
| 
      
 50 
     | 
    
         
            +
            }
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            h4 {
         
     | 
| 
      
 53 
     | 
    
         
            +
              font-size: 1.2em;
         
     | 
| 
      
 54 
     | 
    
         
            +
            }
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            p, ul {
         
     | 
| 
      
 57 
     | 
    
         
            +
              margin: 15px 0;
         
     | 
| 
      
 58 
     | 
    
         
            +
            }
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            ul {
         
     | 
| 
      
 61 
     | 
    
         
            +
              padding-left: 30px;
         
     | 
| 
      
 62 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            source 'https://rubygems.org'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            gem 'rake'
         
     | 
| 
      
 4 
     | 
    
         
            +
            gem 'opal',           :github => 'opal/opal', :ref => '85220f32136c74ac93f1cb721462324a3423cf44'
         
     | 
| 
      
 5 
     | 
    
         
            +
            gem 'opal-jquery',    :github => 'opal/opal-jquery'
         
     | 
| 
      
 6 
     | 
    
         
            +
            gem 'vienna',         :github => 'opal/vienna', :ref => '593335cbd7fb99ce471fa720e9b9c849d99b8dda'
         
     | 
| 
      
 7 
     | 
    
         
            +
            gem 'opal-haml',      :github => 'opal/opal-haml'
         
     | 
| 
      
 8 
     | 
    
         
            +
            gem 'opal-rspec',     '0.3.0.beta2'
         
     | 
| 
      
 9 
     | 
    
         
            +
            gem 'react.rb', :path => "../.."
         
     | 
| 
      
 10 
     | 
    
         
            +
            gem 'thin'
         
     | 
| 
      
 11 
     | 
    
         
            +
            gem 'react-source'
         
     | 
| 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            GIT
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: git://github.com/opal/opal-haml.git
         
     | 
| 
      
 3 
     | 
    
         
            +
              revision: 0bdd3eb53ec03d380e14440a94f779ed7c3741e1
         
     | 
| 
      
 4 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 5 
     | 
    
         
            +
                opal-haml (0.2.0)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  haml
         
     | 
| 
      
 7 
     | 
    
         
            +
                  opal (>= 0.5.0, < 1.0.0)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            GIT
         
     | 
| 
      
 10 
     | 
    
         
            +
              remote: git://github.com/opal/opal-jquery.git
         
     | 
| 
      
 11 
     | 
    
         
            +
              revision: 1814202085f168176231b877b2b7a967b75b0726
         
     | 
| 
      
 12 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 13 
     | 
    
         
            +
                opal-jquery (0.1.2)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  opal (>= 0.5.0, < 1.0.0)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            GIT
         
     | 
| 
      
 17 
     | 
    
         
            +
              remote: git://github.com/opal/opal.git
         
     | 
| 
      
 18 
     | 
    
         
            +
              revision: 85220f32136c74ac93f1cb721462324a3423cf44
         
     | 
| 
      
 19 
     | 
    
         
            +
              ref: 85220f32136c74ac93f1cb721462324a3423cf44
         
     | 
| 
      
 20 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 21 
     | 
    
         
            +
                opal (0.6.0)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  source_map
         
     | 
| 
      
 23 
     | 
    
         
            +
                  sprockets
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            GIT
         
     | 
| 
      
 26 
     | 
    
         
            +
              remote: git://github.com/opal/vienna.git
         
     | 
| 
      
 27 
     | 
    
         
            +
              revision: 593335cbd7fb99ce471fa720e9b9c849d99b8dda
         
     | 
| 
      
 28 
     | 
    
         
            +
              ref: 593335cbd7fb99ce471fa720e9b9c849d99b8dda
         
     | 
| 
      
 29 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 30 
     | 
    
         
            +
                vienna (0.0.2)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  opal (>= 0.5.0, < 1.0.0)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  opal-activesupport
         
     | 
| 
      
 33 
     | 
    
         
            +
                  opal-jquery
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 36 
     | 
    
         
            +
              remote: ../..
         
     | 
| 
      
 37 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 38 
     | 
    
         
            +
                react.rb (0.0.1)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  opal (~> 0.6.0)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  opal-activesupport
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 43 
     | 
    
         
            +
              remote: https://rubygems.org/
         
     | 
| 
      
 44 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 45 
     | 
    
         
            +
                daemons (1.1.9)
         
     | 
| 
      
 46 
     | 
    
         
            +
                eventmachine (1.0.3)
         
     | 
| 
      
 47 
     | 
    
         
            +
                haml (4.0.5)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  tilt
         
     | 
| 
      
 49 
     | 
    
         
            +
                hike (1.2.3)
         
     | 
| 
      
 50 
     | 
    
         
            +
                json (1.8.2)
         
     | 
| 
      
 51 
     | 
    
         
            +
                multi_json (1.10.1)
         
     | 
| 
      
 52 
     | 
    
         
            +
                opal-activesupport (0.1.0)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  opal (>= 0.5.0, < 1.0.0)
         
     | 
| 
      
 54 
     | 
    
         
            +
                opal-rspec (0.3.0.beta2)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  opal (>= 0.6.0, < 1.0.0)
         
     | 
| 
      
 56 
     | 
    
         
            +
                rack (1.5.2)
         
     | 
| 
      
 57 
     | 
    
         
            +
                rake (10.1.1)
         
     | 
| 
      
 58 
     | 
    
         
            +
                react-source (0.12.2)
         
     | 
| 
      
 59 
     | 
    
         
            +
                source_map (3.0.1)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  json
         
     | 
| 
      
 61 
     | 
    
         
            +
                sprockets (2.12.3)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  hike (~> 1.2)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  multi_json (~> 1.0)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  rack (~> 1.0)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  tilt (~> 1.1, != 1.3.0)
         
     | 
| 
      
 66 
     | 
    
         
            +
                thin (1.6.2)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  daemons (>= 1.0.9)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  eventmachine (>= 1.0.0)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  rack (>= 1.0.0)
         
     | 
| 
      
 70 
     | 
    
         
            +
                tilt (1.4.1)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 73 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 76 
     | 
    
         
            +
              opal!
         
     | 
| 
      
 77 
     | 
    
         
            +
              opal-haml!
         
     | 
| 
      
 78 
     | 
    
         
            +
              opal-jquery!
         
     | 
| 
      
 79 
     | 
    
         
            +
              opal-rspec (= 0.3.0.beta2)
         
     | 
| 
      
 80 
     | 
    
         
            +
              rake
         
     | 
| 
      
 81 
     | 
    
         
            +
              react-source
         
     | 
| 
      
 82 
     | 
    
         
            +
              react.rb!
         
     | 
| 
      
 83 
     | 
    
         
            +
              thin
         
     | 
| 
      
 84 
     | 
    
         
            +
              vienna!
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # react.rb-todos
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Modify from original version of [Opal-Todos](https://github.com/opal/opal-todos)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ## Running
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Get dependencies:
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ```
         
     | 
| 
      
 10 
     | 
    
         
            +
            $ bundle install
         
     | 
| 
      
 11 
     | 
    
         
            +
            ```
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            Run the sprockets based server for auto-compiling:
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ```
         
     | 
| 
      
 16 
     | 
    
         
            +
            $ bundle exec rackup
         
     | 
| 
      
 17 
     | 
    
         
            +
            ```
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            Open `http://localhost:9292` in the browser.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            ## Code Overview
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            Opal comes with sprockets support built in, so using rack we can have an
         
     | 
| 
      
 24 
     | 
    
         
            +
            easy to boot build system to handle all opal dependencies. If you look
         
     | 
| 
      
 25 
     | 
    
         
            +
            in `index.html.erb`, you will see a call to `javascript_include_tag`
         
     | 
| 
      
 26 
     | 
    
         
            +
            which acts just like the rails tag helper. This will include our
         
     | 
| 
      
 27 
     | 
    
         
            +
            `application.rb` file, and all of its dependencies. Each file will be included
         
     | 
| 
      
 28 
     | 
    
         
            +
            in a seperate `<script>..</script>` tag to make navigating the code in a
         
     | 
| 
      
 29 
     | 
    
         
            +
            web browser easier.
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            ### Router
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            `TodoAppView` use router provided by [Vienna](https://github.com/opal/vienna)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            ### Model
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            Use model layer provided by [Vienna](https://github.com/opal/vienna) which provide basic support of local storage and update notification.
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'opal'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'jquery'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'opal-jquery'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'opal-haml'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'vienna'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "react"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require 'models/todo'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            require "components/app.react"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            Document.ready? do
         
     | 
| 
      
 13 
     | 
    
         
            +
              element = React.create_element(TodoAppView, filter: "all")
         
     | 
| 
      
 14 
     | 
    
         
            +
              component = React.render(element, Element.find('#todoapp').get(0))
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              Vienna::Router.new.tap do |router|
         
     | 
| 
      
 17 
     | 
    
         
            +
                router.route('/:filter') do |params|
         
     | 
| 
      
 18 
     | 
    
         
            +
                  component.set_props(filter: params[:filter].empty? ? "all" : params[:filter])
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end.update
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "components/footer.react"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "components/todo_item.react"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "components/todo_list.react"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class TodoAppView
         
     | 
| 
      
 6 
     | 
    
         
            +
              include React::Component
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              KEY_ENTER = 13
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              params do
         
     | 
| 
      
 11 
     | 
    
         
            +
                requires :filter, values: ["all", "active", "completed"]
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              define_state(:todos) { [] }
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              before_mount do
         
     | 
| 
      
 17 
     | 
    
         
            +
                Todo.on(:create)  { Todo.adapter.sync_models(Todo); reload_current_filter }
         
     | 
| 
      
 18 
     | 
    
         
            +
                Todo.on(:update)  { Todo.adapter.sync_models(Todo); reload_current_filter }
         
     | 
| 
      
 19 
     | 
    
         
            +
                Todo.on(:destroy) { Todo.adapter.sync_models(Todo); reload_current_filter }
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              before_receive_props do |next_props|
         
     | 
| 
      
 23 
     | 
    
         
            +
                apply_filter next_props[:filter]
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def reload_current_filter
         
     | 
| 
      
 27 
     | 
    
         
            +
                apply_filter(params[:filter])
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def apply_filter(filter)
         
     | 
| 
      
 31 
     | 
    
         
            +
                Todo.adapter.find_all(Todo) do |models|
         
     | 
| 
      
 32 
     | 
    
         
            +
                  case filter
         
     | 
| 
      
 33 
     | 
    
         
            +
                  when "all"
         
     | 
| 
      
 34 
     | 
    
         
            +
                    self.todos = models
         
     | 
| 
      
 35 
     | 
    
         
            +
                  when "active"
         
     | 
| 
      
 36 
     | 
    
         
            +
                    self.todos = models.reject(&:completed)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  when "completed"
         
     | 
| 
      
 38 
     | 
    
         
            +
                    self.todos = models.select(&:completed)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def handle_keydown(event)
         
     | 
| 
      
 44 
     | 
    
         
            +
                if event.key_code == KEY_ENTER
         
     | 
| 
      
 45 
     | 
    
         
            +
                  value = event.target.value.strip
         
     | 
| 
      
 46 
     | 
    
         
            +
                  Todo.create title: value, completed: false
         
     | 
| 
      
 47 
     | 
    
         
            +
                  event.target.value = ""
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              def render
         
     | 
| 
      
 52 
     | 
    
         
            +
                div do
         
     | 
| 
      
 53 
     | 
    
         
            +
                  header(id: "header") do
         
     | 
| 
      
 54 
     | 
    
         
            +
                    h1 { "Todos" }
         
     | 
| 
      
 55 
     | 
    
         
            +
                    input(id: "new-todo", placeholder: "What needs to be done?").on(:key_down) { |e| handle_keydown(e) }
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  present TodoList, todos: self.todos
         
     | 
| 
      
 58 
     | 
    
         
            +
                  present Footer, selected_filter: params[:filter]
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Footer
         
     | 
| 
      
 2 
     | 
    
         
            +
              include React::Component
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def clear_completed
         
     | 
| 
      
 5 
     | 
    
         
            +
                Todo.completed.each { |t| t.destroy }
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def render
         
     | 
| 
      
 9 
     | 
    
         
            +
                footer(id: "footer") do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  span(id: "todo-count") do
         
     | 
| 
      
 11 
     | 
    
         
            +
                    strong { Todo.active.size }
         
     | 
| 
      
 12 
     | 
    
         
            +
                    span { Todo.active.size == 1 ? ' item left' : ' items left' }
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  ul(id: "filters") do
         
     | 
| 
      
 16 
     | 
    
         
            +
                    filters = [{href: "#/", filter: "all"},
         
     | 
| 
      
 17 
     | 
    
         
            +
                               {href: "#/active", filter: "active"},
         
     | 
| 
      
 18 
     | 
    
         
            +
                               {href: "#/completed", filter: "completed"}]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    filters.map do |item|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      li { a(href: item[:href], class_name: {selected: params[:selected_filter] == item[:filter]}) { item[:filter].capitalize } }
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  completed = Todo.completed.size
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  if completed > 0
         
     | 
| 
      
 27 
     | 
    
         
            +
                    button(id: "clear-completed") { "Clear completed (#{completed})" }.on(:click) { clear_completed }
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class TodoItem
         
     | 
| 
      
 2 
     | 
    
         
            +
              include React::Component
         
     | 
| 
      
 3 
     | 
    
         
            +
              KEY_ENTER = 13
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              define_state(:editing) { false }
         
     | 
| 
      
 6 
     | 
    
         
            +
              define_state(:edit_text)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              before_mount :set_up
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def set_up
         
     | 
| 
      
 11 
     | 
    
         
            +
                self.edit_text = params[:todo].title
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def finish_editing
         
     | 
| 
      
 15 
     | 
    
         
            +
                self.editing = false
         
     | 
| 
      
 16 
     | 
    
         
            +
                new_value = self.edit_text.strip
         
     | 
| 
      
 17 
     | 
    
         
            +
                if new_value.empty?
         
     | 
| 
      
 18 
     | 
    
         
            +
                  params[:todo].destroy
         
     | 
| 
      
 19 
     | 
    
         
            +
                else
         
     | 
| 
      
 20 
     | 
    
         
            +
                  params[:todo].update(title: new_value)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def render
         
     | 
| 
      
 25 
     | 
    
         
            +
                li(class_name: {editing: self.editing}) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  div(class_name: 'view') do
         
     | 
| 
      
 27 
     | 
    
         
            +
                    input(class_name: "toggle", type: "checkbox", checked: params[:todo].completed).on(:click) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                      todo = params[:todo]
         
     | 
| 
      
 29 
     | 
    
         
            +
                      todo.update(:completed => !todo.completed)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    label { self.edit_text }.on(:double_click) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                      # set on state will trigger re-render, so we manipulate the DOM after render done
         
     | 
| 
      
 33 
     | 
    
         
            +
                      self.set_state(editing: true) do
         
     | 
| 
      
 34 
     | 
    
         
            +
                        self.refs[:input].dom_node.focus
         
     | 
| 
      
 35 
     | 
    
         
            +
                      end
         
     | 
| 
      
 36 
     | 
    
         
            +
                      self.edit_text = params[:todo].title
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    button(class_name: "destroy").on(:click) { params[:todo].destroy }
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  input(class_name: "edit", value: self.edit_text, ref: :input)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  .on(:blur) { finish_editing }
         
     | 
| 
      
 42 
     | 
    
         
            +
                  .on(:change) {|e| self.edit_text = e.target.value }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  .on(:key_down) { |e| finish_editing if (e.key_code == KEY_ENTER) }
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class TodoList
         
     | 
| 
      
 2 
     | 
    
         
            +
              include React::Component
         
     | 
| 
      
 3 
     | 
    
         
            +
              
         
     | 
| 
      
 4 
     | 
    
         
            +
              def toggle_all
         
     | 
| 
      
 5 
     | 
    
         
            +
                distinct_status = Todo.all.map {|t| t.completed }.uniq
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                if distinct_status.count == 1
         
     | 
| 
      
 8 
     | 
    
         
            +
                  Todo.all.each {|t| t.update(:completed => !distinct_status[0]) }
         
     | 
| 
      
 9 
     | 
    
         
            +
                else # toggle all as completed
         
     | 
| 
      
 10 
     | 
    
         
            +
                  Todo.all.each {|t| t.update(:completed => true) }
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
              
         
     | 
| 
      
 14 
     | 
    
         
            +
              def render
         
     | 
| 
      
 15 
     | 
    
         
            +
                section(id: "main") do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  input(id: "toggle-all", type: "checkbox").on(:click) { toggle_all }
         
     | 
| 
      
 17 
     | 
    
         
            +
                  label(htmlFor: "toggle-all") { "Mark all as complete" }
         
     | 
| 
      
 18 
     | 
    
         
            +
                  ul(id: "todo-list") do
         
     | 
| 
      
 19 
     | 
    
         
            +
                    params[:todos].map do |todo|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      present TodoItem, todo: todo , key: todo.id
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'vienna/adapters/local'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Todo < Vienna::Model
         
     | 
| 
      
 4 
     | 
    
         
            +
              adapter Vienna::LocalAdapter
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              attributes :title, :completed
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              alias completed? completed
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              # All active (not completed) todos
         
     | 
| 
      
 11 
     | 
    
         
            +
              def self.active
         
     | 
| 
      
 12 
     | 
    
         
            +
                all.reject(&:completed)
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              # All completed todos
         
     | 
| 
      
 16 
     | 
    
         
            +
              def self.completed
         
     | 
| 
      
 17 
     | 
    
         
            +
                all.select(&:completed)
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'bundler'
         
     | 
| 
      
 2 
     | 
    
         
            +
            Bundler.require
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require "react/source"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            run Opal::Server.new { |s|
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.append_path 'app'
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.append_path 'vendor'
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.append_path File.dirname(::React::Source.bundled_path_for("react-with-addons.js"))
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              s.debug = true
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.main = 'application'
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.index_path = 'index.html.haml'
         
     | 
| 
      
 14 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            !!!
         
     | 
| 
      
 2 
     | 
    
         
            +
            %html(lang="en")
         
     | 
| 
      
 3 
     | 
    
         
            +
              %head
         
     | 
| 
      
 4 
     | 
    
         
            +
                %meta(charset="utf-8")
         
     | 
| 
      
 5 
     | 
    
         
            +
                %meta(http-equiv="X-UA-Compatible" content="IE=edge,chrome=1")
         
     | 
| 
      
 6 
     | 
    
         
            +
                %link(rel="stylesheet" href="/vendor/base.css")
         
     | 
| 
      
 7 
     | 
    
         
            +
                = javascript_include_tag 'react-with-addons.min.js'
         
     | 
| 
      
 8 
     | 
    
         
            +
                = javascript_include_tag 'application'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              %body
         
     | 
| 
      
 11 
     | 
    
         
            +
                %section#todoapp
         
     | 
| 
      
 12 
     | 
    
         
            +
                #info
         
     | 
| 
      
 13 
     | 
    
         
            +
                  %p Double-click to edit a todo
         
     | 
| 
      
 14 
     | 
    
         
            +
                  %p
         
     | 
| 
      
 15 
     | 
    
         
            +
                    Part of
         
     | 
| 
      
 16 
     | 
    
         
            +
                    %a(href="http://todomvc.com") TodoMVC
         
     |