reactive-ruby 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +53 -0
  5. data/LICENSE +19 -0
  6. data/README.md +303 -0
  7. data/config.ru +15 -0
  8. data/example/examples/Gemfile +7 -0
  9. data/example/examples/Gemfile.lock +45 -0
  10. data/example/examples/config.ru +44 -0
  11. data/example/examples/hello.js.rb +43 -0
  12. data/example/react-tutorial/Gemfile +7 -0
  13. data/example/react-tutorial/Gemfile.lock +49 -0
  14. data/example/react-tutorial/README.md +8 -0
  15. data/example/react-tutorial/_comments.json +14 -0
  16. data/example/react-tutorial/config.ru +63 -0
  17. data/example/react-tutorial/example.js.rb +290 -0
  18. data/example/react-tutorial/public/base.css +62 -0
  19. data/example/todos/Gemfile +11 -0
  20. data/example/todos/Gemfile.lock +84 -0
  21. data/example/todos/README.md +37 -0
  22. data/example/todos/Rakefile +8 -0
  23. data/example/todos/app/application.rb +22 -0
  24. data/example/todos/app/components/app.react.rb +61 -0
  25. data/example/todos/app/components/footer.react.rb +31 -0
  26. data/example/todos/app/components/todo_item.react.rb +46 -0
  27. data/example/todos/app/components/todo_list.react.rb +25 -0
  28. data/example/todos/app/models/todo.rb +19 -0
  29. data/example/todos/config.ru +14 -0
  30. data/example/todos/index.html.haml +16 -0
  31. data/example/todos/spec/todo_spec.rb +28 -0
  32. data/example/todos/vendor/base.css +410 -0
  33. data/example/todos/vendor/bg.png +0 -0
  34. data/example/todos/vendor/jquery.js +4 -0
  35. data/lib/rails-helpers/react_component.rb +32 -0
  36. data/lib/reactive-ruby.rb +23 -0
  37. data/lib/reactive-ruby/api.rb +177 -0
  38. data/lib/reactive-ruby/callbacks.rb +35 -0
  39. data/lib/reactive-ruby/component.rb +411 -0
  40. data/lib/reactive-ruby/element.rb +87 -0
  41. data/lib/reactive-ruby/event.rb +76 -0
  42. data/lib/reactive-ruby/ext/hash.rb +9 -0
  43. data/lib/reactive-ruby/ext/string.rb +8 -0
  44. data/lib/reactive-ruby/isomorphic_helpers.rb +223 -0
  45. data/lib/reactive-ruby/observable.rb +33 -0
  46. data/lib/reactive-ruby/rendering_context.rb +91 -0
  47. data/lib/reactive-ruby/serializers.rb +15 -0
  48. data/lib/reactive-ruby/state.rb +90 -0
  49. data/lib/reactive-ruby/top_level.rb +53 -0
  50. data/lib/reactive-ruby/validator.rb +83 -0
  51. data/lib/reactive-ruby/version.rb +3 -0
  52. data/logo1.png +0 -0
  53. data/logo2.png +0 -0
  54. data/logo3.png +0 -0
  55. data/reactive-ruby.gemspec +25 -0
  56. data/spec/callbacks_spec.rb +107 -0
  57. data/spec/component_spec.rb +597 -0
  58. data/spec/element_spec.rb +60 -0
  59. data/spec/event_spec.rb +22 -0
  60. data/spec/react_spec.rb +209 -0
  61. data/spec/reactjs/index.html.erb +11 -0
  62. data/spec/spec_helper.rb +29 -0
  63. data/spec/tutorial/tutorial_spec.rb +37 -0
  64. data/spec/validator_spec.rb +79 -0
  65. data/vendor/active_support/core_ext/array/extract_options.rb +29 -0
  66. data/vendor/active_support/core_ext/class/attribute.rb +127 -0
  67. data/vendor/active_support/core_ext/kernel/singleton_class.rb +13 -0
  68. data/vendor/active_support/core_ext/module/remove_method.rb +11 -0
  69. metadata +205 -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,8 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ require 'opal/rspec/rake_task'
5
+ Opal::RSpec::RakeTask.new(:default) do |s|
6
+ s.append_path 'app'
7
+ s.append_path 'vendor'
8
+ end
@@ -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