styx 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +158 -0
- data/app/assets/javascripts/styx.forms.js.coffee +27 -0
- data/app/assets/javascripts/styx.js.coffee +22 -0
- data/app/views/styx/_initializer.html.haml +9 -0
- data/init.rb +1 -0
- data/lib/styx.rb +8 -0
- data/lib/styx/engine.rb +4 -0
- data/lib/styx/forms.rb +33 -0
- data/lib/styx/helpers.rb +32 -0
- data/lib/styx/initializer.rb +27 -0
- data/styx.gemspec +14 -0
- metadata +57 -0
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
Styx
|
2
|
+
========
|
3
|
+
|
4
|
+
Bridge between Server (Rails) side and Client (JS) side divided into several modules:
|
5
|
+
|
6
|
+
* **Helpers**: set of helpers to support all other modules.
|
7
|
+
* **Initializer**: organizes JS into bootstrap classes and allows you to pass data from controller/view.
|
8
|
+
* **Forms**: remote validaton engine.
|
9
|
+
|
10
|
+
|
11
|
+
Installation
|
12
|
+
------------
|
13
|
+
|
14
|
+
$ gem install styx
|
15
|
+
|
16
|
+
|
17
|
+
Basic Usage
|
18
|
+
------------
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# app/controllers/foos_controller.rb
|
22
|
+
class FoosController < ApplicationController
|
23
|
+
include Styx::Initializer
|
24
|
+
include Styx::Forms
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
Include modules to ApplicationController if you want to use it everywhere.
|
29
|
+
|
30
|
+
|
31
|
+
Initializer
|
32
|
+
------------
|
33
|
+
|
34
|
+
In common each controller in Rails comes with *app/assets/javascripts/controller_name.js.coffee*.
|
35
|
+
Styx.Initializer allows you to separately define bootstrap logic for each Rails action and pass
|
36
|
+
some data from server right in.
|
37
|
+
|
38
|
+
To enable initializers bootstrap, add *styx_initialize* line to your layout:
|
39
|
+
|
40
|
+
```erb
|
41
|
+
<head>
|
42
|
+
<title>Rails Application</title>
|
43
|
+
<%= stylesheet_link_tag "application" %>
|
44
|
+
<%= javascript_include_tag "application" %>
|
45
|
+
<%= styx_initialize %>
|
46
|
+
<%= csrf_meta_tags %>
|
47
|
+
```
|
48
|
+
|
49
|
+
Imagine you have controller *Foos* and therefore *app/assets/javascripts/foos.js.coffee* file.
|
50
|
+
|
51
|
+
```coffee-script
|
52
|
+
@Styx.Initializers.Foos =
|
53
|
+
initialize: ->
|
54
|
+
console.log 'This will be called in foos#ANY action after <head> was parsed'
|
55
|
+
|
56
|
+
index: (data) ->
|
57
|
+
console.log 'This will be called in foos#index action after <head> was parsed'
|
58
|
+
|
59
|
+
show: (data) ->
|
60
|
+
$ ->
|
61
|
+
console.log 'This will be called in foos#show action after the page was loaded'
|
62
|
+
```
|
63
|
+
|
64
|
+
Note that any method besides common *initialize* has the *data* parameter. To pass some data to your
|
65
|
+
initializers you can use *styx_initialize_with* helper in your controller or views. Like that:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# app/controllers/foos_controller.rb
|
69
|
+
class FoosController < ApplicationController
|
70
|
+
def index
|
71
|
+
styx_initialize_with :some => 'data', :and => {:even => 'more data'}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# app/views/foos/index.html.erb
|
76
|
+
<%- styx_initialize_with :enabled => true %>
|
77
|
+
```
|
78
|
+
|
79
|
+
As the result *Styx.Initializers.Foos->index* will be called with data equal to
|
80
|
+
|
81
|
+
{some: data, and: {even: 'mode data'}, enabled: true}
|
82
|
+
|
83
|
+
|
84
|
+
Forms
|
85
|
+
------------
|
86
|
+
|
87
|
+
Forms assist *form_for @foo, :remote => true* to automate server response and UJS callbacks.
|
88
|
+
|
89
|
+
If everything went smooth you can respond with URL for redirect or with JSON for your callback.
|
90
|
+
If validation failed appropriate form fields will be wraped with native
|
91
|
+
|
92
|
+
<div class="field_with_errors">
|
93
|
+
|
94
|
+
### Client side
|
95
|
+
|
96
|
+
```erb
|
97
|
+
# app/views/foos/new.html.erb
|
98
|
+
<%= form_for @foo, :remote => true, :html => {:id => 'foo_form'} do |f| %>
|
99
|
+
<%= f.text_field :f.title %>
|
100
|
+
<% end %>
|
101
|
+
```
|
102
|
+
|
103
|
+
```javascript
|
104
|
+
// Light case
|
105
|
+
Forms.attach('#foo_form')
|
106
|
+
|
107
|
+
|
108
|
+
// Heavy case
|
109
|
+
success_callback = function(data) {} // Form validated and stored. Data is what you pass from server.
|
110
|
+
error_callback = function() {} // Form wasn't validated
|
111
|
+
|
112
|
+
Forms.attach('#foo_form', success_callback, error_callback)
|
113
|
+
```
|
114
|
+
|
115
|
+
Note that if success_callback was not given but server responded with some data, Styx.Forms will try
|
116
|
+
to evaluate it as URL and will try to redirect to it.
|
117
|
+
|
118
|
+
Javascript part goes best with Styx.Initializers.
|
119
|
+
|
120
|
+
### Server side
|
121
|
+
|
122
|
+
In common you just want to store your form as model. Styx.Forms come with predefined helper for this:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
# app/controllers/foos_controller.rb
|
126
|
+
def create
|
127
|
+
@entity = Foo.new(params[:foo])
|
128
|
+
response = foos_url # Return URL or anything for your custom callback
|
129
|
+
|
130
|
+
styx_form_store_and_respond(@foo, response) do
|
131
|
+
# this will be called after (and if) @foo was validated and saved
|
132
|
+
end
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
*response* parameter can either be passed as lambda{|x|}.
|
137
|
+
|
138
|
+
### What if form was invalid?
|
139
|
+
|
140
|
+
First of all, *success_callback* and server-side block won't be called. *response* won't be return to JS.
|
141
|
+
Validation errors for @entity will be returned instead. All invalid form fields will be wraped with
|
142
|
+
|
143
|
+
<div class="field_with_errors">
|
144
|
+
|
145
|
+
### Server side without models
|
146
|
+
|
147
|
+
To work without models (i.e. to serve login form) you can use two helpers
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
styx_form_respond_success(data)
|
151
|
+
```
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
styx_form_respond_failure(entity_name, fields) # fields = {'field_name' => 'error message'}
|
155
|
+
```
|
156
|
+
|
157
|
+
To choose which fields have to be marked as invalid JS part will concatenate entity_name + field _name. So if
|
158
|
+
you don't have entity, simply pass empty string as the first arg.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
@Styx.Forms =
|
2
|
+
validate: (element, success_action=false, error_action=false, substitutions={}) ->
|
3
|
+
$(element).live 'ajax:success', (event, result, status, xhr) ->
|
4
|
+
$(element).find('.field_with_errors').children().unwrap()
|
5
|
+
|
6
|
+
unless success_action
|
7
|
+
Styx.URL.go xhr.responseText, true
|
8
|
+
else
|
9
|
+
success_action.call(this, xhr.responseText)
|
10
|
+
|
11
|
+
$(element).live 'ajax:error', (evt, xhr, status, error) ->
|
12
|
+
$(element).find('.field_with_errors').children().unwrap()
|
13
|
+
error_action.call(this) if error_action
|
14
|
+
|
15
|
+
errors = jQuery.parseJSON xhr.responseText
|
16
|
+
|
17
|
+
console.log errors if console?
|
18
|
+
|
19
|
+
for field, notifications of errors.messages
|
20
|
+
field = substitutions[field] if substitutions[field]?
|
21
|
+
input = $(element).find("##{errors.entity}_#{field}")
|
22
|
+
|
23
|
+
# Support for Chosen jQuery plugin
|
24
|
+
if input.is('.chzn-done')
|
25
|
+
input = $(element).find("##{errors.entity}_#{field}_chzn")
|
26
|
+
|
27
|
+
input.wrap("<div class='field_with_errors' />")
|
@@ -0,0 +1,22 @@
|
|
1
|
+
@Styx = {
|
2
|
+
Initializers: {}
|
3
|
+
}
|
4
|
+
|
5
|
+
@Styx.URL =
|
6
|
+
go: (url, force=false) ->
|
7
|
+
# 'Force' required if you want to reload same page with another anchor
|
8
|
+
url = this.build(url, "reloadthispagepls=#{Math.random()}") if force
|
9
|
+
window.location.href = url
|
10
|
+
|
11
|
+
build: (url, params) ->
|
12
|
+
hash = url.match(/\#.*$/)
|
13
|
+
hash = if hash then hash[0] else false
|
14
|
+
|
15
|
+
url = url.replace(hash, '') if hash
|
16
|
+
url = url + "?" if url.indexOf("?") == -1
|
17
|
+
|
18
|
+
url = "#{url}&#{params}"
|
19
|
+
|
20
|
+
url = url + hash if hash
|
21
|
+
|
22
|
+
return url
|
@@ -0,0 +1,9 @@
|
|
1
|
+
:javascript
|
2
|
+
if (Styx.Initializers.#{klass} && Styx.Initializers.#{klass}.initialize)
|
3
|
+
{
|
4
|
+
Styx.Initializers.#{klass}.initialize(#{data.to_json})
|
5
|
+
}
|
6
|
+
if (Styx.Initializers.#{klass} && Styx.Initializers.#{klass}.#{method})
|
7
|
+
{
|
8
|
+
Styx.Initializers.#{klass}.#{method}(#{data.to_json})
|
9
|
+
}
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'styx'
|
data/lib/styx.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'styx/engine'
|
2
|
+
require 'styx/helpers'
|
3
|
+
require 'styx/forms'
|
4
|
+
require 'styx/initializer'
|
5
|
+
|
6
|
+
ActionController::Base.send :include, Styx::Helpers
|
7
|
+
# ActionController::Base.send :include, Styx::Forms
|
8
|
+
# ActionController::Base.send :include, Styx::Initializer
|
data/lib/styx/engine.rb
ADDED
data/lib/styx/forms.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Styx
|
2
|
+
module Forms
|
3
|
+
def self.included base
|
4
|
+
base.send(:include, InstanceMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module InstanceMethods
|
8
|
+
def styx_form_store_and_respond(entity, data=nil, &block)
|
9
|
+
response.content_type = Mime::TEXT
|
10
|
+
|
11
|
+
if entity.save
|
12
|
+
styx_form_respond_success(data, entity, &block)
|
13
|
+
else
|
14
|
+
styx_form_respond_failure(entity.class.name, entity.errors.messages)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def styx_form_respond_failure(entity, errors)
|
19
|
+
response.content_type = Mime::TEXT
|
20
|
+
|
21
|
+
errors = Hash[*errors.map {|x| [x, nil]}.flatten] if errors.is_a?(Array)
|
22
|
+
render :text => {:entity => entity.to_s.underscore.gsub('/', '_'), :messages => errors}.to_json, :status => :unprocessable_entity
|
23
|
+
end
|
24
|
+
|
25
|
+
def styx_form_respond_success(data, entity=nil, &block)
|
26
|
+
response.content_type = Mime::TEXT
|
27
|
+
|
28
|
+
block.call(entity) if block_given?
|
29
|
+
render :text => (data.is_a?(Proc) ? data.call(entity) : data)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/styx/helpers.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Styx
|
2
|
+
module Helpers
|
3
|
+
def self.included base
|
4
|
+
base.send(:include, InstanceMethods)
|
5
|
+
base.class_eval do
|
6
|
+
helper_method :this_page?, :this_namespace?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def this_page?(mask)
|
12
|
+
mask = mask.split('#')
|
13
|
+
|
14
|
+
c = mask[0]
|
15
|
+
a = mask[1]
|
16
|
+
|
17
|
+
flag = true
|
18
|
+
|
19
|
+
flag = false if !c.blank? && c != controller_path
|
20
|
+
flag = false if !a.blank? && a != action_name
|
21
|
+
|
22
|
+
flag
|
23
|
+
end
|
24
|
+
|
25
|
+
def this_namespace?(namespace)
|
26
|
+
current = controller_path.split('/').first
|
27
|
+
|
28
|
+
return namespace == current
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Styx
|
2
|
+
module Initializer
|
3
|
+
def self.included base
|
4
|
+
base.send(:include, InstanceMethods)
|
5
|
+
base.class_eval do
|
6
|
+
helper_method :styx_initialize, :styx_initialize_with
|
7
|
+
before_filter { @styx_initialize_with = {} }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def styx_initialize_with(data)
|
13
|
+
@styx_initialize_with.merge! data
|
14
|
+
end
|
15
|
+
|
16
|
+
def styx_initialize()
|
17
|
+
result = render_to_string :partial => 'styx/initializer', :locals => {
|
18
|
+
:klass => controller_path.gsub('/', '_').camelize,
|
19
|
+
:method => action_name,
|
20
|
+
:data => @styx_initialize_with
|
21
|
+
}
|
22
|
+
|
23
|
+
result.html_safe
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/styx.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
SPEC = Gem::Specification.new do |s|
|
2
|
+
s.name = "styx"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.summary = "Set of helpers to maintain bridge between Server (Rails) side and Client (JS) side"
|
6
|
+
s.email = "boris@roundlake.ru"
|
7
|
+
s.homepage = "http://github.com/roundlake/styx"
|
8
|
+
s.description = s.summary
|
9
|
+
s.authors = ['Boris Staal']
|
10
|
+
|
11
|
+
s.has_rdoc = false # disable rdoc generation until we've got more
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.require_paths = ["lib"]
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: styx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Boris Staal
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-22 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Set of helpers to maintain bridge between Server (Rails) side and Client
|
15
|
+
(JS) side
|
16
|
+
email: boris@roundlake.ru
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- README.md
|
22
|
+
- app/assets/javascripts/styx.forms.js.coffee
|
23
|
+
- app/assets/javascripts/styx.js.coffee
|
24
|
+
- app/views/styx/_initializer.html.haml
|
25
|
+
- init.rb
|
26
|
+
- lib/styx.rb
|
27
|
+
- lib/styx/engine.rb
|
28
|
+
- lib/styx/forms.rb
|
29
|
+
- lib/styx/helpers.rb
|
30
|
+
- lib/styx/initializer.rb
|
31
|
+
- styx.gemspec
|
32
|
+
homepage: http://github.com/roundlake/styx
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.8.10
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Set of helpers to maintain bridge between Server (Rails) side and Client
|
56
|
+
(JS) side
|
57
|
+
test_files: []
|